Roo/bootstrap/Input.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets[0], function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })();/*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass('hidden');
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass('hidden');
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * Body
442  *
443  */
444
445 /**
446  * @class Roo.bootstrap.Body
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Body class
449  *
450  * @constructor
451  * Create a new body
452  * @param {Object} config The config object
453  */
454
455 Roo.bootstrap.Body = function(config){
456
457     config = config || {};
458
459     Roo.bootstrap.Body.superclass.constructor.call(this, config);
460     this.el = Roo.get(config.el ? config.el : document.body );
461     if (this.cls && this.cls.length) {
462         Roo.get(document.body).addClass(this.cls);
463     }
464 };
465
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
467
468     is_body : true,// just to make sure it's constructed?
469
470         autoCreate : {
471         cls: 'container'
472     },
473     onRender : function(ct, position)
474     {
475        /* Roo.log("Roo.bootstrap.Body - onRender");
476         if (this.cls && this.cls.length) {
477             Roo.get(document.body).addClass(this.cls);
478         }
479         // style??? xttr???
480         */
481     }
482
483
484
485
486 });
487 /*
488  * - LGPL
489  *
490  * button group
491  * 
492  */
493
494
495 /**
496  * @class Roo.bootstrap.ButtonGroup
497  * @extends Roo.bootstrap.Component
498  * Bootstrap ButtonGroup class
499  * @cfg {String} size lg | sm | xs (default empty normal)
500  * @cfg {String} align vertical | justified  (default none)
501  * @cfg {String} direction up | down (default down)
502  * @cfg {Boolean} toolbar false | true
503  * @cfg {Boolean} btn true | false
504  * 
505  * 
506  * @constructor
507  * Create a new Input
508  * @param {Object} config The config object
509  */
510
511 Roo.bootstrap.ButtonGroup = function(config){
512     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
513 };
514
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
516     
517     size: '',
518     align: '',
519     direction: '',
520     toolbar: false,
521     btn: true,
522
523     getAutoCreate : function(){
524         var cfg = {
525             cls: 'btn-group',
526             html : null
527         };
528         
529         cfg.html = this.html || cfg.html;
530         
531         if (this.toolbar) {
532             cfg = {
533                 cls: 'btn-toolbar',
534                 html: null
535             };
536             
537             return cfg;
538         }
539         
540         if (['vertical','justified'].indexOf(this.align)!==-1) {
541             cfg.cls = 'btn-group-' + this.align;
542             
543             if (this.align == 'justified') {
544                 console.log(this.items);
545             }
546         }
547         
548         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549             cfg.cls += ' btn-group-' + this.size;
550         }
551         
552         if (this.direction == 'up') {
553             cfg.cls += ' dropup' ;
554         }
555         
556         return cfg;
557     },
558     /**
559      * Add a button to the group (similar to NavItem API.)
560      */
561     addItem : function(cfg)
562     {
563         var cn = new Roo.bootstrap.Button(cfg);
564         //this.register(cn);
565         cn.parentId = this.id;
566         cn.onRender(this.el, null);
567         return cn;
568     }
569    
570 });
571
572  /*
573  * - LGPL
574  *
575  * button
576  * 
577  */
578
579 /**
580  * @class Roo.bootstrap.Button
581  * @extends Roo.bootstrap.Component
582  * Bootstrap Button class
583  * @cfg {String} html The button content
584  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587  * @cfg {String} size ( lg | sm | xs)
588  * @cfg {String} tag ( a | input | submit)
589  * @cfg {String} href empty or href
590  * @cfg {Boolean} disabled default false;
591  * @cfg {Boolean} isClose default false;
592  * @cfg {String} glyphicon depricated - use fa
593  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
594  * @cfg {String} badge text for badge
595  * @cfg {String} theme (default|glow)  
596  * @cfg {Boolean} inverse dark themed version
597  * @cfg {Boolean} toggle is it a slidy toggle button
598  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
599  * @cfg {String} ontext text for on slidy toggle state
600  * @cfg {String} offtext text for off slidy toggle state
601  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
602  * @cfg {Boolean} removeClass remove the standard class..
603  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
604  * 
605  * @constructor
606  * Create a new button
607  * @param {Object} config The config object
608  */
609
610
611 Roo.bootstrap.Button = function(config){
612     Roo.bootstrap.Button.superclass.constructor.call(this, config);
613     this.weightClass = ["btn-default btn-outline-secondary", 
614                        "btn-primary", 
615                        "btn-success", 
616                        "btn-info", 
617                        "btn-warning",
618                        "btn-danger",
619                        "btn-link"
620                       ],  
621     this.addEvents({
622         // raw events
623         /**
624          * @event click
625          * When a butotn is pressed
626          * @param {Roo.bootstrap.Button} btn
627          * @param {Roo.EventObject} e
628          */
629         "click" : true,
630          /**
631          * @event toggle
632          * After the button has been toggles
633          * @param {Roo.bootstrap.Button} btn
634          * @param {Roo.EventObject} e
635          * @param {boolean} pressed (also available as button.pressed)
636          */
637         "toggle" : true
638     });
639 };
640
641 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
642     html: false,
643     active: false,
644     weight: '',
645     badge_weight: '',
646     outline : false,
647     size: '',
648     tag: 'button',
649     href: '',
650     disabled: false,
651     isClose: false,
652     glyphicon: '',
653     fa: '',
654     badge: '',
655     theme: 'default',
656     inverse: false,
657     
658     toggle: false,
659     ontext: 'ON',
660     offtext: 'OFF',
661     defaulton: true,
662     preventDefault: true,
663     removeClass: false,
664     name: false,
665     target: false,
666      
667     pressed : null,
668      
669     
670     getAutoCreate : function(){
671         
672         var cfg = {
673             tag : 'button',
674             cls : 'roo-button',
675             html: ''
676         };
677         
678         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
679             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
680             this.tag = 'button';
681         } else {
682             cfg.tag = this.tag;
683         }
684         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
685         
686         if (this.toggle == true) {
687             cfg={
688                 tag: 'div',
689                 cls: 'slider-frame roo-button',
690                 cn: [
691                     {
692                         tag: 'span',
693                         'data-on-text':'ON',
694                         'data-off-text':'OFF',
695                         cls: 'slider-button',
696                         html: this.offtext
697                     }
698                 ]
699             };
700             
701             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
702                 cfg.cls += ' '+this.weight;
703             }
704             
705             return cfg;
706         }
707         
708         if (this.isClose) {
709             cfg.cls += ' close';
710             
711             cfg["aria-hidden"] = true;
712             
713             cfg.html = "&times;";
714             
715             return cfg;
716         }
717         
718          
719         if (this.theme==='default') {
720             cfg.cls = 'btn roo-button';
721             
722             //if (this.parentType != 'Navbar') {
723             this.weight = this.weight.length ?  this.weight : 'default';
724             //}
725             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
726                 
727                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
728                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
729                 cfg.cls += ' btn-' + outline + weight;
730                 if (this.weight == 'default') {
731                     // BC
732                     cfg.cls += ' btn-' + this.weight;
733                 }
734             }
735         } else if (this.theme==='glow') {
736             
737             cfg.tag = 'a';
738             cfg.cls = 'btn-glow roo-button';
739             
740             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
741                 
742                 cfg.cls += ' ' + this.weight;
743             }
744         }
745    
746         
747         if (this.inverse) {
748             this.cls += ' inverse';
749         }
750         
751         
752         if (this.active || this.pressed === true) {
753             cfg.cls += ' active';
754         }
755         
756         if (this.disabled) {
757             cfg.disabled = 'disabled';
758         }
759         
760         if (this.items) {
761             Roo.log('changing to ul' );
762             cfg.tag = 'ul';
763             this.glyphicon = 'caret';
764             if (Roo.bootstrap.version == 4) {
765                 this.fa = 'caret-down';
766             }
767             
768         }
769         
770         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
771          
772         //gsRoo.log(this.parentType);
773         if (this.parentType === 'Navbar' && !this.parent().bar) {
774             Roo.log('changing to li?');
775             
776             cfg.tag = 'li';
777             
778             cfg.cls = '';
779             cfg.cn =  [{
780                 tag : 'a',
781                 cls : 'roo-button',
782                 html : this.html,
783                 href : this.href || '#'
784             }];
785             if (this.menu) {
786                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
787                 cfg.cls += ' dropdown';
788             }   
789             
790             delete cfg.html;
791             
792         }
793         
794        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
795         
796         if (this.glyphicon) {
797             cfg.html = ' ' + cfg.html;
798             
799             cfg.cn = [
800                 {
801                     tag: 'span',
802                     cls: 'glyphicon glyphicon-' + this.glyphicon
803                 }
804             ];
805         }
806         if (this.fa) {
807             cfg.html = ' ' + cfg.html;
808             
809             cfg.cn = [
810                 {
811                     tag: 'i',
812                     cls: 'fa fas fa-' + this.fa
813                 }
814             ];
815         }
816         
817         if (this.badge) {
818             cfg.html += ' ';
819             
820             cfg.tag = 'a';
821             
822 //            cfg.cls='btn roo-button';
823             
824             cfg.href=this.href;
825             
826             var value = cfg.html;
827             
828             if(this.glyphicon){
829                 value = {
830                     tag: 'span',
831                     cls: 'glyphicon glyphicon-' + this.glyphicon,
832                     html: this.html
833                 };
834             }
835             if(this.fa){
836                 value = {
837                     tag: 'i',
838                     cls: 'fa fas fa-' + this.fa,
839                     html: this.html
840                 };
841             }
842             
843             var bw = this.badge_weight.length ? this.badge_weight :
844                 (this.weight.length ? this.weight : 'secondary');
845             bw = bw == 'default' ? 'secondary' : bw;
846             
847             cfg.cn = [
848                 value,
849                 {
850                     tag: 'span',
851                     cls: 'badge badge-' + bw,
852                     html: this.badge
853                 }
854             ];
855             
856             cfg.html='';
857         }
858         
859         if (this.menu) {
860             cfg.cls += ' dropdown';
861             cfg.html = typeof(cfg.html) != 'undefined' ?
862                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
863         }
864         
865         if (cfg.tag !== 'a' && this.href !== '') {
866             throw "Tag must be a to set href.";
867         } else if (this.href.length > 0) {
868             cfg.href = this.href;
869         }
870         
871         if(this.removeClass){
872             cfg.cls = '';
873         }
874         
875         if(this.target){
876             cfg.target = this.target;
877         }
878         
879         return cfg;
880     },
881     initEvents: function() {
882        // Roo.log('init events?');
883 //        Roo.log(this.el.dom);
884         // add the menu...
885         
886         if (typeof (this.menu) != 'undefined') {
887             this.menu.parentType = this.xtype;
888             this.menu.triggerEl = this.el;
889             this.addxtype(Roo.apply({}, this.menu));
890         }
891
892
893        if (this.el.hasClass('roo-button')) {
894             this.el.on('click', this.onClick, this);
895        } else {
896             this.el.select('.roo-button').on('click', this.onClick, this);
897        }
898        
899        if(this.removeClass){
900            this.el.on('click', this.onClick, this);
901        }
902        
903        this.el.enableDisplayMode();
904         
905     },
906     onClick : function(e)
907     {
908         if (this.disabled) {
909             return;
910         }
911         
912         Roo.log('button on click ');
913         if(this.preventDefault){
914             e.preventDefault();
915         }
916         
917         if (this.pressed === true || this.pressed === false) {
918             this.toggleActive(e);
919         }
920         
921         
922         this.fireEvent('click', this, e);
923     },
924     
925     /**
926      * Enables this button
927      */
928     enable : function()
929     {
930         this.disabled = false;
931         this.el.removeClass('disabled');
932     },
933     
934     /**
935      * Disable this button
936      */
937     disable : function()
938     {
939         this.disabled = true;
940         this.el.addClass('disabled');
941     },
942      /**
943      * sets the active state on/off, 
944      * @param {Boolean} state (optional) Force a particular state
945      */
946     setActive : function(v) {
947         
948         this.el[v ? 'addClass' : 'removeClass']('active');
949         this.pressed = v;
950     },
951      /**
952      * toggles the current active state 
953      */
954     toggleActive : function(e)
955     {
956         this.setActive(!this.pressed);
957         this.fireEvent('toggle', this, e, !this.pressed);
958     },
959      /**
960      * get the current active state
961      * @return {boolean} true if it's active
962      */
963     isActive : function()
964     {
965         return this.el.hasClass('active');
966     },
967     /**
968      * set the text of the first selected button
969      */
970     setText : function(str)
971     {
972         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
973     },
974     /**
975      * get the text of the first selected button
976      */
977     getText : function()
978     {
979         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
980     },
981     
982     setWeight : function(str)
983     {
984         this.el.removeClass(this.weightClass);
985         this.weight = str;
986         var outline = this.outline ? 'outline-' : '';
987         if (str == 'default') {
988             this.el.addClass('btn-default btn-outline-secondary');        
989             return;
990         }
991         this.el.addClass('btn-' + outline + str);        
992     }
993     
994     
995 });
996
997  /*
998  * - LGPL
999  *
1000  * column
1001  * 
1002  */
1003
1004 /**
1005  * @class Roo.bootstrap.Column
1006  * @extends Roo.bootstrap.Component
1007  * Bootstrap Column class
1008  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1009  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1010  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1011  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1012  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1013  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1014  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1015  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1016  *
1017  * 
1018  * @cfg {Boolean} hidden (true|false) hide the element
1019  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1020  * @cfg {String} fa (ban|check|...) font awesome icon
1021  * @cfg {Number} fasize (1|2|....) font awsome size
1022
1023  * @cfg {String} icon (info-sign|check|...) glyphicon name
1024
1025  * @cfg {String} html content of column.
1026  * 
1027  * @constructor
1028  * Create a new Column
1029  * @param {Object} config The config object
1030  */
1031
1032 Roo.bootstrap.Column = function(config){
1033     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1034 };
1035
1036 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1037     
1038     xs: false,
1039     sm: false,
1040     md: false,
1041     lg: false,
1042     xsoff: false,
1043     smoff: false,
1044     mdoff: false,
1045     lgoff: false,
1046     html: '',
1047     offset: 0,
1048     alert: false,
1049     fa: false,
1050     icon : false,
1051     hidden : false,
1052     fasize : 1,
1053     
1054     getAutoCreate : function(){
1055         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1056         
1057         cfg = {
1058             tag: 'div',
1059             cls: 'column'
1060         };
1061         
1062         var settings=this;
1063         ['xs','sm','md','lg'].map(function(size){
1064             //Roo.log( size + ':' + settings[size]);
1065             
1066             if (settings[size+'off'] !== false) {
1067                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1068             }
1069             
1070             if (settings[size] === false) {
1071                 return;
1072             }
1073             
1074             if (!settings[size]) { // 0 = hidden
1075                 cfg.cls += ' hidden-' + size;
1076                 return;
1077             }
1078             cfg.cls += ' col-' + size + '-' + settings[size];
1079             
1080         });
1081         
1082         if (this.hidden) {
1083             cfg.cls += ' hidden';
1084         }
1085         
1086         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1087             cfg.cls +=' alert alert-' + this.alert;
1088         }
1089         
1090         
1091         if (this.html.length) {
1092             cfg.html = this.html;
1093         }
1094         if (this.fa) {
1095             var fasize = '';
1096             if (this.fasize > 1) {
1097                 fasize = ' fa-' + this.fasize + 'x';
1098             }
1099             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1100             
1101             
1102         }
1103         if (this.icon) {
1104             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1105         }
1106         
1107         return cfg;
1108     }
1109    
1110 });
1111
1112  
1113
1114  /*
1115  * - LGPL
1116  *
1117  * page container.
1118  * 
1119  */
1120
1121
1122 /**
1123  * @class Roo.bootstrap.Container
1124  * @extends Roo.bootstrap.Component
1125  * Bootstrap Container class
1126  * @cfg {Boolean} jumbotron is it a jumbotron element
1127  * @cfg {String} html content of element
1128  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1129  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1130  * @cfg {String} header content of header (for panel)
1131  * @cfg {String} footer content of footer (for panel)
1132  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1133  * @cfg {String} tag (header|aside|section) type of HTML tag.
1134  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1135  * @cfg {String} fa font awesome icon
1136  * @cfg {String} icon (info-sign|check|...) glyphicon name
1137  * @cfg {Boolean} hidden (true|false) hide the element
1138  * @cfg {Boolean} expandable (true|false) default false
1139  * @cfg {Boolean} expanded (true|false) default true
1140  * @cfg {String} rheader contet on the right of header
1141  * @cfg {Boolean} clickable (true|false) default false
1142
1143  *     
1144  * @constructor
1145  * Create a new Container
1146  * @param {Object} config The config object
1147  */
1148
1149 Roo.bootstrap.Container = function(config){
1150     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1151     
1152     this.addEvents({
1153         // raw events
1154          /**
1155          * @event expand
1156          * After the panel has been expand
1157          * 
1158          * @param {Roo.bootstrap.Container} this
1159          */
1160         "expand" : true,
1161         /**
1162          * @event collapse
1163          * After the panel has been collapsed
1164          * 
1165          * @param {Roo.bootstrap.Container} this
1166          */
1167         "collapse" : true,
1168         /**
1169          * @event click
1170          * When a element is chick
1171          * @param {Roo.bootstrap.Container} this
1172          * @param {Roo.EventObject} e
1173          */
1174         "click" : true
1175     });
1176 };
1177
1178 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1179     
1180     jumbotron : false,
1181     well: '',
1182     panel : '',
1183     header: '',
1184     footer : '',
1185     sticky: '',
1186     tag : false,
1187     alert : false,
1188     fa: false,
1189     icon : false,
1190     expandable : false,
1191     rheader : '',
1192     expanded : true,
1193     clickable: false,
1194   
1195      
1196     getChildContainer : function() {
1197         
1198         if(!this.el){
1199             return false;
1200         }
1201         
1202         if (this.panel.length) {
1203             return this.el.select('.panel-body',true).first();
1204         }
1205         
1206         return this.el;
1207     },
1208     
1209     
1210     getAutoCreate : function(){
1211         
1212         var cfg = {
1213             tag : this.tag || 'div',
1214             html : '',
1215             cls : ''
1216         };
1217         if (this.jumbotron) {
1218             cfg.cls = 'jumbotron';
1219         }
1220         
1221         
1222         
1223         // - this is applied by the parent..
1224         //if (this.cls) {
1225         //    cfg.cls = this.cls + '';
1226         //}
1227         
1228         if (this.sticky.length) {
1229             
1230             var bd = Roo.get(document.body);
1231             if (!bd.hasClass('bootstrap-sticky')) {
1232                 bd.addClass('bootstrap-sticky');
1233                 Roo.select('html',true).setStyle('height', '100%');
1234             }
1235              
1236             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1237         }
1238         
1239         
1240         if (this.well.length) {
1241             switch (this.well) {
1242                 case 'lg':
1243                 case 'sm':
1244                     cfg.cls +=' well well-' +this.well;
1245                     break;
1246                 default:
1247                     cfg.cls +=' well';
1248                     break;
1249             }
1250         }
1251         
1252         if (this.hidden) {
1253             cfg.cls += ' hidden';
1254         }
1255         
1256         
1257         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1258             cfg.cls +=' alert alert-' + this.alert;
1259         }
1260         
1261         var body = cfg;
1262         
1263         if (this.panel.length) {
1264             cfg.cls += ' panel panel-' + this.panel;
1265             cfg.cn = [];
1266             if (this.header.length) {
1267                 
1268                 var h = [];
1269                 
1270                 if(this.expandable){
1271                     
1272                     cfg.cls = cfg.cls + ' expandable';
1273                     
1274                     h.push({
1275                         tag: 'i',
1276                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1277                     });
1278                     
1279                 }
1280                 
1281                 h.push(
1282                     {
1283                         tag: 'span',
1284                         cls : 'panel-title',
1285                         html : (this.expandable ? '&nbsp;' : '') + this.header
1286                     },
1287                     {
1288                         tag: 'span',
1289                         cls: 'panel-header-right',
1290                         html: this.rheader
1291                     }
1292                 );
1293                 
1294                 cfg.cn.push({
1295                     cls : 'panel-heading',
1296                     style : this.expandable ? 'cursor: pointer' : '',
1297                     cn : h
1298                 });
1299                 
1300             }
1301             
1302             body = false;
1303             cfg.cn.push({
1304                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1305                 html : this.html
1306             });
1307             
1308             
1309             if (this.footer.length) {
1310                 cfg.cn.push({
1311                     cls : 'panel-footer',
1312                     html : this.footer
1313                     
1314                 });
1315             }
1316             
1317         }
1318         
1319         if (body) {
1320             body.html = this.html || cfg.html;
1321             // prefix with the icons..
1322             if (this.fa) {
1323                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1324             }
1325             if (this.icon) {
1326                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1327             }
1328             
1329             
1330         }
1331         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1332             cfg.cls =  'container';
1333         }
1334         
1335         return cfg;
1336     },
1337     
1338     initEvents: function() 
1339     {
1340         if(this.expandable){
1341             var headerEl = this.headerEl();
1342         
1343             if(headerEl){
1344                 headerEl.on('click', this.onToggleClick, this);
1345             }
1346         }
1347         
1348         if(this.clickable){
1349             this.el.on('click', this.onClick, this);
1350         }
1351         
1352     },
1353     
1354     onToggleClick : function()
1355     {
1356         var headerEl = this.headerEl();
1357         
1358         if(!headerEl){
1359             return;
1360         }
1361         
1362         if(this.expanded){
1363             this.collapse();
1364             return;
1365         }
1366         
1367         this.expand();
1368     },
1369     
1370     expand : function()
1371     {
1372         if(this.fireEvent('expand', this)) {
1373             
1374             this.expanded = true;
1375             
1376             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1377             
1378             this.el.select('.panel-body',true).first().removeClass('hide');
1379             
1380             var toggleEl = this.toggleEl();
1381
1382             if(!toggleEl){
1383                 return;
1384             }
1385
1386             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1387         }
1388         
1389     },
1390     
1391     collapse : function()
1392     {
1393         if(this.fireEvent('collapse', this)) {
1394             
1395             this.expanded = false;
1396             
1397             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1398             this.el.select('.panel-body',true).first().addClass('hide');
1399         
1400             var toggleEl = this.toggleEl();
1401
1402             if(!toggleEl){
1403                 return;
1404             }
1405
1406             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1407         }
1408     },
1409     
1410     toggleEl : function()
1411     {
1412         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1413             return;
1414         }
1415         
1416         return this.el.select('.panel-heading .fa',true).first();
1417     },
1418     
1419     headerEl : function()
1420     {
1421         if(!this.el || !this.panel.length || !this.header.length){
1422             return;
1423         }
1424         
1425         return this.el.select('.panel-heading',true).first()
1426     },
1427     
1428     bodyEl : function()
1429     {
1430         if(!this.el || !this.panel.length){
1431             return;
1432         }
1433         
1434         return this.el.select('.panel-body',true).first()
1435     },
1436     
1437     titleEl : function()
1438     {
1439         if(!this.el || !this.panel.length || !this.header.length){
1440             return;
1441         }
1442         
1443         return this.el.select('.panel-title',true).first();
1444     },
1445     
1446     setTitle : function(v)
1447     {
1448         var titleEl = this.titleEl();
1449         
1450         if(!titleEl){
1451             return;
1452         }
1453         
1454         titleEl.dom.innerHTML = v;
1455     },
1456     
1457     getTitle : function()
1458     {
1459         
1460         var titleEl = this.titleEl();
1461         
1462         if(!titleEl){
1463             return '';
1464         }
1465         
1466         return titleEl.dom.innerHTML;
1467     },
1468     
1469     setRightTitle : function(v)
1470     {
1471         var t = this.el.select('.panel-header-right',true).first();
1472         
1473         if(!t){
1474             return;
1475         }
1476         
1477         t.dom.innerHTML = v;
1478     },
1479     
1480     onClick : function(e)
1481     {
1482         e.preventDefault();
1483         
1484         this.fireEvent('click', this, e);
1485     }
1486 });
1487
1488  /*
1489  * - LGPL
1490  *
1491  * image
1492  * 
1493  */
1494
1495
1496 /**
1497  * @class Roo.bootstrap.Img
1498  * @extends Roo.bootstrap.Component
1499  * Bootstrap Img class
1500  * @cfg {Boolean} imgResponsive false | true
1501  * @cfg {String} border rounded | circle | thumbnail
1502  * @cfg {String} src image source
1503  * @cfg {String} alt image alternative text
1504  * @cfg {String} href a tag href
1505  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1506  * @cfg {String} xsUrl xs image source
1507  * @cfg {String} smUrl sm image source
1508  * @cfg {String} mdUrl md image source
1509  * @cfg {String} lgUrl lg image source
1510  * 
1511  * @constructor
1512  * Create a new Input
1513  * @param {Object} config The config object
1514  */
1515
1516 Roo.bootstrap.Img = function(config){
1517     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1518     
1519     this.addEvents({
1520         // img events
1521         /**
1522          * @event click
1523          * The img click event for the img.
1524          * @param {Roo.EventObject} e
1525          */
1526         "click" : true
1527     });
1528 };
1529
1530 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1531     
1532     imgResponsive: true,
1533     border: '',
1534     src: 'about:blank',
1535     href: false,
1536     target: false,
1537     xsUrl: '',
1538     smUrl: '',
1539     mdUrl: '',
1540     lgUrl: '',
1541
1542     getAutoCreate : function()
1543     {   
1544         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1545             return this.createSingleImg();
1546         }
1547         
1548         var cfg = {
1549             tag: 'div',
1550             cls: 'roo-image-responsive-group',
1551             cn: []
1552         };
1553         var _this = this;
1554         
1555         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1556             
1557             if(!_this[size + 'Url']){
1558                 return;
1559             }
1560             
1561             var img = {
1562                 tag: 'img',
1563                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1564                 html: _this.html || cfg.html,
1565                 src: _this[size + 'Url']
1566             };
1567             
1568             img.cls += ' roo-image-responsive-' + size;
1569             
1570             var s = ['xs', 'sm', 'md', 'lg'];
1571             
1572             s.splice(s.indexOf(size), 1);
1573             
1574             Roo.each(s, function(ss){
1575                 img.cls += ' hidden-' + ss;
1576             });
1577             
1578             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1579                 cfg.cls += ' img-' + _this.border;
1580             }
1581             
1582             if(_this.alt){
1583                 cfg.alt = _this.alt;
1584             }
1585             
1586             if(_this.href){
1587                 var a = {
1588                     tag: 'a',
1589                     href: _this.href,
1590                     cn: [
1591                         img
1592                     ]
1593                 };
1594
1595                 if(this.target){
1596                     a.target = _this.target;
1597                 }
1598             }
1599             
1600             cfg.cn.push((_this.href) ? a : img);
1601             
1602         });
1603         
1604         return cfg;
1605     },
1606     
1607     createSingleImg : function()
1608     {
1609         var cfg = {
1610             tag: 'img',
1611             cls: (this.imgResponsive) ? 'img-responsive' : '',
1612             html : null,
1613             src : 'about:blank'  // just incase src get's set to undefined?!?
1614         };
1615         
1616         cfg.html = this.html || cfg.html;
1617         
1618         cfg.src = this.src || cfg.src;
1619         
1620         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1621             cfg.cls += ' img-' + this.border;
1622         }
1623         
1624         if(this.alt){
1625             cfg.alt = this.alt;
1626         }
1627         
1628         if(this.href){
1629             var a = {
1630                 tag: 'a',
1631                 href: this.href,
1632                 cn: [
1633                     cfg
1634                 ]
1635             };
1636             
1637             if(this.target){
1638                 a.target = this.target;
1639             }
1640             
1641         }
1642         
1643         return (this.href) ? a : cfg;
1644     },
1645     
1646     initEvents: function() 
1647     {
1648         if(!this.href){
1649             this.el.on('click', this.onClick, this);
1650         }
1651         
1652     },
1653     
1654     onClick : function(e)
1655     {
1656         Roo.log('img onclick');
1657         this.fireEvent('click', this, e);
1658     },
1659     /**
1660      * Sets the url of the image - used to update it
1661      * @param {String} url the url of the image
1662      */
1663     
1664     setSrc : function(url)
1665     {
1666         this.src =  url;
1667         
1668         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1669             this.el.dom.src =  url;
1670             return;
1671         }
1672         
1673         this.el.select('img', true).first().dom.src =  url;
1674     }
1675     
1676     
1677    
1678 });
1679
1680  /*
1681  * - LGPL
1682  *
1683  * image
1684  * 
1685  */
1686
1687
1688 /**
1689  * @class Roo.bootstrap.Link
1690  * @extends Roo.bootstrap.Component
1691  * Bootstrap Link Class
1692  * @cfg {String} alt image alternative text
1693  * @cfg {String} href a tag href
1694  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1695  * @cfg {String} html the content of the link.
1696  * @cfg {String} anchor name for the anchor link
1697  * @cfg {String} fa - favicon
1698
1699  * @cfg {Boolean} preventDefault (true | false) default false
1700
1701  * 
1702  * @constructor
1703  * Create a new Input
1704  * @param {Object} config The config object
1705  */
1706
1707 Roo.bootstrap.Link = function(config){
1708     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1709     
1710     this.addEvents({
1711         // img events
1712         /**
1713          * @event click
1714          * The img click event for the img.
1715          * @param {Roo.EventObject} e
1716          */
1717         "click" : true
1718     });
1719 };
1720
1721 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1722     
1723     href: false,
1724     target: false,
1725     preventDefault: false,
1726     anchor : false,
1727     alt : false,
1728     fa: false,
1729
1730
1731     getAutoCreate : function()
1732     {
1733         var html = this.html || '';
1734         
1735         if (this.fa !== false) {
1736             html = '<i class="fa fa-' + this.fa + '"></i>';
1737         }
1738         var cfg = {
1739             tag: 'a'
1740         };
1741         // anchor's do not require html/href...
1742         if (this.anchor === false) {
1743             cfg.html = html;
1744             cfg.href = this.href || '#';
1745         } else {
1746             cfg.name = this.anchor;
1747             if (this.html !== false || this.fa !== false) {
1748                 cfg.html = html;
1749             }
1750             if (this.href !== false) {
1751                 cfg.href = this.href;
1752             }
1753         }
1754         
1755         if(this.alt !== false){
1756             cfg.alt = this.alt;
1757         }
1758         
1759         
1760         if(this.target !== false) {
1761             cfg.target = this.target;
1762         }
1763         
1764         return cfg;
1765     },
1766     
1767     initEvents: function() {
1768         
1769         if(!this.href || this.preventDefault){
1770             this.el.on('click', this.onClick, this);
1771         }
1772     },
1773     
1774     onClick : function(e)
1775     {
1776         if(this.preventDefault){
1777             e.preventDefault();
1778         }
1779         //Roo.log('img onclick');
1780         this.fireEvent('click', this, e);
1781     }
1782    
1783 });
1784
1785  /*
1786  * - LGPL
1787  *
1788  * header
1789  * 
1790  */
1791
1792 /**
1793  * @class Roo.bootstrap.Header
1794  * @extends Roo.bootstrap.Component
1795  * Bootstrap Header class
1796  * @cfg {String} html content of header
1797  * @cfg {Number} level (1|2|3|4|5|6) default 1
1798  * 
1799  * @constructor
1800  * Create a new Header
1801  * @param {Object} config The config object
1802  */
1803
1804
1805 Roo.bootstrap.Header  = function(config){
1806     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1807 };
1808
1809 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1810     
1811     //href : false,
1812     html : false,
1813     level : 1,
1814     
1815     
1816     
1817     getAutoCreate : function(){
1818         
1819         
1820         
1821         var cfg = {
1822             tag: 'h' + (1 *this.level),
1823             html: this.html || ''
1824         } ;
1825         
1826         return cfg;
1827     }
1828    
1829 });
1830
1831  
1832
1833  /*
1834  * Based on:
1835  * Ext JS Library 1.1.1
1836  * Copyright(c) 2006-2007, Ext JS, LLC.
1837  *
1838  * Originally Released Under LGPL - original licence link has changed is not relivant.
1839  *
1840  * Fork - LGPL
1841  * <script type="text/javascript">
1842  */
1843  
1844 /**
1845  * @class Roo.bootstrap.MenuMgr
1846  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1847  * @singleton
1848  */
1849 Roo.bootstrap.MenuMgr = function(){
1850    var menus, active, groups = {}, attached = false, lastShow = new Date();
1851
1852    // private - called when first menu is created
1853    function init(){
1854        menus = {};
1855        active = new Roo.util.MixedCollection();
1856        Roo.get(document).addKeyListener(27, function(){
1857            if(active.length > 0){
1858                hideAll();
1859            }
1860        });
1861    }
1862
1863    // private
1864    function hideAll(){
1865        if(active && active.length > 0){
1866            var c = active.clone();
1867            c.each(function(m){
1868                m.hide();
1869            });
1870        }
1871    }
1872
1873    // private
1874    function onHide(m){
1875        active.remove(m);
1876        if(active.length < 1){
1877            Roo.get(document).un("mouseup", onMouseDown);
1878             
1879            attached = false;
1880        }
1881    }
1882
1883    // private
1884    function onShow(m){
1885        var last = active.last();
1886        lastShow = new Date();
1887        active.add(m);
1888        if(!attached){
1889           Roo.get(document).on("mouseup", onMouseDown);
1890            
1891            attached = true;
1892        }
1893        if(m.parentMenu){
1894           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1895           m.parentMenu.activeChild = m;
1896        }else if(last && last.isVisible()){
1897           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1898        }
1899    }
1900
1901    // private
1902    function onBeforeHide(m){
1903        if(m.activeChild){
1904            m.activeChild.hide();
1905        }
1906        if(m.autoHideTimer){
1907            clearTimeout(m.autoHideTimer);
1908            delete m.autoHideTimer;
1909        }
1910    }
1911
1912    // private
1913    function onBeforeShow(m){
1914        var pm = m.parentMenu;
1915        if(!pm && !m.allowOtherMenus){
1916            hideAll();
1917        }else if(pm && pm.activeChild && active != m){
1918            pm.activeChild.hide();
1919        }
1920    }
1921
1922    // private this should really trigger on mouseup..
1923    function onMouseDown(e){
1924         Roo.log("on Mouse Up");
1925         
1926         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1927             Roo.log("MenuManager hideAll");
1928             hideAll();
1929             e.stopEvent();
1930         }
1931         
1932         
1933    }
1934
1935    // private
1936    function onBeforeCheck(mi, state){
1937        if(state){
1938            var g = groups[mi.group];
1939            for(var i = 0, l = g.length; i < l; i++){
1940                if(g[i] != mi){
1941                    g[i].setChecked(false);
1942                }
1943            }
1944        }
1945    }
1946
1947    return {
1948
1949        /**
1950         * Hides all menus that are currently visible
1951         */
1952        hideAll : function(){
1953             hideAll();  
1954        },
1955
1956        // private
1957        register : function(menu){
1958            if(!menus){
1959                init();
1960            }
1961            menus[menu.id] = menu;
1962            menu.on("beforehide", onBeforeHide);
1963            menu.on("hide", onHide);
1964            menu.on("beforeshow", onBeforeShow);
1965            menu.on("show", onShow);
1966            var g = menu.group;
1967            if(g && menu.events["checkchange"]){
1968                if(!groups[g]){
1969                    groups[g] = [];
1970                }
1971                groups[g].push(menu);
1972                menu.on("checkchange", onCheck);
1973            }
1974        },
1975
1976         /**
1977          * Returns a {@link Roo.menu.Menu} object
1978          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1979          * be used to generate and return a new Menu instance.
1980          */
1981        get : function(menu){
1982            if(typeof menu == "string"){ // menu id
1983                return menus[menu];
1984            }else if(menu.events){  // menu instance
1985                return menu;
1986            }
1987            /*else if(typeof menu.length == 'number'){ // array of menu items?
1988                return new Roo.bootstrap.Menu({items:menu});
1989            }else{ // otherwise, must be a config
1990                return new Roo.bootstrap.Menu(menu);
1991            }
1992            */
1993            return false;
1994        },
1995
1996        // private
1997        unregister : function(menu){
1998            delete menus[menu.id];
1999            menu.un("beforehide", onBeforeHide);
2000            menu.un("hide", onHide);
2001            menu.un("beforeshow", onBeforeShow);
2002            menu.un("show", onShow);
2003            var g = menu.group;
2004            if(g && menu.events["checkchange"]){
2005                groups[g].remove(menu);
2006                menu.un("checkchange", onCheck);
2007            }
2008        },
2009
2010        // private
2011        registerCheckable : function(menuItem){
2012            var g = menuItem.group;
2013            if(g){
2014                if(!groups[g]){
2015                    groups[g] = [];
2016                }
2017                groups[g].push(menuItem);
2018                menuItem.on("beforecheckchange", onBeforeCheck);
2019            }
2020        },
2021
2022        // private
2023        unregisterCheckable : function(menuItem){
2024            var g = menuItem.group;
2025            if(g){
2026                groups[g].remove(menuItem);
2027                menuItem.un("beforecheckchange", onBeforeCheck);
2028            }
2029        }
2030    };
2031 }();/*
2032  * - LGPL
2033  *
2034  * menu
2035  * 
2036  */
2037
2038 /**
2039  * @class Roo.bootstrap.Menu
2040  * @extends Roo.bootstrap.Component
2041  * Bootstrap Menu class - container for MenuItems
2042  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2043  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2044  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2045  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2046  * 
2047  * @constructor
2048  * Create a new Menu
2049  * @param {Object} config The config object
2050  */
2051
2052
2053 Roo.bootstrap.Menu = function(config){
2054     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2055     if (this.registerMenu && this.type != 'treeview')  {
2056         Roo.bootstrap.MenuMgr.register(this);
2057     }
2058     
2059     
2060     this.addEvents({
2061         /**
2062          * @event beforeshow
2063          * Fires before this menu is displayed
2064          * @param {Roo.menu.Menu} this
2065          */
2066         beforeshow : true,
2067         /**
2068          * @event beforehide
2069          * Fires before this menu is hidden
2070          * @param {Roo.menu.Menu} this
2071          */
2072         beforehide : true,
2073         /**
2074          * @event show
2075          * Fires after this menu is displayed
2076          * @param {Roo.menu.Menu} this
2077          */
2078         show : true,
2079         /**
2080          * @event hide
2081          * Fires after this menu is hidden
2082          * @param {Roo.menu.Menu} this
2083          */
2084         hide : true,
2085         /**
2086          * @event click
2087          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2088          * @param {Roo.menu.Menu} this
2089          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2090          * @param {Roo.EventObject} e
2091          */
2092         click : true,
2093         /**
2094          * @event mouseover
2095          * Fires when the mouse is hovering over this menu
2096          * @param {Roo.menu.Menu} this
2097          * @param {Roo.EventObject} e
2098          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2099          */
2100         mouseover : true,
2101         /**
2102          * @event mouseout
2103          * Fires when the mouse exits this menu
2104          * @param {Roo.menu.Menu} this
2105          * @param {Roo.EventObject} e
2106          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2107          */
2108         mouseout : true,
2109         /**
2110          * @event itemclick
2111          * Fires when a menu item contained in this menu is clicked
2112          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2113          * @param {Roo.EventObject} e
2114          */
2115         itemclick: true
2116     });
2117     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2118 };
2119
2120 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2121     
2122    /// html : false,
2123     //align : '',
2124     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2125     type: false,
2126     /**
2127      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2128      */
2129     registerMenu : true,
2130     
2131     menuItems :false, // stores the menu items..
2132     
2133     hidden:true,
2134         
2135     parentMenu : false,
2136     
2137     stopEvent : true,
2138     
2139     isLink : false,
2140     
2141     getChildContainer : function() {
2142         return this.el;  
2143     },
2144     
2145     getAutoCreate : function(){
2146          
2147         //if (['right'].indexOf(this.align)!==-1) {
2148         //    cfg.cn[1].cls += ' pull-right'
2149         //}
2150         
2151         
2152         var cfg = {
2153             tag : 'ul',
2154             cls : 'dropdown-menu' ,
2155             style : 'z-index:1000'
2156             
2157         };
2158         
2159         if (this.type === 'submenu') {
2160             cfg.cls = 'submenu active';
2161         }
2162         if (this.type === 'treeview') {
2163             cfg.cls = 'treeview-menu';
2164         }
2165         
2166         return cfg;
2167     },
2168     initEvents : function() {
2169         
2170        // Roo.log("ADD event");
2171        // Roo.log(this.triggerEl.dom);
2172         
2173         this.triggerEl.on('click', this.onTriggerClick, this);
2174         
2175         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2176         
2177         
2178         if (this.triggerEl.hasClass('nav-item')) {
2179             // dropdown toggle on the 'a' in BS4?
2180             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2181         } else {
2182             this.triggerEl.addClass('dropdown-toggle');
2183         }
2184         if (Roo.isTouch) {
2185             this.el.on('touchstart'  , this.onTouch, this);
2186         }
2187         this.el.on('click' , this.onClick, this);
2188
2189         this.el.on("mouseover", this.onMouseOver, this);
2190         this.el.on("mouseout", this.onMouseOut, this);
2191         
2192     },
2193     
2194     findTargetItem : function(e)
2195     {
2196         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2197         if(!t){
2198             return false;
2199         }
2200         //Roo.log(t);         Roo.log(t.id);
2201         if(t && t.id){
2202             //Roo.log(this.menuitems);
2203             return this.menuitems.get(t.id);
2204             
2205             //return this.items.get(t.menuItemId);
2206         }
2207         
2208         return false;
2209     },
2210     
2211     onTouch : function(e) 
2212     {
2213         Roo.log("menu.onTouch");
2214         //e.stopEvent(); this make the user popdown broken
2215         this.onClick(e);
2216     },
2217     
2218     onClick : function(e)
2219     {
2220         Roo.log("menu.onClick");
2221         
2222         var t = this.findTargetItem(e);
2223         if(!t || t.isContainer){
2224             return;
2225         }
2226         Roo.log(e);
2227         /*
2228         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2229             if(t == this.activeItem && t.shouldDeactivate(e)){
2230                 this.activeItem.deactivate();
2231                 delete this.activeItem;
2232                 return;
2233             }
2234             if(t.canActivate){
2235                 this.setActiveItem(t, true);
2236             }
2237             return;
2238             
2239             
2240         }
2241         */
2242        
2243         Roo.log('pass click event');
2244         
2245         t.onClick(e);
2246         
2247         this.fireEvent("click", this, t, e);
2248         
2249         var _this = this;
2250         
2251         if(!t.href.length || t.href == '#'){
2252             (function() { _this.hide(); }).defer(100);
2253         }
2254         
2255     },
2256     
2257     onMouseOver : function(e){
2258         var t  = this.findTargetItem(e);
2259         //Roo.log(t);
2260         //if(t){
2261         //    if(t.canActivate && !t.disabled){
2262         //        this.setActiveItem(t, true);
2263         //    }
2264         //}
2265         
2266         this.fireEvent("mouseover", this, e, t);
2267     },
2268     isVisible : function(){
2269         return !this.hidden;
2270     },
2271      onMouseOut : function(e){
2272         var t  = this.findTargetItem(e);
2273         
2274         //if(t ){
2275         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2276         //        this.activeItem.deactivate();
2277         //        delete this.activeItem;
2278         //    }
2279         //}
2280         this.fireEvent("mouseout", this, e, t);
2281     },
2282     
2283     
2284     /**
2285      * Displays this menu relative to another element
2286      * @param {String/HTMLElement/Roo.Element} element The element to align to
2287      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2288      * the element (defaults to this.defaultAlign)
2289      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2290      */
2291     show : function(el, pos, parentMenu){
2292         this.parentMenu = parentMenu;
2293         if(!this.el){
2294             this.render();
2295         }
2296         this.fireEvent("beforeshow", this);
2297         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2298     },
2299      /**
2300      * Displays this menu at a specific xy position
2301      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2302      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2303      */
2304     showAt : function(xy, parentMenu, /* private: */_e){
2305         this.parentMenu = parentMenu;
2306         if(!this.el){
2307             this.render();
2308         }
2309         if(_e !== false){
2310             this.fireEvent("beforeshow", this);
2311             //xy = this.el.adjustForConstraints(xy);
2312         }
2313         
2314         //this.el.show();
2315         this.hideMenuItems();
2316         this.hidden = false;
2317         this.triggerEl.addClass('open');
2318         this.el.addClass('show');
2319         
2320         // reassign x when hitting right
2321         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2322             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2323         }
2324         
2325         // reassign y when hitting bottom
2326         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2327             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2328         }
2329         
2330         // but the list may align on trigger left or trigger top... should it be a properity?
2331         
2332         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2333             this.el.setXY(xy);
2334         }
2335         
2336         this.focus();
2337         this.fireEvent("show", this);
2338     },
2339     
2340     focus : function(){
2341         return;
2342         if(!this.hidden){
2343             this.doFocus.defer(50, this);
2344         }
2345     },
2346
2347     doFocus : function(){
2348         if(!this.hidden){
2349             this.focusEl.focus();
2350         }
2351     },
2352
2353     /**
2354      * Hides this menu and optionally all parent menus
2355      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2356      */
2357     hide : function(deep)
2358     {
2359         
2360         this.hideMenuItems();
2361         if(this.el && this.isVisible()){
2362             this.fireEvent("beforehide", this);
2363             if(this.activeItem){
2364                 this.activeItem.deactivate();
2365                 this.activeItem = null;
2366             }
2367             this.triggerEl.removeClass('open');;
2368             this.el.removeClass('show');
2369             this.hidden = true;
2370             this.fireEvent("hide", this);
2371         }
2372         if(deep === true && this.parentMenu){
2373             this.parentMenu.hide(true);
2374         }
2375     },
2376     
2377     onTriggerClick : function(e)
2378     {
2379         Roo.log('trigger click');
2380         
2381         var target = e.getTarget();
2382         
2383         Roo.log(target.nodeName.toLowerCase());
2384         
2385         if(target.nodeName.toLowerCase() === 'i'){
2386             e.preventDefault();
2387         }
2388         
2389     },
2390     
2391     onTriggerPress  : function(e)
2392     {
2393         Roo.log('trigger press');
2394         //Roo.log(e.getTarget());
2395        // Roo.log(this.triggerEl.dom);
2396        
2397         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2398         var pel = Roo.get(e.getTarget());
2399         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2400             Roo.log('is treeview or dropdown?');
2401             return;
2402         }
2403         
2404         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2405             return;
2406         }
2407         
2408         if (this.isVisible()) {
2409             Roo.log('hide');
2410             this.hide();
2411         } else {
2412             Roo.log('show');
2413             this.show(this.triggerEl, false, false);
2414         }
2415         
2416         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2417             e.stopEvent();
2418         }
2419         
2420     },
2421        
2422     
2423     hideMenuItems : function()
2424     {
2425         Roo.log("hide Menu Items");
2426         if (!this.el) { 
2427             return;
2428         }
2429         //$(backdrop).remove()
2430         this.el.select('.open',true).each(function(aa) {
2431             
2432             aa.removeClass('open');
2433           //var parent = getParent($(this))
2434           //var relatedTarget = { relatedTarget: this }
2435           
2436            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2437           //if (e.isDefaultPrevented()) return
2438            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2439         });
2440     },
2441     addxtypeChild : function (tree, cntr) {
2442         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2443           
2444         this.menuitems.add(comp);
2445         return comp;
2446
2447     },
2448     getEl : function()
2449     {
2450         Roo.log(this.el);
2451         return this.el;
2452     },
2453     
2454     clear : function()
2455     {
2456         this.getEl().dom.innerHTML = '';
2457         this.menuitems.clear();
2458     }
2459 });
2460
2461  
2462  /*
2463  * - LGPL
2464  *
2465  * menu item
2466  * 
2467  */
2468
2469
2470 /**
2471  * @class Roo.bootstrap.MenuItem
2472  * @extends Roo.bootstrap.Component
2473  * Bootstrap MenuItem class
2474  * @cfg {String} html the menu label
2475  * @cfg {String} href the link
2476  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2477  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2478  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2479  * @cfg {String} fa favicon to show on left of menu item.
2480  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2481  * 
2482  * 
2483  * @constructor
2484  * Create a new MenuItem
2485  * @param {Object} config The config object
2486  */
2487
2488
2489 Roo.bootstrap.MenuItem = function(config){
2490     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2491     this.addEvents({
2492         // raw events
2493         /**
2494          * @event click
2495          * The raw click event for the entire grid.
2496          * @param {Roo.bootstrap.MenuItem} this
2497          * @param {Roo.EventObject} e
2498          */
2499         "click" : true
2500     });
2501 };
2502
2503 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2504     
2505     href : false,
2506     html : false,
2507     preventDefault: false,
2508     isContainer : false,
2509     active : false,
2510     fa: false,
2511     
2512     getAutoCreate : function(){
2513         
2514         if(this.isContainer){
2515             return {
2516                 tag: 'li',
2517                 cls: 'dropdown-menu-item '
2518             };
2519         }
2520         var ctag = {
2521             tag: 'span',
2522             html: 'Link'
2523         };
2524         
2525         var anc = {
2526             tag : 'a',
2527             cls : 'dropdown-item',
2528             href : '#',
2529             cn : [  ]
2530         };
2531         
2532         if (this.fa !== false) {
2533             anc.cn.push({
2534                 tag : 'i',
2535                 cls : 'fa fa-' + this.fa
2536             });
2537         }
2538         
2539         anc.cn.push(ctag);
2540         
2541         
2542         var cfg= {
2543             tag: 'li',
2544             cls: 'dropdown-menu-item',
2545             cn: [ anc ]
2546         };
2547         if (this.parent().type == 'treeview') {
2548             cfg.cls = 'treeview-menu';
2549         }
2550         if (this.active) {
2551             cfg.cls += ' active';
2552         }
2553         
2554         
2555         
2556         anc.href = this.href || cfg.cn[0].href ;
2557         ctag.html = this.html || cfg.cn[0].html ;
2558         return cfg;
2559     },
2560     
2561     initEvents: function()
2562     {
2563         if (this.parent().type == 'treeview') {
2564             this.el.select('a').on('click', this.onClick, this);
2565         }
2566         
2567         if (this.menu) {
2568             this.menu.parentType = this.xtype;
2569             this.menu.triggerEl = this.el;
2570             this.menu = this.addxtype(Roo.apply({}, this.menu));
2571         }
2572         
2573     },
2574     onClick : function(e)
2575     {
2576         Roo.log('item on click ');
2577         
2578         if(this.preventDefault){
2579             e.preventDefault();
2580         }
2581         //this.parent().hideMenuItems();
2582         
2583         this.fireEvent('click', this, e);
2584     },
2585     getEl : function()
2586     {
2587         return this.el;
2588     } 
2589 });
2590
2591  
2592
2593  /*
2594  * - LGPL
2595  *
2596  * menu separator
2597  * 
2598  */
2599
2600
2601 /**
2602  * @class Roo.bootstrap.MenuSeparator
2603  * @extends Roo.bootstrap.Component
2604  * Bootstrap MenuSeparator class
2605  * 
2606  * @constructor
2607  * Create a new MenuItem
2608  * @param {Object} config The config object
2609  */
2610
2611
2612 Roo.bootstrap.MenuSeparator = function(config){
2613     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2614 };
2615
2616 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2617     
2618     getAutoCreate : function(){
2619         var cfg = {
2620             cls: 'divider',
2621             tag : 'li'
2622         };
2623         
2624         return cfg;
2625     }
2626    
2627 });
2628
2629  
2630
2631  
2632 /*
2633 * Licence: LGPL
2634 */
2635
2636 /**
2637  * @class Roo.bootstrap.Modal
2638  * @extends Roo.bootstrap.Component
2639  * Bootstrap Modal class
2640  * @cfg {String} title Title of dialog
2641  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2642  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2643  * @cfg {Boolean} specificTitle default false
2644  * @cfg {Array} buttons Array of buttons or standard button set..
2645  * @cfg {String} buttonPosition (left|right|center) default right
2646  * @cfg {Boolean} animate default true
2647  * @cfg {Boolean} allow_close default true
2648  * @cfg {Boolean} fitwindow default false
2649  * @cfg {String} size (sm|lg) default empty
2650  * @cfg {Number} max_width set the max width of modal
2651  *
2652  *
2653  * @constructor
2654  * Create a new Modal Dialog
2655  * @param {Object} config The config object
2656  */
2657
2658 Roo.bootstrap.Modal = function(config){
2659     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2660     this.addEvents({
2661         // raw events
2662         /**
2663          * @event btnclick
2664          * The raw btnclick event for the button
2665          * @param {Roo.EventObject} e
2666          */
2667         "btnclick" : true,
2668         /**
2669          * @event resize
2670          * Fire when dialog resize
2671          * @param {Roo.bootstrap.Modal} this
2672          * @param {Roo.EventObject} e
2673          */
2674         "resize" : true
2675     });
2676     this.buttons = this.buttons || [];
2677
2678     if (this.tmpl) {
2679         this.tmpl = Roo.factory(this.tmpl);
2680     }
2681
2682 };
2683
2684 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2685
2686     title : 'test dialog',
2687
2688     buttons : false,
2689
2690     // set on load...
2691
2692     html: false,
2693
2694     tmp: false,
2695
2696     specificTitle: false,
2697
2698     buttonPosition: 'right',
2699
2700     allow_close : true,
2701
2702     animate : true,
2703
2704     fitwindow: false,
2705     
2706      // private
2707     dialogEl: false,
2708     bodyEl:  false,
2709     footerEl:  false,
2710     titleEl:  false,
2711     closeEl:  false,
2712
2713     size: '',
2714     
2715     max_width: 0,
2716     
2717     max_height: 0,
2718     
2719     fit_content: false,
2720
2721     onRender : function(ct, position)
2722     {
2723         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2724
2725         if(!this.el){
2726             var cfg = Roo.apply({},  this.getAutoCreate());
2727             cfg.id = Roo.id();
2728             //if(!cfg.name){
2729             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2730             //}
2731             //if (!cfg.name.length) {
2732             //    delete cfg.name;
2733            // }
2734             if (this.cls) {
2735                 cfg.cls += ' ' + this.cls;
2736             }
2737             if (this.style) {
2738                 cfg.style = this.style;
2739             }
2740             this.el = Roo.get(document.body).createChild(cfg, position);
2741         }
2742         //var type = this.el.dom.type;
2743
2744
2745         if(this.tabIndex !== undefined){
2746             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2747         }
2748
2749         this.dialogEl = this.el.select('.modal-dialog',true).first();
2750         this.bodyEl = this.el.select('.modal-body',true).first();
2751         this.closeEl = this.el.select('.modal-header .close', true).first();
2752         this.headerEl = this.el.select('.modal-header',true).first();
2753         this.titleEl = this.el.select('.modal-title',true).first();
2754         this.footerEl = this.el.select('.modal-footer',true).first();
2755
2756         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2757         
2758         //this.el.addClass("x-dlg-modal");
2759
2760         if (this.buttons.length) {
2761             Roo.each(this.buttons, function(bb) {
2762                 var b = Roo.apply({}, bb);
2763                 b.xns = b.xns || Roo.bootstrap;
2764                 b.xtype = b.xtype || 'Button';
2765                 if (typeof(b.listeners) == 'undefined') {
2766                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2767                 }
2768
2769                 var btn = Roo.factory(b);
2770
2771                 btn.render(this.el.select('.modal-footer div').first());
2772
2773             },this);
2774         }
2775         // render the children.
2776         var nitems = [];
2777
2778         if(typeof(this.items) != 'undefined'){
2779             var items = this.items;
2780             delete this.items;
2781
2782             for(var i =0;i < items.length;i++) {
2783                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2784             }
2785         }
2786
2787         this.items = nitems;
2788
2789         // where are these used - they used to be body/close/footer
2790
2791
2792         this.initEvents();
2793         //this.el.addClass([this.fieldClass, this.cls]);
2794
2795     },
2796
2797     getAutoCreate : function()
2798     {
2799         var bdy = {
2800                 cls : 'modal-body',
2801                 html : this.html || ''
2802         };
2803
2804         var title = {
2805             tag: 'h4',
2806             cls : 'modal-title',
2807             html : this.title
2808         };
2809
2810         if(this.specificTitle){
2811             title = this.title;
2812
2813         };
2814
2815         var header = [];
2816         if (this.allow_close && Roo.bootstrap.version == 3) {
2817             header.push({
2818                 tag: 'button',
2819                 cls : 'close',
2820                 html : '&times'
2821             });
2822         }
2823
2824         header.push(title);
2825
2826         if (this.allow_close && Roo.bootstrap.version == 4) {
2827             header.push({
2828                 tag: 'button',
2829                 cls : 'close',
2830                 html : '&times'
2831             });
2832         }
2833         
2834         var size = '';
2835
2836         if(this.size.length){
2837             size = 'modal-' + this.size;
2838         }
2839
2840         var modal = {
2841             cls: "modal",
2842              cn : [
2843                 {
2844                     cls: "modal-dialog " + size,
2845                     cn : [
2846                         {
2847                             cls : "modal-content",
2848                             cn : [
2849                                 {
2850                                     cls : 'modal-header',
2851                                     cn : header
2852                                 },
2853                                 bdy,
2854                                 {
2855                                     cls : 'modal-footer',
2856                                     cn : [
2857                                         {
2858                                             tag: 'div',
2859                                             cls: 'btn-' + this.buttonPosition
2860                                         }
2861                                     ]
2862
2863                                 }
2864
2865
2866                             ]
2867
2868                         }
2869                     ]
2870
2871                 }
2872             ]
2873         };
2874
2875         if(this.animate){
2876             modal.cls += ' fade';
2877         }
2878
2879         return modal;
2880
2881     },
2882     getChildContainer : function() {
2883
2884          return this.bodyEl;
2885
2886     },
2887     getButtonContainer : function() {
2888          return this.el.select('.modal-footer div',true).first();
2889
2890     },
2891     initEvents : function()
2892     {
2893         if (this.allow_close) {
2894             this.closeEl.on('click', this.hide, this);
2895         }
2896         Roo.EventManager.onWindowResize(this.resize, this, true);
2897
2898
2899     },
2900
2901     resize : function()
2902     {
2903         this.maskEl.setSize(
2904             Roo.lib.Dom.getViewWidth(true),
2905             Roo.lib.Dom.getViewHeight(true)
2906         );
2907         
2908         if (this.fitwindow) {
2909             this.setSize(
2910                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2911                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2912             );
2913             return;
2914         }
2915         
2916         if(this.max_width !== 0) {
2917             
2918             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2919             
2920             if(this.height) {
2921                 this.setSize(w, this.height);
2922                 return;
2923             }
2924             
2925             if(this.max_height) {
2926                 this.setSize(w,Math.min(
2927                     this.max_height,
2928                     Roo.lib.Dom.getViewportHeight(true) - 60
2929                 ));
2930                 
2931                 return;
2932             }
2933             
2934             if(!this.fit_content) {
2935                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2936                 return;
2937             }
2938             
2939             this.setSize(w, Math.min(
2940                 60 +
2941                 this.headerEl.getHeight() + 
2942                 this.footerEl.getHeight() + 
2943                 this.getChildHeight(this.bodyEl.dom.childNodes),
2944                 Roo.lib.Dom.getViewportHeight(true) - 60)
2945             );
2946         }
2947         
2948     },
2949
2950     setSize : function(w,h)
2951     {
2952         if (!w && !h) {
2953             return;
2954         }
2955         
2956         this.resizeTo(w,h);
2957     },
2958
2959     show : function() {
2960
2961         if (!this.rendered) {
2962             this.render();
2963         }
2964
2965         //this.el.setStyle('display', 'block');
2966         this.el.removeClass('hideing');
2967         this.el.dom.style.display='block';
2968         
2969         Roo.get(document.body).addClass('modal-open');
2970  
2971         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2972             var _this = this;
2973             (function(){
2974                 this.el.addClass('show');
2975                 this.el.addClass('in');
2976             }).defer(50, this);
2977         }else{
2978             this.el.addClass('show');
2979             this.el.addClass('in');
2980         }
2981
2982         // not sure how we can show data in here..
2983         //if (this.tmpl) {
2984         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2985         //}
2986
2987         Roo.get(document.body).addClass("x-body-masked");
2988         
2989         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2990         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2991         this.maskEl.dom.style.display = 'block';
2992         this.maskEl.addClass('show');
2993         
2994         
2995         this.resize();
2996         
2997         this.fireEvent('show', this);
2998
2999         // set zindex here - otherwise it appears to be ignored...
3000         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3001
3002         (function () {
3003             this.items.forEach( function(e) {
3004                 e.layout ? e.layout() : false;
3005
3006             });
3007         }).defer(100,this);
3008
3009     },
3010     hide : function()
3011     {
3012         if(this.fireEvent("beforehide", this) !== false){
3013             
3014             this.maskEl.removeClass('show');
3015             
3016             this.maskEl.dom.style.display = '';
3017             Roo.get(document.body).removeClass("x-body-masked");
3018             this.el.removeClass('in');
3019             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3020
3021             if(this.animate){ // why
3022                 this.el.addClass('hideing');
3023                 this.el.removeClass('show');
3024                 (function(){
3025                     if (!this.el.hasClass('hideing')) {
3026                         return; // it's been shown again...
3027                     }
3028                     
3029                     this.el.dom.style.display='';
3030
3031                     Roo.get(document.body).removeClass('modal-open');
3032                     this.el.removeClass('hideing');
3033                 }).defer(150,this);
3034                 
3035             }else{
3036                 this.el.removeClass('show');
3037                 this.el.dom.style.display='';
3038                 Roo.get(document.body).removeClass('modal-open');
3039
3040             }
3041             this.fireEvent('hide', this);
3042         }
3043     },
3044     isVisible : function()
3045     {
3046         
3047         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3048         
3049     },
3050
3051     addButton : function(str, cb)
3052     {
3053
3054
3055         var b = Roo.apply({}, { html : str } );
3056         b.xns = b.xns || Roo.bootstrap;
3057         b.xtype = b.xtype || 'Button';
3058         if (typeof(b.listeners) == 'undefined') {
3059             b.listeners = { click : cb.createDelegate(this)  };
3060         }
3061
3062         var btn = Roo.factory(b);
3063
3064         btn.render(this.el.select('.modal-footer div').first());
3065
3066         return btn;
3067
3068     },
3069
3070     setDefaultButton : function(btn)
3071     {
3072         //this.el.select('.modal-footer').()
3073     },
3074     diff : false,
3075
3076     resizeTo: function(w,h)
3077     {
3078         // skip.. ?? why??
3079
3080         this.dialogEl.setWidth(w);
3081         if (this.diff === false) {
3082             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3083         }
3084
3085         this.bodyEl.setHeight(h - this.diff);
3086
3087         this.fireEvent('resize', this);
3088
3089     },
3090     setContentSize  : function(w, h)
3091     {
3092
3093     },
3094     onButtonClick: function(btn,e)
3095     {
3096         //Roo.log([a,b,c]);
3097         this.fireEvent('btnclick', btn.name, e);
3098     },
3099      /**
3100      * Set the title of the Dialog
3101      * @param {String} str new Title
3102      */
3103     setTitle: function(str) {
3104         this.titleEl.dom.innerHTML = str;
3105     },
3106     /**
3107      * Set the body of the Dialog
3108      * @param {String} str new Title
3109      */
3110     setBody: function(str) {
3111         this.bodyEl.dom.innerHTML = str;
3112     },
3113     /**
3114      * Set the body of the Dialog using the template
3115      * @param {Obj} data - apply this data to the template and replace the body contents.
3116      */
3117     applyBody: function(obj)
3118     {
3119         if (!this.tmpl) {
3120             Roo.log("Error - using apply Body without a template");
3121             //code
3122         }
3123         this.tmpl.overwrite(this.bodyEl, obj);
3124     },
3125     
3126     getChildHeight : function(child_nodes)
3127     {
3128         if(
3129             !child_nodes ||
3130             child_nodes.length == 0
3131         ) {
3132             return;
3133         }
3134         
3135         var child_height = 0;
3136         
3137         for(var i = 0; i < child_nodes.length; i++) {
3138             
3139             /*
3140             * for modal with tabs...
3141             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3142                 
3143                 var layout_childs = child_nodes[i].childNodes;
3144                 
3145                 for(var j = 0; j < layout_childs.length; j++) {
3146                     
3147                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3148                         
3149                         var layout_body_childs = layout_childs[j].childNodes;
3150                         
3151                         for(var k = 0; k < layout_body_childs.length; k++) {
3152                             
3153                             if(layout_body_childs[k].classList.contains('navbar')) {
3154                                 child_height += layout_body_childs[k].offsetHeight;
3155                                 continue;
3156                             }
3157                             
3158                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3159                                 
3160                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3161                                 
3162                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3163                                     
3164                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3165                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3166                                         continue;
3167                                     }
3168                                     
3169                                 }
3170                                 
3171                             }
3172                             
3173                         }
3174                     }
3175                 }
3176                 continue;
3177             }
3178             */
3179             
3180             child_height += child_nodes[i].offsetHeight;
3181             // Roo.log(child_nodes[i].offsetHeight);
3182         }
3183         
3184         return child_height;
3185     }
3186
3187 });
3188
3189
3190 Roo.apply(Roo.bootstrap.Modal,  {
3191     /**
3192          * Button config that displays a single OK button
3193          * @type Object
3194          */
3195         OK :  [{
3196             name : 'ok',
3197             weight : 'primary',
3198             html : 'OK'
3199         }],
3200         /**
3201          * Button config that displays Yes and No buttons
3202          * @type Object
3203          */
3204         YESNO : [
3205             {
3206                 name  : 'no',
3207                 html : 'No'
3208             },
3209             {
3210                 name  :'yes',
3211                 weight : 'primary',
3212                 html : 'Yes'
3213             }
3214         ],
3215
3216         /**
3217          * Button config that displays OK and Cancel buttons
3218          * @type Object
3219          */
3220         OKCANCEL : [
3221             {
3222                name : 'cancel',
3223                 html : 'Cancel'
3224             },
3225             {
3226                 name : 'ok',
3227                 weight : 'primary',
3228                 html : 'OK'
3229             }
3230         ],
3231         /**
3232          * Button config that displays Yes, No and Cancel buttons
3233          * @type Object
3234          */
3235         YESNOCANCEL : [
3236             {
3237                 name : 'yes',
3238                 weight : 'primary',
3239                 html : 'Yes'
3240             },
3241             {
3242                 name : 'no',
3243                 html : 'No'
3244             },
3245             {
3246                 name : 'cancel',
3247                 html : 'Cancel'
3248             }
3249         ],
3250         
3251         zIndex : 10001
3252 });
3253 /*
3254  * - LGPL
3255  *
3256  * messagebox - can be used as a replace
3257  * 
3258  */
3259 /**
3260  * @class Roo.MessageBox
3261  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3262  * Example usage:
3263  *<pre><code>
3264 // Basic alert:
3265 Roo.Msg.alert('Status', 'Changes saved successfully.');
3266
3267 // Prompt for user data:
3268 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3269     if (btn == 'ok'){
3270         // process text value...
3271     }
3272 });
3273
3274 // Show a dialog using config options:
3275 Roo.Msg.show({
3276    title:'Save Changes?',
3277    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3278    buttons: Roo.Msg.YESNOCANCEL,
3279    fn: processResult,
3280    animEl: 'elId'
3281 });
3282 </code></pre>
3283  * @singleton
3284  */
3285 Roo.bootstrap.MessageBox = function(){
3286     var dlg, opt, mask, waitTimer;
3287     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3288     var buttons, activeTextEl, bwidth;
3289
3290     
3291     // private
3292     var handleButton = function(button){
3293         dlg.hide();
3294         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3295     };
3296
3297     // private
3298     var handleHide = function(){
3299         if(opt && opt.cls){
3300             dlg.el.removeClass(opt.cls);
3301         }
3302         //if(waitTimer){
3303         //    Roo.TaskMgr.stop(waitTimer);
3304         //    waitTimer = null;
3305         //}
3306     };
3307
3308     // private
3309     var updateButtons = function(b){
3310         var width = 0;
3311         if(!b){
3312             buttons["ok"].hide();
3313             buttons["cancel"].hide();
3314             buttons["yes"].hide();
3315             buttons["no"].hide();
3316             //dlg.footer.dom.style.display = 'none';
3317             return width;
3318         }
3319         dlg.footerEl.dom.style.display = '';
3320         for(var k in buttons){
3321             if(typeof buttons[k] != "function"){
3322                 if(b[k]){
3323                     buttons[k].show();
3324                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3325                     width += buttons[k].el.getWidth()+15;
3326                 }else{
3327                     buttons[k].hide();
3328                 }
3329             }
3330         }
3331         return width;
3332     };
3333
3334     // private
3335     var handleEsc = function(d, k, e){
3336         if(opt && opt.closable !== false){
3337             dlg.hide();
3338         }
3339         if(e){
3340             e.stopEvent();
3341         }
3342     };
3343
3344     return {
3345         /**
3346          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3347          * @return {Roo.BasicDialog} The BasicDialog element
3348          */
3349         getDialog : function(){
3350            if(!dlg){
3351                 dlg = new Roo.bootstrap.Modal( {
3352                     //draggable: true,
3353                     //resizable:false,
3354                     //constraintoviewport:false,
3355                     //fixedcenter:true,
3356                     //collapsible : false,
3357                     //shim:true,
3358                     //modal: true,
3359                 //    width: 'auto',
3360                   //  height:100,
3361                     //buttonAlign:"center",
3362                     closeClick : function(){
3363                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3364                             handleButton("no");
3365                         }else{
3366                             handleButton("cancel");
3367                         }
3368                     }
3369                 });
3370                 dlg.render();
3371                 dlg.on("hide", handleHide);
3372                 mask = dlg.mask;
3373                 //dlg.addKeyListener(27, handleEsc);
3374                 buttons = {};
3375                 this.buttons = buttons;
3376                 var bt = this.buttonText;
3377                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3378                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3379                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3380                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3381                 //Roo.log(buttons);
3382                 bodyEl = dlg.bodyEl.createChild({
3383
3384                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3385                         '<textarea class="roo-mb-textarea"></textarea>' +
3386                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3387                 });
3388                 msgEl = bodyEl.dom.firstChild;
3389                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3390                 textboxEl.enableDisplayMode();
3391                 textboxEl.addKeyListener([10,13], function(){
3392                     if(dlg.isVisible() && opt && opt.buttons){
3393                         if(opt.buttons.ok){
3394                             handleButton("ok");
3395                         }else if(opt.buttons.yes){
3396                             handleButton("yes");
3397                         }
3398                     }
3399                 });
3400                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3401                 textareaEl.enableDisplayMode();
3402                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3403                 progressEl.enableDisplayMode();
3404                 
3405                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3406                 var pf = progressEl.dom.firstChild;
3407                 if (pf) {
3408                     pp = Roo.get(pf.firstChild);
3409                     pp.setHeight(pf.offsetHeight);
3410                 }
3411                 
3412             }
3413             return dlg;
3414         },
3415
3416         /**
3417          * Updates the message box body text
3418          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3419          * the XHTML-compliant non-breaking space character '&amp;#160;')
3420          * @return {Roo.MessageBox} This message box
3421          */
3422         updateText : function(text)
3423         {
3424             if(!dlg.isVisible() && !opt.width){
3425                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3426                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3427             }
3428             msgEl.innerHTML = text || '&#160;';
3429       
3430             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3431             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3432             var w = Math.max(
3433                     Math.min(opt.width || cw , this.maxWidth), 
3434                     Math.max(opt.minWidth || this.minWidth, bwidth)
3435             );
3436             if(opt.prompt){
3437                 activeTextEl.setWidth(w);
3438             }
3439             if(dlg.isVisible()){
3440                 dlg.fixedcenter = false;
3441             }
3442             // to big, make it scroll. = But as usual stupid IE does not support
3443             // !important..
3444             
3445             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3446                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3447                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3448             } else {
3449                 bodyEl.dom.style.height = '';
3450                 bodyEl.dom.style.overflowY = '';
3451             }
3452             if (cw > w) {
3453                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3454             } else {
3455                 bodyEl.dom.style.overflowX = '';
3456             }
3457             
3458             dlg.setContentSize(w, bodyEl.getHeight());
3459             if(dlg.isVisible()){
3460                 dlg.fixedcenter = true;
3461             }
3462             return this;
3463         },
3464
3465         /**
3466          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3467          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3468          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3469          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3470          * @return {Roo.MessageBox} This message box
3471          */
3472         updateProgress : function(value, text){
3473             if(text){
3474                 this.updateText(text);
3475             }
3476             
3477             if (pp) { // weird bug on my firefox - for some reason this is not defined
3478                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3479                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3480             }
3481             return this;
3482         },        
3483
3484         /**
3485          * Returns true if the message box is currently displayed
3486          * @return {Boolean} True if the message box is visible, else false
3487          */
3488         isVisible : function(){
3489             return dlg && dlg.isVisible();  
3490         },
3491
3492         /**
3493          * Hides the message box if it is displayed
3494          */
3495         hide : function(){
3496             if(this.isVisible()){
3497                 dlg.hide();
3498             }  
3499         },
3500
3501         /**
3502          * Displays a new message box, or reinitializes an existing message box, based on the config options
3503          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3504          * The following config object properties are supported:
3505          * <pre>
3506 Property    Type             Description
3507 ----------  ---------------  ------------------------------------------------------------------------------------
3508 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3509                                    closes (defaults to undefined)
3510 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3511                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3512 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3513                                    progress and wait dialogs will ignore this property and always hide the
3514                                    close button as they can only be closed programmatically.
3515 cls               String           A custom CSS class to apply to the message box element
3516 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3517                                    displayed (defaults to 75)
3518 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3519                                    function will be btn (the name of the button that was clicked, if applicable,
3520                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3521                                    Progress and wait dialogs will ignore this option since they do not respond to
3522                                    user actions and can only be closed programmatically, so any required function
3523                                    should be called by the same code after it closes the dialog.
3524 icon              String           A CSS class that provides a background image to be used as an icon for
3525                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3526 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3527 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3528 modal             Boolean          False to allow user interaction with the page while the message box is
3529                                    displayed (defaults to true)
3530 msg               String           A string that will replace the existing message box body text (defaults
3531                                    to the XHTML-compliant non-breaking space character '&#160;')
3532 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3533 progress          Boolean          True to display a progress bar (defaults to false)
3534 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3535 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3536 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3537 title             String           The title text
3538 value             String           The string value to set into the active textbox element if displayed
3539 wait              Boolean          True to display a progress bar (defaults to false)
3540 width             Number           The width of the dialog in pixels
3541 </pre>
3542          *
3543          * Example usage:
3544          * <pre><code>
3545 Roo.Msg.show({
3546    title: 'Address',
3547    msg: 'Please enter your address:',
3548    width: 300,
3549    buttons: Roo.MessageBox.OKCANCEL,
3550    multiline: true,
3551    fn: saveAddress,
3552    animEl: 'addAddressBtn'
3553 });
3554 </code></pre>
3555          * @param {Object} config Configuration options
3556          * @return {Roo.MessageBox} This message box
3557          */
3558         show : function(options)
3559         {
3560             
3561             // this causes nightmares if you show one dialog after another
3562             // especially on callbacks..
3563              
3564             if(this.isVisible()){
3565                 
3566                 this.hide();
3567                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3568                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3569                 Roo.log("New Dialog Message:" +  options.msg )
3570                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3571                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3572                 
3573             }
3574             var d = this.getDialog();
3575             opt = options;
3576             d.setTitle(opt.title || "&#160;");
3577             d.closeEl.setDisplayed(opt.closable !== false);
3578             activeTextEl = textboxEl;
3579             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3580             if(opt.prompt){
3581                 if(opt.multiline){
3582                     textboxEl.hide();
3583                     textareaEl.show();
3584                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3585                         opt.multiline : this.defaultTextHeight);
3586                     activeTextEl = textareaEl;
3587                 }else{
3588                     textboxEl.show();
3589                     textareaEl.hide();
3590                 }
3591             }else{
3592                 textboxEl.hide();
3593                 textareaEl.hide();
3594             }
3595             progressEl.setDisplayed(opt.progress === true);
3596             this.updateProgress(0);
3597             activeTextEl.dom.value = opt.value || "";
3598             if(opt.prompt){
3599                 dlg.setDefaultButton(activeTextEl);
3600             }else{
3601                 var bs = opt.buttons;
3602                 var db = null;
3603                 if(bs && bs.ok){
3604                     db = buttons["ok"];
3605                 }else if(bs && bs.yes){
3606                     db = buttons["yes"];
3607                 }
3608                 dlg.setDefaultButton(db);
3609             }
3610             bwidth = updateButtons(opt.buttons);
3611             this.updateText(opt.msg);
3612             if(opt.cls){
3613                 d.el.addClass(opt.cls);
3614             }
3615             d.proxyDrag = opt.proxyDrag === true;
3616             d.modal = opt.modal !== false;
3617             d.mask = opt.modal !== false ? mask : false;
3618             if(!d.isVisible()){
3619                 // force it to the end of the z-index stack so it gets a cursor in FF
3620                 document.body.appendChild(dlg.el.dom);
3621                 d.animateTarget = null;
3622                 d.show(options.animEl);
3623             }
3624             return this;
3625         },
3626
3627         /**
3628          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3629          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3630          * and closing the message box when the process is complete.
3631          * @param {String} title The title bar text
3632          * @param {String} msg The message box body text
3633          * @return {Roo.MessageBox} This message box
3634          */
3635         progress : function(title, msg){
3636             this.show({
3637                 title : title,
3638                 msg : msg,
3639                 buttons: false,
3640                 progress:true,
3641                 closable:false,
3642                 minWidth: this.minProgressWidth,
3643                 modal : true
3644             });
3645             return this;
3646         },
3647
3648         /**
3649          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3650          * If a callback function is passed it will be called after the user clicks the button, and the
3651          * id of the button that was clicked will be passed as the only parameter to the callback
3652          * (could also be the top-right close button).
3653          * @param {String} title The title bar text
3654          * @param {String} msg The message box body text
3655          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3656          * @param {Object} scope (optional) The scope of the callback function
3657          * @return {Roo.MessageBox} This message box
3658          */
3659         alert : function(title, msg, fn, scope)
3660         {
3661             this.show({
3662                 title : title,
3663                 msg : msg,
3664                 buttons: this.OK,
3665                 fn: fn,
3666                 closable : false,
3667                 scope : scope,
3668                 modal : true
3669             });
3670             return this;
3671         },
3672
3673         /**
3674          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3675          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3676          * You are responsible for closing the message box when the process is complete.
3677          * @param {String} msg The message box body text
3678          * @param {String} title (optional) The title bar text
3679          * @return {Roo.MessageBox} This message box
3680          */
3681         wait : function(msg, title){
3682             this.show({
3683                 title : title,
3684                 msg : msg,
3685                 buttons: false,
3686                 closable:false,
3687                 progress:true,
3688                 modal:true,
3689                 width:300,
3690                 wait:true
3691             });
3692             waitTimer = Roo.TaskMgr.start({
3693                 run: function(i){
3694                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3695                 },
3696                 interval: 1000
3697             });
3698             return this;
3699         },
3700
3701         /**
3702          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3703          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3704          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3705          * @param {String} title The title bar text
3706          * @param {String} msg The message box body text
3707          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3708          * @param {Object} scope (optional) The scope of the callback function
3709          * @return {Roo.MessageBox} This message box
3710          */
3711         confirm : function(title, msg, fn, scope){
3712             this.show({
3713                 title : title,
3714                 msg : msg,
3715                 buttons: this.YESNO,
3716                 fn: fn,
3717                 scope : scope,
3718                 modal : true
3719             });
3720             return this;
3721         },
3722
3723         /**
3724          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3725          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3726          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3727          * (could also be the top-right close button) and the text that was entered will be passed as the two
3728          * parameters to the callback.
3729          * @param {String} title The title bar text
3730          * @param {String} msg The message box body text
3731          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3732          * @param {Object} scope (optional) The scope of the callback function
3733          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3734          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3735          * @return {Roo.MessageBox} This message box
3736          */
3737         prompt : function(title, msg, fn, scope, multiline){
3738             this.show({
3739                 title : title,
3740                 msg : msg,
3741                 buttons: this.OKCANCEL,
3742                 fn: fn,
3743                 minWidth:250,
3744                 scope : scope,
3745                 prompt:true,
3746                 multiline: multiline,
3747                 modal : true
3748             });
3749             return this;
3750         },
3751
3752         /**
3753          * Button config that displays a single OK button
3754          * @type Object
3755          */
3756         OK : {ok:true},
3757         /**
3758          * Button config that displays Yes and No buttons
3759          * @type Object
3760          */
3761         YESNO : {yes:true, no:true},
3762         /**
3763          * Button config that displays OK and Cancel buttons
3764          * @type Object
3765          */
3766         OKCANCEL : {ok:true, cancel:true},
3767         /**
3768          * Button config that displays Yes, No and Cancel buttons
3769          * @type Object
3770          */
3771         YESNOCANCEL : {yes:true, no:true, cancel:true},
3772
3773         /**
3774          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3775          * @type Number
3776          */
3777         defaultTextHeight : 75,
3778         /**
3779          * The maximum width in pixels of the message box (defaults to 600)
3780          * @type Number
3781          */
3782         maxWidth : 600,
3783         /**
3784          * The minimum width in pixels of the message box (defaults to 100)
3785          * @type Number
3786          */
3787         minWidth : 100,
3788         /**
3789          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3790          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3791          * @type Number
3792          */
3793         minProgressWidth : 250,
3794         /**
3795          * An object containing the default button text strings that can be overriden for localized language support.
3796          * Supported properties are: ok, cancel, yes and no.
3797          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3798          * @type Object
3799          */
3800         buttonText : {
3801             ok : "OK",
3802             cancel : "Cancel",
3803             yes : "Yes",
3804             no : "No"
3805         }
3806     };
3807 }();
3808
3809 /**
3810  * Shorthand for {@link Roo.MessageBox}
3811  */
3812 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3813 Roo.Msg = Roo.Msg || Roo.MessageBox;
3814 /*
3815  * - LGPL
3816  *
3817  * navbar
3818  * 
3819  */
3820
3821 /**
3822  * @class Roo.bootstrap.Navbar
3823  * @extends Roo.bootstrap.Component
3824  * Bootstrap Navbar class
3825
3826  * @constructor
3827  * Create a new Navbar
3828  * @param {Object} config The config object
3829  */
3830
3831
3832 Roo.bootstrap.Navbar = function(config){
3833     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3834     this.addEvents({
3835         // raw events
3836         /**
3837          * @event beforetoggle
3838          * Fire before toggle the menu
3839          * @param {Roo.EventObject} e
3840          */
3841         "beforetoggle" : true
3842     });
3843 };
3844
3845 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3846     
3847     
3848    
3849     // private
3850     navItems : false,
3851     loadMask : false,
3852     
3853     
3854     getAutoCreate : function(){
3855         
3856         
3857         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3858         
3859     },
3860     
3861     initEvents :function ()
3862     {
3863         //Roo.log(this.el.select('.navbar-toggle',true));
3864         this.el.select('.navbar-toggle',true).on('click', function() {
3865             if(this.fireEvent('beforetoggle', this) !== false){
3866                 var ce = this.el.select('.navbar-collapse',true).first();
3867                 ce.toggleClass('in'); // old...
3868                 if (ce.hasClass('collapse')) {
3869                     // show it...
3870                     ce.removeClass('collapse');
3871                     ce.addClass('show');
3872                     var h = ce.getHeight();
3873                     Roo.log(h);
3874                     ce.removeClass('show');
3875                     // at this point we should be able to see it..
3876                     ce.addClass('collapsing');
3877                     
3878                     ce.setHeight(0); // resize it ...
3879                     ce.on('transitionend', function() {
3880                         Roo.log('done transition');
3881                         ce.removeClass('collapsing');
3882                         ce.addClass('show');
3883                         ce.removeClass('collapse');
3884
3885                         ce.dom.style.height = '';
3886                     }, this, { single: true} );
3887                     ce.setHeight(h);
3888                     
3889                 } else {
3890                     ce.setHeight(ce.getHeight());
3891                     ce.removeClass('show');
3892                     ce.addClass('collapsing');
3893                     
3894                     ce.on('transitionend', function() {
3895                         ce.dom.style.height = '';
3896                         ce.removeClass('collapsing');
3897                         ce.addClass('collapse');
3898                     }, this, { single: true} );
3899                     ce.setHeight(0);
3900                 }
3901             }
3902             
3903         }, this);
3904         
3905         var mark = {
3906             tag: "div",
3907             cls:"x-dlg-mask"
3908         };
3909         
3910         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3911         
3912         var size = this.el.getSize();
3913         this.maskEl.setSize(size.width, size.height);
3914         this.maskEl.enableDisplayMode("block");
3915         this.maskEl.hide();
3916         
3917         if(this.loadMask){
3918             this.maskEl.show();
3919         }
3920     },
3921     
3922     
3923     getChildContainer : function()
3924     {
3925         if (this.el.select('.collapse').getCount()) {
3926             return this.el.select('.collapse',true).first();
3927         }
3928         
3929         return this.el;
3930     },
3931     
3932     mask : function()
3933     {
3934         this.maskEl.show();
3935     },
3936     
3937     unmask : function()
3938     {
3939         this.maskEl.hide();
3940     } 
3941     
3942     
3943     
3944     
3945 });
3946
3947
3948
3949  
3950
3951  /*
3952  * - LGPL
3953  *
3954  * navbar
3955  * 
3956  */
3957
3958 /**
3959  * @class Roo.bootstrap.NavSimplebar
3960  * @extends Roo.bootstrap.Navbar
3961  * Bootstrap Sidebar class
3962  *
3963  * @cfg {Boolean} inverse is inverted color
3964  * 
3965  * @cfg {String} type (nav | pills | tabs)
3966  * @cfg {Boolean} arrangement stacked | justified
3967  * @cfg {String} align (left | right) alignment
3968  * 
3969  * @cfg {Boolean} main (true|false) main nav bar? default false
3970  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3971  * 
3972  * @cfg {String} tag (header|footer|nav|div) default is nav 
3973
3974  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3975  * 
3976  * 
3977  * @constructor
3978  * Create a new Sidebar
3979  * @param {Object} config The config object
3980  */
3981
3982
3983 Roo.bootstrap.NavSimplebar = function(config){
3984     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3985 };
3986
3987 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3988     
3989     inverse: false,
3990     
3991     type: false,
3992     arrangement: '',
3993     align : false,
3994     
3995     weight : 'light',
3996     
3997     main : false,
3998     
3999     
4000     tag : false,
4001     
4002     
4003     getAutoCreate : function(){
4004         
4005         
4006         var cfg = {
4007             tag : this.tag || 'div',
4008             cls : 'navbar navbar-expand-lg'
4009         };
4010         if (['light','white'].indexOf(this.weight) > -1) {
4011             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4012         }
4013         cfg.cls += ' bg-' + this.weight;
4014         
4015           
4016         
4017         cfg.cn = [
4018             {
4019                 cls: 'nav',
4020                 tag : 'ul'
4021             }
4022         ];
4023         
4024          
4025         this.type = this.type || 'nav';
4026         if (['tabs','pills'].indexOf(this.type)!==-1) {
4027             cfg.cn[0].cls += ' nav-' + this.type
4028         
4029         
4030         } else {
4031             if (this.type!=='nav') {
4032                 Roo.log('nav type must be nav/tabs/pills')
4033             }
4034             cfg.cn[0].cls += ' navbar-nav'
4035         }
4036         
4037         
4038         
4039         
4040         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4041             cfg.cn[0].cls += ' nav-' + this.arrangement;
4042         }
4043         
4044         
4045         if (this.align === 'right') {
4046             cfg.cn[0].cls += ' navbar-right';
4047         }
4048         
4049         if (this.inverse) {
4050             cfg.cls += ' navbar-inverse';
4051             
4052         }
4053         
4054         
4055         return cfg;
4056     
4057         
4058     }
4059     
4060     
4061     
4062 });
4063
4064
4065
4066  
4067
4068  
4069        /*
4070  * - LGPL
4071  *
4072  * navbar
4073  * navbar-fixed-top
4074  * navbar-expand-md  fixed-top 
4075  */
4076
4077 /**
4078  * @class Roo.bootstrap.NavHeaderbar
4079  * @extends Roo.bootstrap.NavSimplebar
4080  * Bootstrap Sidebar class
4081  *
4082  * @cfg {String} brand what is brand
4083  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4084  * @cfg {String} brand_href href of the brand
4085  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4086  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4087  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4088  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4089  * 
4090  * @constructor
4091  * Create a new Sidebar
4092  * @param {Object} config The config object
4093  */
4094
4095
4096 Roo.bootstrap.NavHeaderbar = function(config){
4097     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4098       
4099 };
4100
4101 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4102     
4103     position: '',
4104     brand: '',
4105     brand_href: false,
4106     srButton : true,
4107     autohide : false,
4108     desktopCenter : false,
4109    
4110     
4111     getAutoCreate : function(){
4112         
4113         var   cfg = {
4114             tag: this.nav || 'nav',
4115             cls: 'navbar navbar-expand-md',
4116             role: 'navigation',
4117             cn: []
4118         };
4119         
4120         var cn = cfg.cn;
4121         if (this.desktopCenter) {
4122             cn.push({cls : 'container', cn : []});
4123             cn = cn[0].cn;
4124         }
4125         
4126         if(this.srButton){
4127             var btn = {
4128                 tag: 'button',
4129                 type: 'button',
4130                 cls: 'navbar-toggle navbar-toggler',
4131                 'data-toggle': 'collapse',
4132                 cn: [
4133                     {
4134                         tag: 'span',
4135                         cls: 'sr-only',
4136                         html: 'Toggle navigation'
4137                     },
4138                     {
4139                         tag: 'span',
4140                         cls: 'icon-bar navbar-toggler-icon'
4141                     },
4142                     {
4143                         tag: 'span',
4144                         cls: 'icon-bar'
4145                     },
4146                     {
4147                         tag: 'span',
4148                         cls: 'icon-bar'
4149                     }
4150                 ]
4151             };
4152             
4153             cn.push( Roo.bootstrap.version == 4 ? btn : {
4154                 tag: 'div',
4155                 cls: 'navbar-header',
4156                 cn: [
4157                     btn
4158                 ]
4159             });
4160         }
4161         
4162         cn.push({
4163             tag: 'div',
4164             cls: 'collapse navbar-collapse',
4165             cn : []
4166         });
4167         
4168         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4169         
4170         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4171             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4172             
4173             // tag can override this..
4174             
4175             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4176         }
4177         
4178         if (this.brand !== '') {
4179             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4180             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4181                 tag: 'a',
4182                 href: this.brand_href ? this.brand_href : '#',
4183                 cls: 'navbar-brand',
4184                 cn: [
4185                 this.brand
4186                 ]
4187             });
4188         }
4189         
4190         if(this.main){
4191             cfg.cls += ' main-nav';
4192         }
4193         
4194         
4195         return cfg;
4196
4197         
4198     },
4199     getHeaderChildContainer : function()
4200     {
4201         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4202             return this.el.select('.navbar-header',true).first();
4203         }
4204         
4205         return this.getChildContainer();
4206     },
4207     
4208     
4209     initEvents : function()
4210     {
4211         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4212         
4213         if (this.autohide) {
4214             
4215             var prevScroll = 0;
4216             var ft = this.el;
4217             
4218             Roo.get(document).on('scroll',function(e) {
4219                 var ns = Roo.get(document).getScroll().top;
4220                 var os = prevScroll;
4221                 prevScroll = ns;
4222                 
4223                 if(ns > os){
4224                     ft.removeClass('slideDown');
4225                     ft.addClass('slideUp');
4226                     return;
4227                 }
4228                 ft.removeClass('slideUp');
4229                 ft.addClass('slideDown');
4230                  
4231               
4232           },this);
4233         }
4234     }    
4235     
4236 });
4237
4238
4239
4240  
4241
4242  /*
4243  * - LGPL
4244  *
4245  * navbar
4246  * 
4247  */
4248
4249 /**
4250  * @class Roo.bootstrap.NavSidebar
4251  * @extends Roo.bootstrap.Navbar
4252  * Bootstrap Sidebar class
4253  * 
4254  * @constructor
4255  * Create a new Sidebar
4256  * @param {Object} config The config object
4257  */
4258
4259
4260 Roo.bootstrap.NavSidebar = function(config){
4261     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4262 };
4263
4264 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4265     
4266     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4267     
4268     getAutoCreate : function(){
4269         
4270         
4271         return  {
4272             tag: 'div',
4273             cls: 'sidebar sidebar-nav'
4274         };
4275     
4276         
4277     }
4278     
4279     
4280     
4281 });
4282
4283
4284
4285  
4286
4287  /*
4288  * - LGPL
4289  *
4290  * nav group
4291  * 
4292  */
4293
4294 /**
4295  * @class Roo.bootstrap.NavGroup
4296  * @extends Roo.bootstrap.Component
4297  * Bootstrap NavGroup class
4298  * @cfg {String} align (left|right)
4299  * @cfg {Boolean} inverse
4300  * @cfg {String} type (nav|pills|tab) default nav
4301  * @cfg {String} navId - reference Id for navbar.
4302
4303  * 
4304  * @constructor
4305  * Create a new nav group
4306  * @param {Object} config The config object
4307  */
4308
4309 Roo.bootstrap.NavGroup = function(config){
4310     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4311     this.navItems = [];
4312    
4313     Roo.bootstrap.NavGroup.register(this);
4314      this.addEvents({
4315         /**
4316              * @event changed
4317              * Fires when the active item changes
4318              * @param {Roo.bootstrap.NavGroup} this
4319              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4320              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4321          */
4322         'changed': true
4323      });
4324     
4325 };
4326
4327 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4328     
4329     align: '',
4330     inverse: false,
4331     form: false,
4332     type: 'nav',
4333     navId : '',
4334     // private
4335     
4336     navItems : false, 
4337     
4338     getAutoCreate : function()
4339     {
4340         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4341         
4342         cfg = {
4343             tag : 'ul',
4344             cls: 'nav' 
4345         };
4346         
4347         if (['tabs','pills'].indexOf(this.type)!==-1) {
4348             cfg.cls += ' nav-' + this.type
4349         } else {
4350             if (this.type!=='nav') {
4351                 Roo.log('nav type must be nav/tabs/pills')
4352             }
4353             cfg.cls += ' navbar-nav'
4354         }
4355         
4356         if (this.parent() && this.parent().sidebar) {
4357             cfg = {
4358                 tag: 'ul',
4359                 cls: 'dashboard-menu sidebar-menu'
4360             };
4361             
4362             return cfg;
4363         }
4364         
4365         if (this.form === true) {
4366             cfg = {
4367                 tag: 'form',
4368                 cls: 'navbar-form'
4369             };
4370             
4371             if (this.align === 'right') {
4372                 cfg.cls += ' navbar-right ml-md-auto';
4373             } else {
4374                 cfg.cls += ' navbar-left';
4375             }
4376         }
4377         
4378         if (this.align === 'right') {
4379             cfg.cls += ' navbar-right ml-md-auto';
4380         } else {
4381             cfg.cls += ' mr-auto';
4382         }
4383         
4384         if (this.inverse) {
4385             cfg.cls += ' navbar-inverse';
4386             
4387         }
4388         
4389         
4390         return cfg;
4391     },
4392     /**
4393     * sets the active Navigation item
4394     * @param {Roo.bootstrap.NavItem} the new current navitem
4395     */
4396     setActiveItem : function(item)
4397     {
4398         var prev = false;
4399         Roo.each(this.navItems, function(v){
4400             if (v == item) {
4401                 return ;
4402             }
4403             if (v.isActive()) {
4404                 v.setActive(false, true);
4405                 prev = v;
4406                 
4407             }
4408             
4409         });
4410
4411         item.setActive(true, true);
4412         this.fireEvent('changed', this, item, prev);
4413         
4414         
4415     },
4416     /**
4417     * gets the active Navigation item
4418     * @return {Roo.bootstrap.NavItem} the current navitem
4419     */
4420     getActive : function()
4421     {
4422         
4423         var prev = false;
4424         Roo.each(this.navItems, function(v){
4425             
4426             if (v.isActive()) {
4427                 prev = v;
4428                 
4429             }
4430             
4431         });
4432         return prev;
4433     },
4434     
4435     indexOfNav : function()
4436     {
4437         
4438         var prev = false;
4439         Roo.each(this.navItems, function(v,i){
4440             
4441             if (v.isActive()) {
4442                 prev = i;
4443                 
4444             }
4445             
4446         });
4447         return prev;
4448     },
4449     /**
4450     * adds a Navigation item
4451     * @param {Roo.bootstrap.NavItem} the navitem to add
4452     */
4453     addItem : function(cfg)
4454     {
4455         var cn = new Roo.bootstrap.NavItem(cfg);
4456         this.register(cn);
4457         cn.parentId = this.id;
4458         cn.onRender(this.el, null);
4459         return cn;
4460     },
4461     /**
4462     * register a Navigation item
4463     * @param {Roo.bootstrap.NavItem} the navitem to add
4464     */
4465     register : function(item)
4466     {
4467         this.navItems.push( item);
4468         item.navId = this.navId;
4469     
4470     },
4471     
4472     /**
4473     * clear all the Navigation item
4474     */
4475    
4476     clearAll : function()
4477     {
4478         this.navItems = [];
4479         this.el.dom.innerHTML = '';
4480     },
4481     
4482     getNavItem: function(tabId)
4483     {
4484         var ret = false;
4485         Roo.each(this.navItems, function(e) {
4486             if (e.tabId == tabId) {
4487                ret =  e;
4488                return false;
4489             }
4490             return true;
4491             
4492         });
4493         return ret;
4494     },
4495     
4496     setActiveNext : function()
4497     {
4498         var i = this.indexOfNav(this.getActive());
4499         if (i > this.navItems.length) {
4500             return;
4501         }
4502         this.setActiveItem(this.navItems[i+1]);
4503     },
4504     setActivePrev : function()
4505     {
4506         var i = this.indexOfNav(this.getActive());
4507         if (i  < 1) {
4508             return;
4509         }
4510         this.setActiveItem(this.navItems[i-1]);
4511     },
4512     clearWasActive : function(except) {
4513         Roo.each(this.navItems, function(e) {
4514             if (e.tabId != except.tabId && e.was_active) {
4515                e.was_active = false;
4516                return false;
4517             }
4518             return true;
4519             
4520         });
4521     },
4522     getWasActive : function ()
4523     {
4524         var r = false;
4525         Roo.each(this.navItems, function(e) {
4526             if (e.was_active) {
4527                r = e;
4528                return false;
4529             }
4530             return true;
4531             
4532         });
4533         return r;
4534     }
4535     
4536     
4537 });
4538
4539  
4540 Roo.apply(Roo.bootstrap.NavGroup, {
4541     
4542     groups: {},
4543      /**
4544     * register a Navigation Group
4545     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4546     */
4547     register : function(navgrp)
4548     {
4549         this.groups[navgrp.navId] = navgrp;
4550         
4551     },
4552     /**
4553     * fetch a Navigation Group based on the navigation ID
4554     * @param {string} the navgroup to add
4555     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4556     */
4557     get: function(navId) {
4558         if (typeof(this.groups[navId]) == 'undefined') {
4559             return false;
4560             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4561         }
4562         return this.groups[navId] ;
4563     }
4564     
4565     
4566     
4567 });
4568
4569  /*
4570  * - LGPL
4571  *
4572  * row
4573  * 
4574  */
4575
4576 /**
4577  * @class Roo.bootstrap.NavItem
4578  * @extends Roo.bootstrap.Component
4579  * Bootstrap Navbar.NavItem class
4580  * @cfg {String} href  link to
4581  * @cfg {String} html content of button
4582  * @cfg {String} badge text inside badge
4583  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4584  * @cfg {String} glyphicon DEPRICATED - use fa
4585  * @cfg {String} icon DEPRICATED - use fa
4586  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4587  * @cfg {Boolean} active Is item active
4588  * @cfg {Boolean} disabled Is item disabled
4589  
4590  * @cfg {Boolean} preventDefault (true | false) default false
4591  * @cfg {String} tabId the tab that this item activates.
4592  * @cfg {String} tagtype (a|span) render as a href or span?
4593  * @cfg {Boolean} animateRef (true|false) link to element default false  
4594   
4595  * @constructor
4596  * Create a new Navbar Item
4597  * @param {Object} config The config object
4598  */
4599 Roo.bootstrap.NavItem = function(config){
4600     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4601     this.addEvents({
4602         // raw events
4603         /**
4604          * @event click
4605          * The raw click event for the entire grid.
4606          * @param {Roo.EventObject} e
4607          */
4608         "click" : true,
4609          /**
4610             * @event changed
4611             * Fires when the active item active state changes
4612             * @param {Roo.bootstrap.NavItem} this
4613             * @param {boolean} state the new state
4614              
4615          */
4616         'changed': true,
4617         /**
4618             * @event scrollto
4619             * Fires when scroll to element
4620             * @param {Roo.bootstrap.NavItem} this
4621             * @param {Object} options
4622             * @param {Roo.EventObject} e
4623              
4624          */
4625         'scrollto': true
4626     });
4627    
4628 };
4629
4630 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4631     
4632     href: false,
4633     html: '',
4634     badge: '',
4635     icon: false,
4636     fa : false,
4637     glyphicon: false,
4638     active: false,
4639     preventDefault : false,
4640     tabId : false,
4641     tagtype : 'a',
4642     disabled : false,
4643     animateRef : false,
4644     was_active : false,
4645     
4646     getAutoCreate : function(){
4647          
4648         var cfg = {
4649             tag: 'li',
4650             cls: 'nav-item'
4651             
4652         };
4653         
4654         if (this.active) {
4655             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4656         }
4657         if (this.disabled) {
4658             cfg.cls += ' disabled';
4659         }
4660         
4661         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4662             cfg.cn = [
4663                 {
4664                     tag: this.tagtype,
4665                     href : this.href || "#",
4666                     html: this.html || ''
4667                 }
4668             ];
4669             if (this.tagtype == 'a') {
4670                 cfg.cn[0].cls = 'nav-link';
4671             }
4672             if (this.icon) {
4673                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4674             }
4675             if (this.fa) {
4676                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4677             }
4678             if(this.glyphicon) {
4679                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4680             }
4681             
4682             if (this.menu) {
4683                 
4684                 cfg.cn[0].html += " <span class='caret'></span>";
4685              
4686             }
4687             
4688             if (this.badge !== '') {
4689                  
4690                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4691             }
4692         }
4693         
4694         
4695         
4696         return cfg;
4697     },
4698     initEvents: function() 
4699     {
4700         if (typeof (this.menu) != 'undefined') {
4701             this.menu.parentType = this.xtype;
4702             this.menu.triggerEl = this.el;
4703             this.menu = this.addxtype(Roo.apply({}, this.menu));
4704         }
4705         
4706         this.el.select('a',true).on('click', this.onClick, this);
4707         
4708         if(this.tagtype == 'span'){
4709             this.el.select('span',true).on('click', this.onClick, this);
4710         }
4711        
4712         // at this point parent should be available..
4713         this.parent().register(this);
4714     },
4715     
4716     onClick : function(e)
4717     {
4718         if (e.getTarget('.dropdown-menu-item')) {
4719             // did you click on a menu itemm.... - then don't trigger onclick..
4720             return;
4721         }
4722         
4723         if(
4724                 this.preventDefault || 
4725                 this.href == '#' 
4726         ){
4727             Roo.log("NavItem - prevent Default?");
4728             e.preventDefault();
4729         }
4730         
4731         if (this.disabled) {
4732             return;
4733         }
4734         
4735         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4736         if (tg && tg.transition) {
4737             Roo.log("waiting for the transitionend");
4738             return;
4739         }
4740         
4741         
4742         
4743         //Roo.log("fire event clicked");
4744         if(this.fireEvent('click', this, e) === false){
4745             return;
4746         };
4747         
4748         if(this.tagtype == 'span'){
4749             return;
4750         }
4751         
4752         //Roo.log(this.href);
4753         var ael = this.el.select('a',true).first();
4754         //Roo.log(ael);
4755         
4756         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4757             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4758             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4759                 return; // ignore... - it's a 'hash' to another page.
4760             }
4761             Roo.log("NavItem - prevent Default?");
4762             e.preventDefault();
4763             this.scrollToElement(e);
4764         }
4765         
4766         
4767         var p =  this.parent();
4768    
4769         if (['tabs','pills'].indexOf(p.type)!==-1) {
4770             if (typeof(p.setActiveItem) !== 'undefined') {
4771                 p.setActiveItem(this);
4772             }
4773         }
4774         
4775         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4776         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4777             // remove the collapsed menu expand...
4778             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4779         }
4780     },
4781     
4782     isActive: function () {
4783         return this.active
4784     },
4785     setActive : function(state, fire, is_was_active)
4786     {
4787         if (this.active && !state && this.navId) {
4788             this.was_active = true;
4789             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4790             if (nv) {
4791                 nv.clearWasActive(this);
4792             }
4793             
4794         }
4795         this.active = state;
4796         
4797         if (!state ) {
4798             this.el.removeClass('active');
4799         } else if (!this.el.hasClass('active')) {
4800             this.el.addClass('active');
4801         }
4802         if (fire) {
4803             this.fireEvent('changed', this, state);
4804         }
4805         
4806         // show a panel if it's registered and related..
4807         
4808         if (!this.navId || !this.tabId || !state || is_was_active) {
4809             return;
4810         }
4811         
4812         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4813         if (!tg) {
4814             return;
4815         }
4816         var pan = tg.getPanelByName(this.tabId);
4817         if (!pan) {
4818             return;
4819         }
4820         // if we can not flip to new panel - go back to old nav highlight..
4821         if (false == tg.showPanel(pan)) {
4822             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4823             if (nv) {
4824                 var onav = nv.getWasActive();
4825                 if (onav) {
4826                     onav.setActive(true, false, true);
4827                 }
4828             }
4829             
4830         }
4831         
4832         
4833         
4834     },
4835      // this should not be here...
4836     setDisabled : function(state)
4837     {
4838         this.disabled = state;
4839         if (!state ) {
4840             this.el.removeClass('disabled');
4841         } else if (!this.el.hasClass('disabled')) {
4842             this.el.addClass('disabled');
4843         }
4844         
4845     },
4846     
4847     /**
4848      * Fetch the element to display the tooltip on.
4849      * @return {Roo.Element} defaults to this.el
4850      */
4851     tooltipEl : function()
4852     {
4853         return this.el.select('' + this.tagtype + '', true).first();
4854     },
4855     
4856     scrollToElement : function(e)
4857     {
4858         var c = document.body;
4859         
4860         /*
4861          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4862          */
4863         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4864             c = document.documentElement;
4865         }
4866         
4867         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4868         
4869         if(!target){
4870             return;
4871         }
4872
4873         var o = target.calcOffsetsTo(c);
4874         
4875         var options = {
4876             target : target,
4877             value : o[1]
4878         };
4879         
4880         this.fireEvent('scrollto', this, options, e);
4881         
4882         Roo.get(c).scrollTo('top', options.value, true);
4883         
4884         return;
4885     }
4886 });
4887  
4888
4889  /*
4890  * - LGPL
4891  *
4892  * sidebar item
4893  *
4894  *  li
4895  *    <span> icon </span>
4896  *    <span> text </span>
4897  *    <span>badge </span>
4898  */
4899
4900 /**
4901  * @class Roo.bootstrap.NavSidebarItem
4902  * @extends Roo.bootstrap.NavItem
4903  * Bootstrap Navbar.NavSidebarItem class
4904  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4905  * {Boolean} open is the menu open
4906  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4907  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4908  * {String} buttonSize (sm|md|lg)the extra classes for the button
4909  * {Boolean} showArrow show arrow next to the text (default true)
4910  * @constructor
4911  * Create a new Navbar Button
4912  * @param {Object} config The config object
4913  */
4914 Roo.bootstrap.NavSidebarItem = function(config){
4915     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4916     this.addEvents({
4917         // raw events
4918         /**
4919          * @event click
4920          * The raw click event for the entire grid.
4921          * @param {Roo.EventObject} e
4922          */
4923         "click" : true,
4924          /**
4925             * @event changed
4926             * Fires when the active item active state changes
4927             * @param {Roo.bootstrap.NavSidebarItem} this
4928             * @param {boolean} state the new state
4929              
4930          */
4931         'changed': true
4932     });
4933    
4934 };
4935
4936 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4937     
4938     badgeWeight : 'default',
4939     
4940     open: false,
4941     
4942     buttonView : false,
4943     
4944     buttonWeight : 'default',
4945     
4946     buttonSize : 'md',
4947     
4948     showArrow : true,
4949     
4950     getAutoCreate : function(){
4951         
4952         
4953         var a = {
4954                 tag: 'a',
4955                 href : this.href || '#',
4956                 cls: '',
4957                 html : '',
4958                 cn : []
4959         };
4960         
4961         if(this.buttonView){
4962             a = {
4963                 tag: 'button',
4964                 href : this.href || '#',
4965                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4966                 html : this.html,
4967                 cn : []
4968             };
4969         }
4970         
4971         var cfg = {
4972             tag: 'li',
4973             cls: '',
4974             cn: [ a ]
4975         };
4976         
4977         if (this.active) {
4978             cfg.cls += ' active';
4979         }
4980         
4981         if (this.disabled) {
4982             cfg.cls += ' disabled';
4983         }
4984         if (this.open) {
4985             cfg.cls += ' open x-open';
4986         }
4987         // left icon..
4988         if (this.glyphicon || this.icon) {
4989             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4990             a.cn.push({ tag : 'i', cls : c }) ;
4991         }
4992         
4993         if(!this.buttonView){
4994             var span = {
4995                 tag: 'span',
4996                 html : this.html || ''
4997             };
4998
4999             a.cn.push(span);
5000             
5001         }
5002         
5003         if (this.badge !== '') {
5004             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5005         }
5006         
5007         if (this.menu) {
5008             
5009             if(this.showArrow){
5010                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5011             }
5012             
5013             a.cls += ' dropdown-toggle treeview' ;
5014         }
5015         
5016         return cfg;
5017     },
5018     
5019     initEvents : function()
5020     { 
5021         if (typeof (this.menu) != 'undefined') {
5022             this.menu.parentType = this.xtype;
5023             this.menu.triggerEl = this.el;
5024             this.menu = this.addxtype(Roo.apply({}, this.menu));
5025         }
5026         
5027         this.el.on('click', this.onClick, this);
5028         
5029         if(this.badge !== ''){
5030             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5031         }
5032         
5033     },
5034     
5035     onClick : function(e)
5036     {
5037         if(this.disabled){
5038             e.preventDefault();
5039             return;
5040         }
5041         
5042         if(this.preventDefault){
5043             e.preventDefault();
5044         }
5045         
5046         this.fireEvent('click', this);
5047     },
5048     
5049     disable : function()
5050     {
5051         this.setDisabled(true);
5052     },
5053     
5054     enable : function()
5055     {
5056         this.setDisabled(false);
5057     },
5058     
5059     setDisabled : function(state)
5060     {
5061         if(this.disabled == state){
5062             return;
5063         }
5064         
5065         this.disabled = state;
5066         
5067         if (state) {
5068             this.el.addClass('disabled');
5069             return;
5070         }
5071         
5072         this.el.removeClass('disabled');
5073         
5074         return;
5075     },
5076     
5077     setActive : function(state)
5078     {
5079         if(this.active == state){
5080             return;
5081         }
5082         
5083         this.active = state;
5084         
5085         if (state) {
5086             this.el.addClass('active');
5087             return;
5088         }
5089         
5090         this.el.removeClass('active');
5091         
5092         return;
5093     },
5094     
5095     isActive: function () 
5096     {
5097         return this.active;
5098     },
5099     
5100     setBadge : function(str)
5101     {
5102         if(!this.badgeEl){
5103             return;
5104         }
5105         
5106         this.badgeEl.dom.innerHTML = str;
5107     }
5108     
5109    
5110      
5111  
5112 });
5113  
5114
5115  /*
5116  * - LGPL
5117  *
5118  * row
5119  * 
5120  */
5121
5122 /**
5123  * @class Roo.bootstrap.Row
5124  * @extends Roo.bootstrap.Component
5125  * Bootstrap Row class (contains columns...)
5126  * 
5127  * @constructor
5128  * Create a new Row
5129  * @param {Object} config The config object
5130  */
5131
5132 Roo.bootstrap.Row = function(config){
5133     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5134 };
5135
5136 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5137     
5138     getAutoCreate : function(){
5139        return {
5140             cls: 'row clearfix'
5141        };
5142     }
5143     
5144     
5145 });
5146
5147  
5148
5149  /*
5150  * - LGPL
5151  *
5152  * element
5153  * 
5154  */
5155
5156 /**
5157  * @class Roo.bootstrap.Element
5158  * @extends Roo.bootstrap.Component
5159  * Bootstrap Element class
5160  * @cfg {String} html contents of the element
5161  * @cfg {String} tag tag of the element
5162  * @cfg {String} cls class of the element
5163  * @cfg {Boolean} preventDefault (true|false) default false
5164  * @cfg {Boolean} clickable (true|false) default false
5165  * 
5166  * @constructor
5167  * Create a new Element
5168  * @param {Object} config The config object
5169  */
5170
5171 Roo.bootstrap.Element = function(config){
5172     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5173     
5174     this.addEvents({
5175         // raw events
5176         /**
5177          * @event click
5178          * When a element is chick
5179          * @param {Roo.bootstrap.Element} this
5180          * @param {Roo.EventObject} e
5181          */
5182         "click" : true
5183     });
5184 };
5185
5186 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5187     
5188     tag: 'div',
5189     cls: '',
5190     html: '',
5191     preventDefault: false, 
5192     clickable: false,
5193     
5194     getAutoCreate : function(){
5195         
5196         var cfg = {
5197             tag: this.tag,
5198             // cls: this.cls, double assign in parent class Component.js :: onRender
5199             html: this.html
5200         };
5201         
5202         return cfg;
5203     },
5204     
5205     initEvents: function() 
5206     {
5207         Roo.bootstrap.Element.superclass.initEvents.call(this);
5208         
5209         if(this.clickable){
5210             this.el.on('click', this.onClick, this);
5211         }
5212         
5213     },
5214     
5215     onClick : function(e)
5216     {
5217         if(this.preventDefault){
5218             e.preventDefault();
5219         }
5220         
5221         this.fireEvent('click', this, e);
5222     },
5223     
5224     getValue : function()
5225     {
5226         return this.el.dom.innerHTML;
5227     },
5228     
5229     setValue : function(value)
5230     {
5231         this.el.dom.innerHTML = value;
5232     }
5233    
5234 });
5235
5236  
5237
5238  /*
5239  * - LGPL
5240  *
5241  * pagination
5242  * 
5243  */
5244
5245 /**
5246  * @class Roo.bootstrap.Pagination
5247  * @extends Roo.bootstrap.Component
5248  * Bootstrap Pagination class
5249  * @cfg {String} size xs | sm | md | lg
5250  * @cfg {Boolean} inverse false | true
5251  * 
5252  * @constructor
5253  * Create a new Pagination
5254  * @param {Object} config The config object
5255  */
5256
5257 Roo.bootstrap.Pagination = function(config){
5258     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5259 };
5260
5261 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5262     
5263     cls: false,
5264     size: false,
5265     inverse: false,
5266     
5267     getAutoCreate : function(){
5268         var cfg = {
5269             tag: 'ul',
5270                 cls: 'pagination'
5271         };
5272         if (this.inverse) {
5273             cfg.cls += ' inverse';
5274         }
5275         if (this.html) {
5276             cfg.html=this.html;
5277         }
5278         if (this.cls) {
5279             cfg.cls += " " + this.cls;
5280         }
5281         return cfg;
5282     }
5283    
5284 });
5285
5286  
5287
5288  /*
5289  * - LGPL
5290  *
5291  * Pagination item
5292  * 
5293  */
5294
5295
5296 /**
5297  * @class Roo.bootstrap.PaginationItem
5298  * @extends Roo.bootstrap.Component
5299  * Bootstrap PaginationItem class
5300  * @cfg {String} html text
5301  * @cfg {String} href the link
5302  * @cfg {Boolean} preventDefault (true | false) default true
5303  * @cfg {Boolean} active (true | false) default false
5304  * @cfg {Boolean} disabled default false
5305  * 
5306  * 
5307  * @constructor
5308  * Create a new PaginationItem
5309  * @param {Object} config The config object
5310  */
5311
5312
5313 Roo.bootstrap.PaginationItem = function(config){
5314     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5315     this.addEvents({
5316         // raw events
5317         /**
5318          * @event click
5319          * The raw click event for the entire grid.
5320          * @param {Roo.EventObject} e
5321          */
5322         "click" : true
5323     });
5324 };
5325
5326 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5327     
5328     href : false,
5329     html : false,
5330     preventDefault: true,
5331     active : false,
5332     cls : false,
5333     disabled: false,
5334     
5335     getAutoCreate : function(){
5336         var cfg= {
5337             tag: 'li',
5338             cn: [
5339                 {
5340                     tag : 'a',
5341                     href : this.href ? this.href : '#',
5342                     html : this.html ? this.html : ''
5343                 }
5344             ]
5345         };
5346         
5347         if(this.cls){
5348             cfg.cls = this.cls;
5349         }
5350         
5351         if(this.disabled){
5352             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5353         }
5354         
5355         if(this.active){
5356             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5357         }
5358         
5359         return cfg;
5360     },
5361     
5362     initEvents: function() {
5363         
5364         this.el.on('click', this.onClick, this);
5365         
5366     },
5367     onClick : function(e)
5368     {
5369         Roo.log('PaginationItem on click ');
5370         if(this.preventDefault){
5371             e.preventDefault();
5372         }
5373         
5374         if(this.disabled){
5375             return;
5376         }
5377         
5378         this.fireEvent('click', this, e);
5379     }
5380    
5381 });
5382
5383  
5384
5385  /*
5386  * - LGPL
5387  *
5388  * slider
5389  * 
5390  */
5391
5392
5393 /**
5394  * @class Roo.bootstrap.Slider
5395  * @extends Roo.bootstrap.Component
5396  * Bootstrap Slider class
5397  *    
5398  * @constructor
5399  * Create a new Slider
5400  * @param {Object} config The config object
5401  */
5402
5403 Roo.bootstrap.Slider = function(config){
5404     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5405 };
5406
5407 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5408     
5409     getAutoCreate : function(){
5410         
5411         var cfg = {
5412             tag: 'div',
5413             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5414             cn: [
5415                 {
5416                     tag: 'a',
5417                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5418                 }
5419             ]
5420         };
5421         
5422         return cfg;
5423     }
5424    
5425 });
5426
5427  /*
5428  * Based on:
5429  * Ext JS Library 1.1.1
5430  * Copyright(c) 2006-2007, Ext JS, LLC.
5431  *
5432  * Originally Released Under LGPL - original licence link has changed is not relivant.
5433  *
5434  * Fork - LGPL
5435  * <script type="text/javascript">
5436  */
5437  
5438
5439 /**
5440  * @class Roo.grid.ColumnModel
5441  * @extends Roo.util.Observable
5442  * This is the default implementation of a ColumnModel used by the Grid. It defines
5443  * the columns in the grid.
5444  * <br>Usage:<br>
5445  <pre><code>
5446  var colModel = new Roo.grid.ColumnModel([
5447         {header: "Ticker", width: 60, sortable: true, locked: true},
5448         {header: "Company Name", width: 150, sortable: true},
5449         {header: "Market Cap.", width: 100, sortable: true},
5450         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5451         {header: "Employees", width: 100, sortable: true, resizable: false}
5452  ]);
5453  </code></pre>
5454  * <p>
5455  
5456  * The config options listed for this class are options which may appear in each
5457  * individual column definition.
5458  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5459  * @constructor
5460  * @param {Object} config An Array of column config objects. See this class's
5461  * config objects for details.
5462 */
5463 Roo.grid.ColumnModel = function(config){
5464         /**
5465      * The config passed into the constructor
5466      */
5467     this.config = config;
5468     this.lookup = {};
5469
5470     // if no id, create one
5471     // if the column does not have a dataIndex mapping,
5472     // map it to the order it is in the config
5473     for(var i = 0, len = config.length; i < len; i++){
5474         var c = config[i];
5475         if(typeof c.dataIndex == "undefined"){
5476             c.dataIndex = i;
5477         }
5478         if(typeof c.renderer == "string"){
5479             c.renderer = Roo.util.Format[c.renderer];
5480         }
5481         if(typeof c.id == "undefined"){
5482             c.id = Roo.id();
5483         }
5484         if(c.editor && c.editor.xtype){
5485             c.editor  = Roo.factory(c.editor, Roo.grid);
5486         }
5487         if(c.editor && c.editor.isFormField){
5488             c.editor = new Roo.grid.GridEditor(c.editor);
5489         }
5490         this.lookup[c.id] = c;
5491     }
5492
5493     /**
5494      * The width of columns which have no width specified (defaults to 100)
5495      * @type Number
5496      */
5497     this.defaultWidth = 100;
5498
5499     /**
5500      * Default sortable of columns which have no sortable specified (defaults to false)
5501      * @type Boolean
5502      */
5503     this.defaultSortable = false;
5504
5505     this.addEvents({
5506         /**
5507              * @event widthchange
5508              * Fires when the width of a column changes.
5509              * @param {ColumnModel} this
5510              * @param {Number} columnIndex The column index
5511              * @param {Number} newWidth The new width
5512              */
5513             "widthchange": true,
5514         /**
5515              * @event headerchange
5516              * Fires when the text of a header changes.
5517              * @param {ColumnModel} this
5518              * @param {Number} columnIndex The column index
5519              * @param {Number} newText The new header text
5520              */
5521             "headerchange": true,
5522         /**
5523              * @event hiddenchange
5524              * Fires when a column is hidden or "unhidden".
5525              * @param {ColumnModel} this
5526              * @param {Number} columnIndex The column index
5527              * @param {Boolean} hidden true if hidden, false otherwise
5528              */
5529             "hiddenchange": true,
5530             /**
5531          * @event columnmoved
5532          * Fires when a column is moved.
5533          * @param {ColumnModel} this
5534          * @param {Number} oldIndex
5535          * @param {Number} newIndex
5536          */
5537         "columnmoved" : true,
5538         /**
5539          * @event columlockchange
5540          * Fires when a column's locked state is changed
5541          * @param {ColumnModel} this
5542          * @param {Number} colIndex
5543          * @param {Boolean} locked true if locked
5544          */
5545         "columnlockchange" : true
5546     });
5547     Roo.grid.ColumnModel.superclass.constructor.call(this);
5548 };
5549 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5550     /**
5551      * @cfg {String} header The header text to display in the Grid view.
5552      */
5553     /**
5554      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5555      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5556      * specified, the column's index is used as an index into the Record's data Array.
5557      */
5558     /**
5559      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5560      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5561      */
5562     /**
5563      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5564      * Defaults to the value of the {@link #defaultSortable} property.
5565      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5566      */
5567     /**
5568      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5569      */
5570     /**
5571      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5572      */
5573     /**
5574      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5575      */
5576     /**
5577      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5578      */
5579     /**
5580      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5581      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5582      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5583      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5584      */
5585        /**
5586      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5587      */
5588     /**
5589      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5590      */
5591     /**
5592      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5593      */
5594     /**
5595      * @cfg {String} cursor (Optional)
5596      */
5597     /**
5598      * @cfg {String} tooltip (Optional)
5599      */
5600     /**
5601      * @cfg {Number} xs (Optional)
5602      */
5603     /**
5604      * @cfg {Number} sm (Optional)
5605      */
5606     /**
5607      * @cfg {Number} md (Optional)
5608      */
5609     /**
5610      * @cfg {Number} lg (Optional)
5611      */
5612     /**
5613      * Returns the id of the column at the specified index.
5614      * @param {Number} index The column index
5615      * @return {String} the id
5616      */
5617     getColumnId : function(index){
5618         return this.config[index].id;
5619     },
5620
5621     /**
5622      * Returns the column for a specified id.
5623      * @param {String} id The column id
5624      * @return {Object} the column
5625      */
5626     getColumnById : function(id){
5627         return this.lookup[id];
5628     },
5629
5630     
5631     /**
5632      * Returns the column for a specified dataIndex.
5633      * @param {String} dataIndex The column dataIndex
5634      * @return {Object|Boolean} the column or false if not found
5635      */
5636     getColumnByDataIndex: function(dataIndex){
5637         var index = this.findColumnIndex(dataIndex);
5638         return index > -1 ? this.config[index] : false;
5639     },
5640     
5641     /**
5642      * Returns the index for a specified column id.
5643      * @param {String} id The column id
5644      * @return {Number} the index, or -1 if not found
5645      */
5646     getIndexById : function(id){
5647         for(var i = 0, len = this.config.length; i < len; i++){
5648             if(this.config[i].id == id){
5649                 return i;
5650             }
5651         }
5652         return -1;
5653     },
5654     
5655     /**
5656      * Returns the index for a specified column dataIndex.
5657      * @param {String} dataIndex The column dataIndex
5658      * @return {Number} the index, or -1 if not found
5659      */
5660     
5661     findColumnIndex : function(dataIndex){
5662         for(var i = 0, len = this.config.length; i < len; i++){
5663             if(this.config[i].dataIndex == dataIndex){
5664                 return i;
5665             }
5666         }
5667         return -1;
5668     },
5669     
5670     
5671     moveColumn : function(oldIndex, newIndex){
5672         var c = this.config[oldIndex];
5673         this.config.splice(oldIndex, 1);
5674         this.config.splice(newIndex, 0, c);
5675         this.dataMap = null;
5676         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5677     },
5678
5679     isLocked : function(colIndex){
5680         return this.config[colIndex].locked === true;
5681     },
5682
5683     setLocked : function(colIndex, value, suppressEvent){
5684         if(this.isLocked(colIndex) == value){
5685             return;
5686         }
5687         this.config[colIndex].locked = value;
5688         if(!suppressEvent){
5689             this.fireEvent("columnlockchange", this, colIndex, value);
5690         }
5691     },
5692
5693     getTotalLockedWidth : function(){
5694         var totalWidth = 0;
5695         for(var i = 0; i < this.config.length; i++){
5696             if(this.isLocked(i) && !this.isHidden(i)){
5697                 this.totalWidth += this.getColumnWidth(i);
5698             }
5699         }
5700         return totalWidth;
5701     },
5702
5703     getLockedCount : function(){
5704         for(var i = 0, len = this.config.length; i < len; i++){
5705             if(!this.isLocked(i)){
5706                 return i;
5707             }
5708         }
5709         
5710         return this.config.length;
5711     },
5712
5713     /**
5714      * Returns the number of columns.
5715      * @return {Number}
5716      */
5717     getColumnCount : function(visibleOnly){
5718         if(visibleOnly === true){
5719             var c = 0;
5720             for(var i = 0, len = this.config.length; i < len; i++){
5721                 if(!this.isHidden(i)){
5722                     c++;
5723                 }
5724             }
5725             return c;
5726         }
5727         return this.config.length;
5728     },
5729
5730     /**
5731      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5732      * @param {Function} fn
5733      * @param {Object} scope (optional)
5734      * @return {Array} result
5735      */
5736     getColumnsBy : function(fn, scope){
5737         var r = [];
5738         for(var i = 0, len = this.config.length; i < len; i++){
5739             var c = this.config[i];
5740             if(fn.call(scope||this, c, i) === true){
5741                 r[r.length] = c;
5742             }
5743         }
5744         return r;
5745     },
5746
5747     /**
5748      * Returns true if the specified column is sortable.
5749      * @param {Number} col The column index
5750      * @return {Boolean}
5751      */
5752     isSortable : function(col){
5753         if(typeof this.config[col].sortable == "undefined"){
5754             return this.defaultSortable;
5755         }
5756         return this.config[col].sortable;
5757     },
5758
5759     /**
5760      * Returns the rendering (formatting) function defined for the column.
5761      * @param {Number} col The column index.
5762      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5763      */
5764     getRenderer : function(col){
5765         if(!this.config[col].renderer){
5766             return Roo.grid.ColumnModel.defaultRenderer;
5767         }
5768         return this.config[col].renderer;
5769     },
5770
5771     /**
5772      * Sets the rendering (formatting) function for a column.
5773      * @param {Number} col The column index
5774      * @param {Function} fn The function to use to process the cell's raw data
5775      * to return HTML markup for the grid view. The render function is called with
5776      * the following parameters:<ul>
5777      * <li>Data value.</li>
5778      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5779      * <li>css A CSS style string to apply to the table cell.</li>
5780      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5781      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5782      * <li>Row index</li>
5783      * <li>Column index</li>
5784      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5785      */
5786     setRenderer : function(col, fn){
5787         this.config[col].renderer = fn;
5788     },
5789
5790     /**
5791      * Returns the width for the specified column.
5792      * @param {Number} col The column index
5793      * @return {Number}
5794      */
5795     getColumnWidth : function(col){
5796         return this.config[col].width * 1 || this.defaultWidth;
5797     },
5798
5799     /**
5800      * Sets the width for a column.
5801      * @param {Number} col The column index
5802      * @param {Number} width The new width
5803      */
5804     setColumnWidth : function(col, width, suppressEvent){
5805         this.config[col].width = width;
5806         this.totalWidth = null;
5807         if(!suppressEvent){
5808              this.fireEvent("widthchange", this, col, width);
5809         }
5810     },
5811
5812     /**
5813      * Returns the total width of all columns.
5814      * @param {Boolean} includeHidden True to include hidden column widths
5815      * @return {Number}
5816      */
5817     getTotalWidth : function(includeHidden){
5818         if(!this.totalWidth){
5819             this.totalWidth = 0;
5820             for(var i = 0, len = this.config.length; i < len; i++){
5821                 if(includeHidden || !this.isHidden(i)){
5822                     this.totalWidth += this.getColumnWidth(i);
5823                 }
5824             }
5825         }
5826         return this.totalWidth;
5827     },
5828
5829     /**
5830      * Returns the header for the specified column.
5831      * @param {Number} col The column index
5832      * @return {String}
5833      */
5834     getColumnHeader : function(col){
5835         return this.config[col].header;
5836     },
5837
5838     /**
5839      * Sets the header for a column.
5840      * @param {Number} col The column index
5841      * @param {String} header The new header
5842      */
5843     setColumnHeader : function(col, header){
5844         this.config[col].header = header;
5845         this.fireEvent("headerchange", this, col, header);
5846     },
5847
5848     /**
5849      * Returns the tooltip for the specified column.
5850      * @param {Number} col The column index
5851      * @return {String}
5852      */
5853     getColumnTooltip : function(col){
5854             return this.config[col].tooltip;
5855     },
5856     /**
5857      * Sets the tooltip for a column.
5858      * @param {Number} col The column index
5859      * @param {String} tooltip The new tooltip
5860      */
5861     setColumnTooltip : function(col, tooltip){
5862             this.config[col].tooltip = tooltip;
5863     },
5864
5865     /**
5866      * Returns the dataIndex for the specified column.
5867      * @param {Number} col The column index
5868      * @return {Number}
5869      */
5870     getDataIndex : function(col){
5871         return this.config[col].dataIndex;
5872     },
5873
5874     /**
5875      * Sets the dataIndex for a column.
5876      * @param {Number} col The column index
5877      * @param {Number} dataIndex The new dataIndex
5878      */
5879     setDataIndex : function(col, dataIndex){
5880         this.config[col].dataIndex = dataIndex;
5881     },
5882
5883     
5884     
5885     /**
5886      * Returns true if the cell is editable.
5887      * @param {Number} colIndex The column index
5888      * @param {Number} rowIndex The row index - this is nto actually used..?
5889      * @return {Boolean}
5890      */
5891     isCellEditable : function(colIndex, rowIndex){
5892         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5893     },
5894
5895     /**
5896      * Returns the editor defined for the cell/column.
5897      * return false or null to disable editing.
5898      * @param {Number} colIndex The column index
5899      * @param {Number} rowIndex The row index
5900      * @return {Object}
5901      */
5902     getCellEditor : function(colIndex, rowIndex){
5903         return this.config[colIndex].editor;
5904     },
5905
5906     /**
5907      * Sets if a column is editable.
5908      * @param {Number} col The column index
5909      * @param {Boolean} editable True if the column is editable
5910      */
5911     setEditable : function(col, editable){
5912         this.config[col].editable = editable;
5913     },
5914
5915
5916     /**
5917      * Returns true if the column is hidden.
5918      * @param {Number} colIndex The column index
5919      * @return {Boolean}
5920      */
5921     isHidden : function(colIndex){
5922         return this.config[colIndex].hidden;
5923     },
5924
5925
5926     /**
5927      * Returns true if the column width cannot be changed
5928      */
5929     isFixed : function(colIndex){
5930         return this.config[colIndex].fixed;
5931     },
5932
5933     /**
5934      * Returns true if the column can be resized
5935      * @return {Boolean}
5936      */
5937     isResizable : function(colIndex){
5938         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5939     },
5940     /**
5941      * Sets if a column is hidden.
5942      * @param {Number} colIndex The column index
5943      * @param {Boolean} hidden True if the column is hidden
5944      */
5945     setHidden : function(colIndex, hidden){
5946         this.config[colIndex].hidden = hidden;
5947         this.totalWidth = null;
5948         this.fireEvent("hiddenchange", this, colIndex, hidden);
5949     },
5950
5951     /**
5952      * Sets the editor for a column.
5953      * @param {Number} col The column index
5954      * @param {Object} editor The editor object
5955      */
5956     setEditor : function(col, editor){
5957         this.config[col].editor = editor;
5958     }
5959 });
5960
5961 Roo.grid.ColumnModel.defaultRenderer = function(value)
5962 {
5963     if(typeof value == "object") {
5964         return value;
5965     }
5966         if(typeof value == "string" && value.length < 1){
5967             return "&#160;";
5968         }
5969     
5970         return String.format("{0}", value);
5971 };
5972
5973 // Alias for backwards compatibility
5974 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5975 /*
5976  * Based on:
5977  * Ext JS Library 1.1.1
5978  * Copyright(c) 2006-2007, Ext JS, LLC.
5979  *
5980  * Originally Released Under LGPL - original licence link has changed is not relivant.
5981  *
5982  * Fork - LGPL
5983  * <script type="text/javascript">
5984  */
5985  
5986 /**
5987  * @class Roo.LoadMask
5988  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5989  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5990  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5991  * element's UpdateManager load indicator and will be destroyed after the initial load.
5992  * @constructor
5993  * Create a new LoadMask
5994  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5995  * @param {Object} config The config object
5996  */
5997 Roo.LoadMask = function(el, config){
5998     this.el = Roo.get(el);
5999     Roo.apply(this, config);
6000     if(this.store){
6001         this.store.on('beforeload', this.onBeforeLoad, this);
6002         this.store.on('load', this.onLoad, this);
6003         this.store.on('loadexception', this.onLoadException, this);
6004         this.removeMask = false;
6005     }else{
6006         var um = this.el.getUpdateManager();
6007         um.showLoadIndicator = false; // disable the default indicator
6008         um.on('beforeupdate', this.onBeforeLoad, this);
6009         um.on('update', this.onLoad, this);
6010         um.on('failure', this.onLoad, this);
6011         this.removeMask = true;
6012     }
6013 };
6014
6015 Roo.LoadMask.prototype = {
6016     /**
6017      * @cfg {Boolean} removeMask
6018      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6019      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6020      */
6021     /**
6022      * @cfg {String} msg
6023      * The text to display in a centered loading message box (defaults to 'Loading...')
6024      */
6025     msg : 'Loading...',
6026     /**
6027      * @cfg {String} msgCls
6028      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6029      */
6030     msgCls : 'x-mask-loading',
6031
6032     /**
6033      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6034      * @type Boolean
6035      */
6036     disabled: false,
6037
6038     /**
6039      * Disables the mask to prevent it from being displayed
6040      */
6041     disable : function(){
6042        this.disabled = true;
6043     },
6044
6045     /**
6046      * Enables the mask so that it can be displayed
6047      */
6048     enable : function(){
6049         this.disabled = false;
6050     },
6051     
6052     onLoadException : function()
6053     {
6054         Roo.log(arguments);
6055         
6056         if (typeof(arguments[3]) != 'undefined') {
6057             Roo.MessageBox.alert("Error loading",arguments[3]);
6058         } 
6059         /*
6060         try {
6061             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6062                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6063             }   
6064         } catch(e) {
6065             
6066         }
6067         */
6068     
6069         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6070     },
6071     // private
6072     onLoad : function()
6073     {
6074         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6075     },
6076
6077     // private
6078     onBeforeLoad : function(){
6079         if(!this.disabled){
6080             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6081         }
6082     },
6083
6084     // private
6085     destroy : function(){
6086         if(this.store){
6087             this.store.un('beforeload', this.onBeforeLoad, this);
6088             this.store.un('load', this.onLoad, this);
6089             this.store.un('loadexception', this.onLoadException, this);
6090         }else{
6091             var um = this.el.getUpdateManager();
6092             um.un('beforeupdate', this.onBeforeLoad, this);
6093             um.un('update', this.onLoad, this);
6094             um.un('failure', this.onLoad, this);
6095         }
6096     }
6097 };/*
6098  * - LGPL
6099  *
6100  * table
6101  * 
6102  */
6103
6104 /**
6105  * @class Roo.bootstrap.Table
6106  * @extends Roo.bootstrap.Component
6107  * Bootstrap Table class
6108  * @cfg {String} cls table class
6109  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6110  * @cfg {String} bgcolor Specifies the background color for a table
6111  * @cfg {Number} border Specifies whether the table cells should have borders or not
6112  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6113  * @cfg {Number} cellspacing Specifies the space between cells
6114  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6115  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6116  * @cfg {String} sortable Specifies that the table should be sortable
6117  * @cfg {String} summary Specifies a summary of the content of a table
6118  * @cfg {Number} width Specifies the width of a table
6119  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6120  * 
6121  * @cfg {boolean} striped Should the rows be alternative striped
6122  * @cfg {boolean} bordered Add borders to the table
6123  * @cfg {boolean} hover Add hover highlighting
6124  * @cfg {boolean} condensed Format condensed
6125  * @cfg {boolean} responsive Format condensed
6126  * @cfg {Boolean} loadMask (true|false) default false
6127  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6128  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6129  * @cfg {Boolean} rowSelection (true|false) default false
6130  * @cfg {Boolean} cellSelection (true|false) default false
6131  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6132  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6133  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6134  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6135  
6136  * 
6137  * @constructor
6138  * Create a new Table
6139  * @param {Object} config The config object
6140  */
6141
6142 Roo.bootstrap.Table = function(config){
6143     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6144     
6145   
6146     
6147     // BC...
6148     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6149     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6150     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6151     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6152     
6153     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6154     if (this.sm) {
6155         this.sm.grid = this;
6156         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6157         this.sm = this.selModel;
6158         this.sm.xmodule = this.xmodule || false;
6159     }
6160     
6161     if (this.cm && typeof(this.cm.config) == 'undefined') {
6162         this.colModel = new Roo.grid.ColumnModel(this.cm);
6163         this.cm = this.colModel;
6164         this.cm.xmodule = this.xmodule || false;
6165     }
6166     if (this.store) {
6167         this.store= Roo.factory(this.store, Roo.data);
6168         this.ds = this.store;
6169         this.ds.xmodule = this.xmodule || false;
6170          
6171     }
6172     if (this.footer && this.store) {
6173         this.footer.dataSource = this.ds;
6174         this.footer = Roo.factory(this.footer);
6175     }
6176     
6177     /** @private */
6178     this.addEvents({
6179         /**
6180          * @event cellclick
6181          * Fires when a cell is clicked
6182          * @param {Roo.bootstrap.Table} this
6183          * @param {Roo.Element} el
6184          * @param {Number} rowIndex
6185          * @param {Number} columnIndex
6186          * @param {Roo.EventObject} e
6187          */
6188         "cellclick" : true,
6189         /**
6190          * @event celldblclick
6191          * Fires when a cell is double clicked
6192          * @param {Roo.bootstrap.Table} this
6193          * @param {Roo.Element} el
6194          * @param {Number} rowIndex
6195          * @param {Number} columnIndex
6196          * @param {Roo.EventObject} e
6197          */
6198         "celldblclick" : true,
6199         /**
6200          * @event rowclick
6201          * Fires when a row is clicked
6202          * @param {Roo.bootstrap.Table} this
6203          * @param {Roo.Element} el
6204          * @param {Number} rowIndex
6205          * @param {Roo.EventObject} e
6206          */
6207         "rowclick" : true,
6208         /**
6209          * @event rowdblclick
6210          * Fires when a row is double clicked
6211          * @param {Roo.bootstrap.Table} this
6212          * @param {Roo.Element} el
6213          * @param {Number} rowIndex
6214          * @param {Roo.EventObject} e
6215          */
6216         "rowdblclick" : true,
6217         /**
6218          * @event mouseover
6219          * Fires when a mouseover occur
6220          * @param {Roo.bootstrap.Table} this
6221          * @param {Roo.Element} el
6222          * @param {Number} rowIndex
6223          * @param {Number} columnIndex
6224          * @param {Roo.EventObject} e
6225          */
6226         "mouseover" : true,
6227         /**
6228          * @event mouseout
6229          * Fires when a mouseout occur
6230          * @param {Roo.bootstrap.Table} this
6231          * @param {Roo.Element} el
6232          * @param {Number} rowIndex
6233          * @param {Number} columnIndex
6234          * @param {Roo.EventObject} e
6235          */
6236         "mouseout" : true,
6237         /**
6238          * @event rowclass
6239          * Fires when a row is rendered, so you can change add a style to it.
6240          * @param {Roo.bootstrap.Table} this
6241          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6242          */
6243         'rowclass' : true,
6244           /**
6245          * @event rowsrendered
6246          * Fires when all the  rows have been rendered
6247          * @param {Roo.bootstrap.Table} this
6248          */
6249         'rowsrendered' : true,
6250         /**
6251          * @event contextmenu
6252          * The raw contextmenu event for the entire grid.
6253          * @param {Roo.EventObject} e
6254          */
6255         "contextmenu" : true,
6256         /**
6257          * @event rowcontextmenu
6258          * Fires when a row is right clicked
6259          * @param {Roo.bootstrap.Table} this
6260          * @param {Number} rowIndex
6261          * @param {Roo.EventObject} e
6262          */
6263         "rowcontextmenu" : true,
6264         /**
6265          * @event cellcontextmenu
6266          * Fires when a cell is right clicked
6267          * @param {Roo.bootstrap.Table} this
6268          * @param {Number} rowIndex
6269          * @param {Number} cellIndex
6270          * @param {Roo.EventObject} e
6271          */
6272          "cellcontextmenu" : true,
6273          /**
6274          * @event headercontextmenu
6275          * Fires when a header is right clicked
6276          * @param {Roo.bootstrap.Table} this
6277          * @param {Number} columnIndex
6278          * @param {Roo.EventObject} e
6279          */
6280         "headercontextmenu" : true
6281     });
6282 };
6283
6284 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6285     
6286     cls: false,
6287     align: false,
6288     bgcolor: false,
6289     border: false,
6290     cellpadding: false,
6291     cellspacing: false,
6292     frame: false,
6293     rules: false,
6294     sortable: false,
6295     summary: false,
6296     width: false,
6297     striped : false,
6298     scrollBody : false,
6299     bordered: false,
6300     hover:  false,
6301     condensed : false,
6302     responsive : false,
6303     sm : false,
6304     cm : false,
6305     store : false,
6306     loadMask : false,
6307     footerShow : true,
6308     headerShow : true,
6309   
6310     rowSelection : false,
6311     cellSelection : false,
6312     layout : false,
6313     
6314     // Roo.Element - the tbody
6315     mainBody: false,
6316     // Roo.Element - thead element
6317     mainHead: false,
6318     
6319     container: false, // used by gridpanel...
6320     
6321     lazyLoad : false,
6322     
6323     CSS : Roo.util.CSS,
6324     
6325     auto_hide_footer : false,
6326     
6327     getAutoCreate : function()
6328     {
6329         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6330         
6331         cfg = {
6332             tag: 'table',
6333             cls : 'table',
6334             cn : []
6335         };
6336         if (this.scrollBody) {
6337             cfg.cls += ' table-body-fixed';
6338         }    
6339         if (this.striped) {
6340             cfg.cls += ' table-striped';
6341         }
6342         
6343         if (this.hover) {
6344             cfg.cls += ' table-hover';
6345         }
6346         if (this.bordered) {
6347             cfg.cls += ' table-bordered';
6348         }
6349         if (this.condensed) {
6350             cfg.cls += ' table-condensed';
6351         }
6352         if (this.responsive) {
6353             cfg.cls += ' table-responsive';
6354         }
6355         
6356         if (this.cls) {
6357             cfg.cls+=  ' ' +this.cls;
6358         }
6359         
6360         // this lot should be simplifed...
6361         var _t = this;
6362         var cp = [
6363             'align',
6364             'bgcolor',
6365             'border',
6366             'cellpadding',
6367             'cellspacing',
6368             'frame',
6369             'rules',
6370             'sortable',
6371             'summary',
6372             'width'
6373         ].forEach(function(k) {
6374             if (_t[k]) {
6375                 cfg[k] = _t[k];
6376             }
6377         });
6378         
6379         
6380         if (this.layout) {
6381             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6382         }
6383         
6384         if(this.store || this.cm){
6385             if(this.headerShow){
6386                 cfg.cn.push(this.renderHeader());
6387             }
6388             
6389             cfg.cn.push(this.renderBody());
6390             
6391             if(this.footerShow){
6392                 cfg.cn.push(this.renderFooter());
6393             }
6394             // where does this come from?
6395             //cfg.cls+=  ' TableGrid';
6396         }
6397         
6398         return { cn : [ cfg ] };
6399     },
6400     
6401     initEvents : function()
6402     {   
6403         if(!this.store || !this.cm){
6404             return;
6405         }
6406         if (this.selModel) {
6407             this.selModel.initEvents();
6408         }
6409         
6410         
6411         //Roo.log('initEvents with ds!!!!');
6412         
6413         this.mainBody = this.el.select('tbody', true).first();
6414         this.mainHead = this.el.select('thead', true).first();
6415         this.mainFoot = this.el.select('tfoot', true).first();
6416         
6417         
6418         
6419         var _this = this;
6420         
6421         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6422             e.on('click', _this.sort, _this);
6423         });
6424         
6425         this.mainBody.on("click", this.onClick, this);
6426         this.mainBody.on("dblclick", this.onDblClick, this);
6427         
6428         // why is this done????? = it breaks dialogs??
6429         //this.parent().el.setStyle('position', 'relative');
6430         
6431         
6432         if (this.footer) {
6433             this.footer.parentId = this.id;
6434             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6435             
6436             if(this.lazyLoad){
6437                 this.el.select('tfoot tr td').first().addClass('hide');
6438             }
6439         } 
6440         
6441         if(this.loadMask) {
6442             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6443         }
6444         
6445         this.store.on('load', this.onLoad, this);
6446         this.store.on('beforeload', this.onBeforeLoad, this);
6447         this.store.on('update', this.onUpdate, this);
6448         this.store.on('add', this.onAdd, this);
6449         this.store.on("clear", this.clear, this);
6450         
6451         this.el.on("contextmenu", this.onContextMenu, this);
6452         
6453         this.mainBody.on('scroll', this.onBodyScroll, this);
6454         
6455         this.cm.on("headerchange", this.onHeaderChange, this);
6456         
6457         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6458         
6459     },
6460     
6461     onContextMenu : function(e, t)
6462     {
6463         this.processEvent("contextmenu", e);
6464     },
6465     
6466     processEvent : function(name, e)
6467     {
6468         if (name != 'touchstart' ) {
6469             this.fireEvent(name, e);    
6470         }
6471         
6472         var t = e.getTarget();
6473         
6474         var cell = Roo.get(t);
6475         
6476         if(!cell){
6477             return;
6478         }
6479         
6480         if(cell.findParent('tfoot', false, true)){
6481             return;
6482         }
6483         
6484         if(cell.findParent('thead', false, true)){
6485             
6486             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6487                 cell = Roo.get(t).findParent('th', false, true);
6488                 if (!cell) {
6489                     Roo.log("failed to find th in thead?");
6490                     Roo.log(e.getTarget());
6491                     return;
6492                 }
6493             }
6494             
6495             var cellIndex = cell.dom.cellIndex;
6496             
6497             var ename = name == 'touchstart' ? 'click' : name;
6498             this.fireEvent("header" + ename, this, cellIndex, e);
6499             
6500             return;
6501         }
6502         
6503         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6504             cell = Roo.get(t).findParent('td', false, true);
6505             if (!cell) {
6506                 Roo.log("failed to find th in tbody?");
6507                 Roo.log(e.getTarget());
6508                 return;
6509             }
6510         }
6511         
6512         var row = cell.findParent('tr', false, true);
6513         var cellIndex = cell.dom.cellIndex;
6514         var rowIndex = row.dom.rowIndex - 1;
6515         
6516         if(row !== false){
6517             
6518             this.fireEvent("row" + name, this, rowIndex, e);
6519             
6520             if(cell !== false){
6521             
6522                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6523             }
6524         }
6525         
6526     },
6527     
6528     onMouseover : function(e, el)
6529     {
6530         var cell = Roo.get(el);
6531         
6532         if(!cell){
6533             return;
6534         }
6535         
6536         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6537             cell = cell.findParent('td', false, true);
6538         }
6539         
6540         var row = cell.findParent('tr', false, true);
6541         var cellIndex = cell.dom.cellIndex;
6542         var rowIndex = row.dom.rowIndex - 1; // start from 0
6543         
6544         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6545         
6546     },
6547     
6548     onMouseout : function(e, el)
6549     {
6550         var cell = Roo.get(el);
6551         
6552         if(!cell){
6553             return;
6554         }
6555         
6556         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6557             cell = cell.findParent('td', false, true);
6558         }
6559         
6560         var row = cell.findParent('tr', false, true);
6561         var cellIndex = cell.dom.cellIndex;
6562         var rowIndex = row.dom.rowIndex - 1; // start from 0
6563         
6564         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6565         
6566     },
6567     
6568     onClick : function(e, el)
6569     {
6570         var cell = Roo.get(el);
6571         
6572         if(!cell || (!this.cellSelection && !this.rowSelection)){
6573             return;
6574         }
6575         
6576         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6577             cell = cell.findParent('td', false, true);
6578         }
6579         
6580         if(!cell || typeof(cell) == 'undefined'){
6581             return;
6582         }
6583         
6584         var row = cell.findParent('tr', false, true);
6585         
6586         if(!row || typeof(row) == 'undefined'){
6587             return;
6588         }
6589         
6590         var cellIndex = cell.dom.cellIndex;
6591         var rowIndex = this.getRowIndex(row);
6592         
6593         // why??? - should these not be based on SelectionModel?
6594         if(this.cellSelection){
6595             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6596         }
6597         
6598         if(this.rowSelection){
6599             this.fireEvent('rowclick', this, row, rowIndex, e);
6600         }
6601         
6602         
6603     },
6604         
6605     onDblClick : function(e,el)
6606     {
6607         var cell = Roo.get(el);
6608         
6609         if(!cell || (!this.cellSelection && !this.rowSelection)){
6610             return;
6611         }
6612         
6613         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6614             cell = cell.findParent('td', false, true);
6615         }
6616         
6617         if(!cell || typeof(cell) == 'undefined'){
6618             return;
6619         }
6620         
6621         var row = cell.findParent('tr', false, true);
6622         
6623         if(!row || typeof(row) == 'undefined'){
6624             return;
6625         }
6626         
6627         var cellIndex = cell.dom.cellIndex;
6628         var rowIndex = this.getRowIndex(row);
6629         
6630         if(this.cellSelection){
6631             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6632         }
6633         
6634         if(this.rowSelection){
6635             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6636         }
6637     },
6638     
6639     sort : function(e,el)
6640     {
6641         var col = Roo.get(el);
6642         
6643         if(!col.hasClass('sortable')){
6644             return;
6645         }
6646         
6647         var sort = col.attr('sort');
6648         var dir = 'ASC';
6649         
6650         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6651             dir = 'DESC';
6652         }
6653         
6654         this.store.sortInfo = {field : sort, direction : dir};
6655         
6656         if (this.footer) {
6657             Roo.log("calling footer first");
6658             this.footer.onClick('first');
6659         } else {
6660         
6661             this.store.load({ params : { start : 0 } });
6662         }
6663     },
6664     
6665     renderHeader : function()
6666     {
6667         var header = {
6668             tag: 'thead',
6669             cn : []
6670         };
6671         
6672         var cm = this.cm;
6673         this.totalWidth = 0;
6674         
6675         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6676             
6677             var config = cm.config[i];
6678             
6679             var c = {
6680                 tag: 'th',
6681                 cls : 'x-hcol-' + i,
6682                 style : '',
6683                 html: cm.getColumnHeader(i)
6684             };
6685             
6686             var hh = '';
6687             
6688             if(typeof(config.sortable) != 'undefined' && config.sortable){
6689                 c.cls = 'sortable';
6690                 c.html = '<i class="glyphicon"></i>' + c.html;
6691             }
6692             
6693             if(typeof(config.lgHeader) != 'undefined'){
6694                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6695             }
6696             
6697             if(typeof(config.mdHeader) != 'undefined'){
6698                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6699             }
6700             
6701             if(typeof(config.smHeader) != 'undefined'){
6702                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6703             }
6704             
6705             if(typeof(config.xsHeader) != 'undefined'){
6706                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6707             }
6708             
6709             if(hh.length){
6710                 c.html = hh;
6711             }
6712             
6713             if(typeof(config.tooltip) != 'undefined'){
6714                 c.tooltip = config.tooltip;
6715             }
6716             
6717             if(typeof(config.colspan) != 'undefined'){
6718                 c.colspan = config.colspan;
6719             }
6720             
6721             if(typeof(config.hidden) != 'undefined' && config.hidden){
6722                 c.style += ' display:none;';
6723             }
6724             
6725             if(typeof(config.dataIndex) != 'undefined'){
6726                 c.sort = config.dataIndex;
6727             }
6728             
6729            
6730             
6731             if(typeof(config.align) != 'undefined' && config.align.length){
6732                 c.style += ' text-align:' + config.align + ';';
6733             }
6734             
6735             if(typeof(config.width) != 'undefined'){
6736                 c.style += ' width:' + config.width + 'px;';
6737                 this.totalWidth += config.width;
6738             } else {
6739                 this.totalWidth += 100; // assume minimum of 100 per column?
6740             }
6741             
6742             if(typeof(config.cls) != 'undefined'){
6743                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6744             }
6745             
6746             ['xs','sm','md','lg'].map(function(size){
6747                 
6748                 if(typeof(config[size]) == 'undefined'){
6749                     return;
6750                 }
6751                 
6752                 if (!config[size]) { // 0 = hidden
6753                     c.cls += ' hidden-' + size;
6754                     return;
6755                 }
6756                 
6757                 c.cls += ' col-' + size + '-' + config[size];
6758
6759             });
6760             
6761             header.cn.push(c)
6762         }
6763         
6764         return header;
6765     },
6766     
6767     renderBody : function()
6768     {
6769         var body = {
6770             tag: 'tbody',
6771             cn : [
6772                 {
6773                     tag: 'tr',
6774                     cn : [
6775                         {
6776                             tag : 'td',
6777                             colspan :  this.cm.getColumnCount()
6778                         }
6779                     ]
6780                 }
6781             ]
6782         };
6783         
6784         return body;
6785     },
6786     
6787     renderFooter : function()
6788     {
6789         var footer = {
6790             tag: 'tfoot',
6791             cn : [
6792                 {
6793                     tag: 'tr',
6794                     cn : [
6795                         {
6796                             tag : 'td',
6797                             colspan :  this.cm.getColumnCount()
6798                         }
6799                     ]
6800                 }
6801             ]
6802         };
6803         
6804         return footer;
6805     },
6806     
6807     
6808     
6809     onLoad : function()
6810     {
6811 //        Roo.log('ds onload');
6812         this.clear();
6813         
6814         var _this = this;
6815         var cm = this.cm;
6816         var ds = this.store;
6817         
6818         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6819             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6820             if (_this.store.sortInfo) {
6821                     
6822                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6823                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6824                 }
6825                 
6826                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6827                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6828                 }
6829             }
6830         });
6831         
6832         var tbody =  this.mainBody;
6833               
6834         if(ds.getCount() > 0){
6835             ds.data.each(function(d,rowIndex){
6836                 var row =  this.renderRow(cm, ds, rowIndex);
6837                 
6838                 tbody.createChild(row);
6839                 
6840                 var _this = this;
6841                 
6842                 if(row.cellObjects.length){
6843                     Roo.each(row.cellObjects, function(r){
6844                         _this.renderCellObject(r);
6845                     })
6846                 }
6847                 
6848             }, this);
6849         }
6850         
6851         var tfoot = this.el.select('tfoot', true).first();
6852         
6853         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6854             
6855             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6856             
6857             var total = this.ds.getTotalCount();
6858             
6859             if(this.footer.pageSize < total){
6860                 this.mainFoot.show();
6861             }
6862         }
6863         
6864         Roo.each(this.el.select('tbody td', true).elements, function(e){
6865             e.on('mouseover', _this.onMouseover, _this);
6866         });
6867         
6868         Roo.each(this.el.select('tbody td', true).elements, function(e){
6869             e.on('mouseout', _this.onMouseout, _this);
6870         });
6871         this.fireEvent('rowsrendered', this);
6872         
6873         this.autoSize();
6874     },
6875     
6876     
6877     onUpdate : function(ds,record)
6878     {
6879         this.refreshRow(record);
6880         this.autoSize();
6881     },
6882     
6883     onRemove : function(ds, record, index, isUpdate){
6884         if(isUpdate !== true){
6885             this.fireEvent("beforerowremoved", this, index, record);
6886         }
6887         var bt = this.mainBody.dom;
6888         
6889         var rows = this.el.select('tbody > tr', true).elements;
6890         
6891         if(typeof(rows[index]) != 'undefined'){
6892             bt.removeChild(rows[index].dom);
6893         }
6894         
6895 //        if(bt.rows[index]){
6896 //            bt.removeChild(bt.rows[index]);
6897 //        }
6898         
6899         if(isUpdate !== true){
6900             //this.stripeRows(index);
6901             //this.syncRowHeights(index, index);
6902             //this.layout();
6903             this.fireEvent("rowremoved", this, index, record);
6904         }
6905     },
6906     
6907     onAdd : function(ds, records, rowIndex)
6908     {
6909         //Roo.log('on Add called');
6910         // - note this does not handle multiple adding very well..
6911         var bt = this.mainBody.dom;
6912         for (var i =0 ; i < records.length;i++) {
6913             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6914             //Roo.log(records[i]);
6915             //Roo.log(this.store.getAt(rowIndex+i));
6916             this.insertRow(this.store, rowIndex + i, false);
6917             return;
6918         }
6919         
6920     },
6921     
6922     
6923     refreshRow : function(record){
6924         var ds = this.store, index;
6925         if(typeof record == 'number'){
6926             index = record;
6927             record = ds.getAt(index);
6928         }else{
6929             index = ds.indexOf(record);
6930         }
6931         this.insertRow(ds, index, true);
6932         this.autoSize();
6933         this.onRemove(ds, record, index+1, true);
6934         this.autoSize();
6935         //this.syncRowHeights(index, index);
6936         //this.layout();
6937         this.fireEvent("rowupdated", this, index, record);
6938     },
6939     
6940     insertRow : function(dm, rowIndex, isUpdate){
6941         
6942         if(!isUpdate){
6943             this.fireEvent("beforerowsinserted", this, rowIndex);
6944         }
6945             //var s = this.getScrollState();
6946         var row = this.renderRow(this.cm, this.store, rowIndex);
6947         // insert before rowIndex..
6948         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6949         
6950         var _this = this;
6951                 
6952         if(row.cellObjects.length){
6953             Roo.each(row.cellObjects, function(r){
6954                 _this.renderCellObject(r);
6955             })
6956         }
6957             
6958         if(!isUpdate){
6959             this.fireEvent("rowsinserted", this, rowIndex);
6960             //this.syncRowHeights(firstRow, lastRow);
6961             //this.stripeRows(firstRow);
6962             //this.layout();
6963         }
6964         
6965     },
6966     
6967     
6968     getRowDom : function(rowIndex)
6969     {
6970         var rows = this.el.select('tbody > tr', true).elements;
6971         
6972         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6973         
6974     },
6975     // returns the object tree for a tr..
6976   
6977     
6978     renderRow : function(cm, ds, rowIndex) 
6979     {
6980         var d = ds.getAt(rowIndex);
6981         
6982         var row = {
6983             tag : 'tr',
6984             cls : 'x-row-' + rowIndex,
6985             cn : []
6986         };
6987             
6988         var cellObjects = [];
6989         
6990         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6991             var config = cm.config[i];
6992             
6993             var renderer = cm.getRenderer(i);
6994             var value = '';
6995             var id = false;
6996             
6997             if(typeof(renderer) !== 'undefined'){
6998                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6999             }
7000             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7001             // and are rendered into the cells after the row is rendered - using the id for the element.
7002             
7003             if(typeof(value) === 'object'){
7004                 id = Roo.id();
7005                 cellObjects.push({
7006                     container : id,
7007                     cfg : value 
7008                 })
7009             }
7010             
7011             var rowcfg = {
7012                 record: d,
7013                 rowIndex : rowIndex,
7014                 colIndex : i,
7015                 rowClass : ''
7016             };
7017
7018             this.fireEvent('rowclass', this, rowcfg);
7019             
7020             var td = {
7021                 tag: 'td',
7022                 cls : rowcfg.rowClass + ' x-col-' + i,
7023                 style: '',
7024                 html: (typeof(value) === 'object') ? '' : value
7025             };
7026             
7027             if (id) {
7028                 td.id = id;
7029             }
7030             
7031             if(typeof(config.colspan) != 'undefined'){
7032                 td.colspan = config.colspan;
7033             }
7034             
7035             if(typeof(config.hidden) != 'undefined' && config.hidden){
7036                 td.style += ' display:none;';
7037             }
7038             
7039             if(typeof(config.align) != 'undefined' && config.align.length){
7040                 td.style += ' text-align:' + config.align + ';';
7041             }
7042             if(typeof(config.valign) != 'undefined' && config.valign.length){
7043                 td.style += ' vertical-align:' + config.valign + ';';
7044             }
7045             
7046             if(typeof(config.width) != 'undefined'){
7047                 td.style += ' width:' +  config.width + 'px;';
7048             }
7049             
7050             if(typeof(config.cursor) != 'undefined'){
7051                 td.style += ' cursor:' +  config.cursor + ';';
7052             }
7053             
7054             if(typeof(config.cls) != 'undefined'){
7055                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7056             }
7057             
7058             ['xs','sm','md','lg'].map(function(size){
7059                 
7060                 if(typeof(config[size]) == 'undefined'){
7061                     return;
7062                 }
7063                 
7064                 if (!config[size]) { // 0 = hidden
7065                     td.cls += ' hidden-' + size;
7066                     return;
7067                 }
7068                 
7069                 td.cls += ' col-' + size + '-' + config[size];
7070
7071             });
7072             
7073             row.cn.push(td);
7074            
7075         }
7076         
7077         row.cellObjects = cellObjects;
7078         
7079         return row;
7080           
7081     },
7082     
7083     
7084     
7085     onBeforeLoad : function()
7086     {
7087         
7088     },
7089      /**
7090      * Remove all rows
7091      */
7092     clear : function()
7093     {
7094         this.el.select('tbody', true).first().dom.innerHTML = '';
7095     },
7096     /**
7097      * Show or hide a row.
7098      * @param {Number} rowIndex to show or hide
7099      * @param {Boolean} state hide
7100      */
7101     setRowVisibility : function(rowIndex, state)
7102     {
7103         var bt = this.mainBody.dom;
7104         
7105         var rows = this.el.select('tbody > tr', true).elements;
7106         
7107         if(typeof(rows[rowIndex]) == 'undefined'){
7108             return;
7109         }
7110         rows[rowIndex].dom.style.display = state ? '' : 'none';
7111     },
7112     
7113     
7114     getSelectionModel : function(){
7115         if(!this.selModel){
7116             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7117         }
7118         return this.selModel;
7119     },
7120     /*
7121      * Render the Roo.bootstrap object from renderder
7122      */
7123     renderCellObject : function(r)
7124     {
7125         var _this = this;
7126         
7127         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7128         
7129         var t = r.cfg.render(r.container);
7130         
7131         if(r.cfg.cn){
7132             Roo.each(r.cfg.cn, function(c){
7133                 var child = {
7134                     container: t.getChildContainer(),
7135                     cfg: c
7136                 };
7137                 _this.renderCellObject(child);
7138             })
7139         }
7140     },
7141     
7142     getRowIndex : function(row)
7143     {
7144         var rowIndex = -1;
7145         
7146         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7147             if(el != row){
7148                 return;
7149             }
7150             
7151             rowIndex = index;
7152         });
7153         
7154         return rowIndex;
7155     },
7156      /**
7157      * Returns the grid's underlying element = used by panel.Grid
7158      * @return {Element} The element
7159      */
7160     getGridEl : function(){
7161         return this.el;
7162     },
7163      /**
7164      * Forces a resize - used by panel.Grid
7165      * @return {Element} The element
7166      */
7167     autoSize : function()
7168     {
7169         //var ctr = Roo.get(this.container.dom.parentElement);
7170         var ctr = Roo.get(this.el.dom);
7171         
7172         var thd = this.getGridEl().select('thead',true).first();
7173         var tbd = this.getGridEl().select('tbody', true).first();
7174         var tfd = this.getGridEl().select('tfoot', true).first();
7175         
7176         var cw = ctr.getWidth();
7177         
7178         if (tbd) {
7179             
7180             tbd.setSize(ctr.getWidth(),
7181                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7182             );
7183             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7184             cw -= barsize;
7185         }
7186         cw = Math.max(cw, this.totalWidth);
7187         this.getGridEl().select('tr',true).setWidth(cw);
7188         // resize 'expandable coloumn?
7189         
7190         return; // we doe not have a view in this design..
7191         
7192     },
7193     onBodyScroll: function()
7194     {
7195         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7196         if(this.mainHead){
7197             this.mainHead.setStyle({
7198                 'position' : 'relative',
7199                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7200             });
7201         }
7202         
7203         if(this.lazyLoad){
7204             
7205             var scrollHeight = this.mainBody.dom.scrollHeight;
7206             
7207             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7208             
7209             var height = this.mainBody.getHeight();
7210             
7211             if(scrollHeight - height == scrollTop) {
7212                 
7213                 var total = this.ds.getTotalCount();
7214                 
7215                 if(this.footer.cursor + this.footer.pageSize < total){
7216                     
7217                     this.footer.ds.load({
7218                         params : {
7219                             start : this.footer.cursor + this.footer.pageSize,
7220                             limit : this.footer.pageSize
7221                         },
7222                         add : true
7223                     });
7224                 }
7225             }
7226             
7227         }
7228     },
7229     
7230     onHeaderChange : function()
7231     {
7232         var header = this.renderHeader();
7233         var table = this.el.select('table', true).first();
7234         
7235         this.mainHead.remove();
7236         this.mainHead = table.createChild(header, this.mainBody, false);
7237     },
7238     
7239     onHiddenChange : function(colModel, colIndex, hidden)
7240     {
7241         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7242         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7243         
7244         this.CSS.updateRule(thSelector, "display", "");
7245         this.CSS.updateRule(tdSelector, "display", "");
7246         
7247         if(hidden){
7248             this.CSS.updateRule(thSelector, "display", "none");
7249             this.CSS.updateRule(tdSelector, "display", "none");
7250         }
7251         
7252         this.onHeaderChange();
7253         this.onLoad();
7254     },
7255     
7256     setColumnWidth: function(col_index, width)
7257     {
7258         // width = "md-2 xs-2..."
7259         if(!this.colModel.config[col_index]) {
7260             return;
7261         }
7262         
7263         var w = width.split(" ");
7264         
7265         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7266         
7267         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7268         
7269         
7270         for(var j = 0; j < w.length; j++) {
7271             
7272             if(!w[j]) {
7273                 continue;
7274             }
7275             
7276             var size_cls = w[j].split("-");
7277             
7278             if(!Number.isInteger(size_cls[1] * 1)) {
7279                 continue;
7280             }
7281             
7282             if(!this.colModel.config[col_index][size_cls[0]]) {
7283                 continue;
7284             }
7285             
7286             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7287                 continue;
7288             }
7289             
7290             h_row[0].classList.replace(
7291                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7292                 "col-"+size_cls[0]+"-"+size_cls[1]
7293             );
7294             
7295             for(var i = 0; i < rows.length; i++) {
7296                 
7297                 var size_cls = w[j].split("-");
7298                 
7299                 if(!Number.isInteger(size_cls[1] * 1)) {
7300                     continue;
7301                 }
7302                 
7303                 if(!this.colModel.config[col_index][size_cls[0]]) {
7304                     continue;
7305                 }
7306                 
7307                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7308                     continue;
7309                 }
7310                 
7311                 rows[i].classList.replace(
7312                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7313                     "col-"+size_cls[0]+"-"+size_cls[1]
7314                 );
7315             }
7316             
7317             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7318         }
7319     }
7320 });
7321
7322  
7323
7324  /*
7325  * - LGPL
7326  *
7327  * table cell
7328  * 
7329  */
7330
7331 /**
7332  * @class Roo.bootstrap.TableCell
7333  * @extends Roo.bootstrap.Component
7334  * Bootstrap TableCell class
7335  * @cfg {String} html cell contain text
7336  * @cfg {String} cls cell class
7337  * @cfg {String} tag cell tag (td|th) default td
7338  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7339  * @cfg {String} align Aligns the content in a cell
7340  * @cfg {String} axis Categorizes cells
7341  * @cfg {String} bgcolor Specifies the background color of a cell
7342  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7343  * @cfg {Number} colspan Specifies the number of columns a cell should span
7344  * @cfg {String} headers Specifies one or more header cells a cell is related to
7345  * @cfg {Number} height Sets the height of a cell
7346  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7347  * @cfg {Number} rowspan Sets the number of rows a cell should span
7348  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7349  * @cfg {String} valign Vertical aligns the content in a cell
7350  * @cfg {Number} width Specifies the width of a cell
7351  * 
7352  * @constructor
7353  * Create a new TableCell
7354  * @param {Object} config The config object
7355  */
7356
7357 Roo.bootstrap.TableCell = function(config){
7358     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7359 };
7360
7361 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7362     
7363     html: false,
7364     cls: false,
7365     tag: false,
7366     abbr: false,
7367     align: false,
7368     axis: false,
7369     bgcolor: false,
7370     charoff: false,
7371     colspan: false,
7372     headers: false,
7373     height: false,
7374     nowrap: false,
7375     rowspan: false,
7376     scope: false,
7377     valign: false,
7378     width: false,
7379     
7380     
7381     getAutoCreate : function(){
7382         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7383         
7384         cfg = {
7385             tag: 'td'
7386         };
7387         
7388         if(this.tag){
7389             cfg.tag = this.tag;
7390         }
7391         
7392         if (this.html) {
7393             cfg.html=this.html
7394         }
7395         if (this.cls) {
7396             cfg.cls=this.cls
7397         }
7398         if (this.abbr) {
7399             cfg.abbr=this.abbr
7400         }
7401         if (this.align) {
7402             cfg.align=this.align
7403         }
7404         if (this.axis) {
7405             cfg.axis=this.axis
7406         }
7407         if (this.bgcolor) {
7408             cfg.bgcolor=this.bgcolor
7409         }
7410         if (this.charoff) {
7411             cfg.charoff=this.charoff
7412         }
7413         if (this.colspan) {
7414             cfg.colspan=this.colspan
7415         }
7416         if (this.headers) {
7417             cfg.headers=this.headers
7418         }
7419         if (this.height) {
7420             cfg.height=this.height
7421         }
7422         if (this.nowrap) {
7423             cfg.nowrap=this.nowrap
7424         }
7425         if (this.rowspan) {
7426             cfg.rowspan=this.rowspan
7427         }
7428         if (this.scope) {
7429             cfg.scope=this.scope
7430         }
7431         if (this.valign) {
7432             cfg.valign=this.valign
7433         }
7434         if (this.width) {
7435             cfg.width=this.width
7436         }
7437         
7438         
7439         return cfg;
7440     }
7441    
7442 });
7443
7444  
7445
7446  /*
7447  * - LGPL
7448  *
7449  * table row
7450  * 
7451  */
7452
7453 /**
7454  * @class Roo.bootstrap.TableRow
7455  * @extends Roo.bootstrap.Component
7456  * Bootstrap TableRow class
7457  * @cfg {String} cls row class
7458  * @cfg {String} align Aligns the content in a table row
7459  * @cfg {String} bgcolor Specifies a background color for a table row
7460  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7461  * @cfg {String} valign Vertical aligns the content in a table row
7462  * 
7463  * @constructor
7464  * Create a new TableRow
7465  * @param {Object} config The config object
7466  */
7467
7468 Roo.bootstrap.TableRow = function(config){
7469     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7470 };
7471
7472 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7473     
7474     cls: false,
7475     align: false,
7476     bgcolor: false,
7477     charoff: false,
7478     valign: false,
7479     
7480     getAutoCreate : function(){
7481         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7482         
7483         cfg = {
7484             tag: 'tr'
7485         };
7486             
7487         if(this.cls){
7488             cfg.cls = this.cls;
7489         }
7490         if(this.align){
7491             cfg.align = this.align;
7492         }
7493         if(this.bgcolor){
7494             cfg.bgcolor = this.bgcolor;
7495         }
7496         if(this.charoff){
7497             cfg.charoff = this.charoff;
7498         }
7499         if(this.valign){
7500             cfg.valign = this.valign;
7501         }
7502         
7503         return cfg;
7504     }
7505    
7506 });
7507
7508  
7509
7510  /*
7511  * - LGPL
7512  *
7513  * table body
7514  * 
7515  */
7516
7517 /**
7518  * @class Roo.bootstrap.TableBody
7519  * @extends Roo.bootstrap.Component
7520  * Bootstrap TableBody class
7521  * @cfg {String} cls element class
7522  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7523  * @cfg {String} align Aligns the content inside the element
7524  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7525  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7526  * 
7527  * @constructor
7528  * Create a new TableBody
7529  * @param {Object} config The config object
7530  */
7531
7532 Roo.bootstrap.TableBody = function(config){
7533     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7534 };
7535
7536 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7537     
7538     cls: false,
7539     tag: false,
7540     align: false,
7541     charoff: false,
7542     valign: false,
7543     
7544     getAutoCreate : function(){
7545         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7546         
7547         cfg = {
7548             tag: 'tbody'
7549         };
7550             
7551         if (this.cls) {
7552             cfg.cls=this.cls
7553         }
7554         if(this.tag){
7555             cfg.tag = this.tag;
7556         }
7557         
7558         if(this.align){
7559             cfg.align = this.align;
7560         }
7561         if(this.charoff){
7562             cfg.charoff = this.charoff;
7563         }
7564         if(this.valign){
7565             cfg.valign = this.valign;
7566         }
7567         
7568         return cfg;
7569     }
7570     
7571     
7572 //    initEvents : function()
7573 //    {
7574 //        
7575 //        if(!this.store){
7576 //            return;
7577 //        }
7578 //        
7579 //        this.store = Roo.factory(this.store, Roo.data);
7580 //        this.store.on('load', this.onLoad, this);
7581 //        
7582 //        this.store.load();
7583 //        
7584 //    },
7585 //    
7586 //    onLoad: function () 
7587 //    {   
7588 //        this.fireEvent('load', this);
7589 //    }
7590 //    
7591 //   
7592 });
7593
7594  
7595
7596  /*
7597  * Based on:
7598  * Ext JS Library 1.1.1
7599  * Copyright(c) 2006-2007, Ext JS, LLC.
7600  *
7601  * Originally Released Under LGPL - original licence link has changed is not relivant.
7602  *
7603  * Fork - LGPL
7604  * <script type="text/javascript">
7605  */
7606
7607 // as we use this in bootstrap.
7608 Roo.namespace('Roo.form');
7609  /**
7610  * @class Roo.form.Action
7611  * Internal Class used to handle form actions
7612  * @constructor
7613  * @param {Roo.form.BasicForm} el The form element or its id
7614  * @param {Object} config Configuration options
7615  */
7616
7617  
7618  
7619 // define the action interface
7620 Roo.form.Action = function(form, options){
7621     this.form = form;
7622     this.options = options || {};
7623 };
7624 /**
7625  * Client Validation Failed
7626  * @const 
7627  */
7628 Roo.form.Action.CLIENT_INVALID = 'client';
7629 /**
7630  * Server Validation Failed
7631  * @const 
7632  */
7633 Roo.form.Action.SERVER_INVALID = 'server';
7634  /**
7635  * Connect to Server Failed
7636  * @const 
7637  */
7638 Roo.form.Action.CONNECT_FAILURE = 'connect';
7639 /**
7640  * Reading Data from Server Failed
7641  * @const 
7642  */
7643 Roo.form.Action.LOAD_FAILURE = 'load';
7644
7645 Roo.form.Action.prototype = {
7646     type : 'default',
7647     failureType : undefined,
7648     response : undefined,
7649     result : undefined,
7650
7651     // interface method
7652     run : function(options){
7653
7654     },
7655
7656     // interface method
7657     success : function(response){
7658
7659     },
7660
7661     // interface method
7662     handleResponse : function(response){
7663
7664     },
7665
7666     // default connection failure
7667     failure : function(response){
7668         
7669         this.response = response;
7670         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7671         this.form.afterAction(this, false);
7672     },
7673
7674     processResponse : function(response){
7675         this.response = response;
7676         if(!response.responseText){
7677             return true;
7678         }
7679         this.result = this.handleResponse(response);
7680         return this.result;
7681     },
7682
7683     // utility functions used internally
7684     getUrl : function(appendParams){
7685         var url = this.options.url || this.form.url || this.form.el.dom.action;
7686         if(appendParams){
7687             var p = this.getParams();
7688             if(p){
7689                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7690             }
7691         }
7692         return url;
7693     },
7694
7695     getMethod : function(){
7696         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7697     },
7698
7699     getParams : function(){
7700         var bp = this.form.baseParams;
7701         var p = this.options.params;
7702         if(p){
7703             if(typeof p == "object"){
7704                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7705             }else if(typeof p == 'string' && bp){
7706                 p += '&' + Roo.urlEncode(bp);
7707             }
7708         }else if(bp){
7709             p = Roo.urlEncode(bp);
7710         }
7711         return p;
7712     },
7713
7714     createCallback : function(){
7715         return {
7716             success: this.success,
7717             failure: this.failure,
7718             scope: this,
7719             timeout: (this.form.timeout*1000),
7720             upload: this.form.fileUpload ? this.success : undefined
7721         };
7722     }
7723 };
7724
7725 Roo.form.Action.Submit = function(form, options){
7726     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7727 };
7728
7729 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7730     type : 'submit',
7731
7732     haveProgress : false,
7733     uploadComplete : false,
7734     
7735     // uploadProgress indicator.
7736     uploadProgress : function()
7737     {
7738         if (!this.form.progressUrl) {
7739             return;
7740         }
7741         
7742         if (!this.haveProgress) {
7743             Roo.MessageBox.progress("Uploading", "Uploading");
7744         }
7745         if (this.uploadComplete) {
7746            Roo.MessageBox.hide();
7747            return;
7748         }
7749         
7750         this.haveProgress = true;
7751    
7752         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7753         
7754         var c = new Roo.data.Connection();
7755         c.request({
7756             url : this.form.progressUrl,
7757             params: {
7758                 id : uid
7759             },
7760             method: 'GET',
7761             success : function(req){
7762                //console.log(data);
7763                 var rdata = false;
7764                 var edata;
7765                 try  {
7766                    rdata = Roo.decode(req.responseText)
7767                 } catch (e) {
7768                     Roo.log("Invalid data from server..");
7769                     Roo.log(edata);
7770                     return;
7771                 }
7772                 if (!rdata || !rdata.success) {
7773                     Roo.log(rdata);
7774                     Roo.MessageBox.alert(Roo.encode(rdata));
7775                     return;
7776                 }
7777                 var data = rdata.data;
7778                 
7779                 if (this.uploadComplete) {
7780                    Roo.MessageBox.hide();
7781                    return;
7782                 }
7783                    
7784                 if (data){
7785                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7786                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7787                     );
7788                 }
7789                 this.uploadProgress.defer(2000,this);
7790             },
7791        
7792             failure: function(data) {
7793                 Roo.log('progress url failed ');
7794                 Roo.log(data);
7795             },
7796             scope : this
7797         });
7798            
7799     },
7800     
7801     
7802     run : function()
7803     {
7804         // run get Values on the form, so it syncs any secondary forms.
7805         this.form.getValues();
7806         
7807         var o = this.options;
7808         var method = this.getMethod();
7809         var isPost = method == 'POST';
7810         if(o.clientValidation === false || this.form.isValid()){
7811             
7812             if (this.form.progressUrl) {
7813                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7814                     (new Date() * 1) + '' + Math.random());
7815                     
7816             } 
7817             
7818             
7819             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7820                 form:this.form.el.dom,
7821                 url:this.getUrl(!isPost),
7822                 method: method,
7823                 params:isPost ? this.getParams() : null,
7824                 isUpload: this.form.fileUpload
7825             }));
7826             
7827             this.uploadProgress();
7828
7829         }else if (o.clientValidation !== false){ // client validation failed
7830             this.failureType = Roo.form.Action.CLIENT_INVALID;
7831             this.form.afterAction(this, false);
7832         }
7833     },
7834
7835     success : function(response)
7836     {
7837         this.uploadComplete= true;
7838         if (this.haveProgress) {
7839             Roo.MessageBox.hide();
7840         }
7841         
7842         
7843         var result = this.processResponse(response);
7844         if(result === true || result.success){
7845             this.form.afterAction(this, true);
7846             return;
7847         }
7848         if(result.errors){
7849             this.form.markInvalid(result.errors);
7850             this.failureType = Roo.form.Action.SERVER_INVALID;
7851         }
7852         this.form.afterAction(this, false);
7853     },
7854     failure : function(response)
7855     {
7856         this.uploadComplete= true;
7857         if (this.haveProgress) {
7858             Roo.MessageBox.hide();
7859         }
7860         
7861         this.response = response;
7862         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7863         this.form.afterAction(this, false);
7864     },
7865     
7866     handleResponse : function(response){
7867         if(this.form.errorReader){
7868             var rs = this.form.errorReader.read(response);
7869             var errors = [];
7870             if(rs.records){
7871                 for(var i = 0, len = rs.records.length; i < len; i++) {
7872                     var r = rs.records[i];
7873                     errors[i] = r.data;
7874                 }
7875             }
7876             if(errors.length < 1){
7877                 errors = null;
7878             }
7879             return {
7880                 success : rs.success,
7881                 errors : errors
7882             };
7883         }
7884         var ret = false;
7885         try {
7886             ret = Roo.decode(response.responseText);
7887         } catch (e) {
7888             ret = {
7889                 success: false,
7890                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7891                 errors : []
7892             };
7893         }
7894         return ret;
7895         
7896     }
7897 });
7898
7899
7900 Roo.form.Action.Load = function(form, options){
7901     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7902     this.reader = this.form.reader;
7903 };
7904
7905 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7906     type : 'load',
7907
7908     run : function(){
7909         
7910         Roo.Ajax.request(Roo.apply(
7911                 this.createCallback(), {
7912                     method:this.getMethod(),
7913                     url:this.getUrl(false),
7914                     params:this.getParams()
7915         }));
7916     },
7917
7918     success : function(response){
7919         
7920         var result = this.processResponse(response);
7921         if(result === true || !result.success || !result.data){
7922             this.failureType = Roo.form.Action.LOAD_FAILURE;
7923             this.form.afterAction(this, false);
7924             return;
7925         }
7926         this.form.clearInvalid();
7927         this.form.setValues(result.data);
7928         this.form.afterAction(this, true);
7929     },
7930
7931     handleResponse : function(response){
7932         if(this.form.reader){
7933             var rs = this.form.reader.read(response);
7934             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7935             return {
7936                 success : rs.success,
7937                 data : data
7938             };
7939         }
7940         return Roo.decode(response.responseText);
7941     }
7942 });
7943
7944 Roo.form.Action.ACTION_TYPES = {
7945     'load' : Roo.form.Action.Load,
7946     'submit' : Roo.form.Action.Submit
7947 };/*
7948  * - LGPL
7949  *
7950  * form
7951  *
7952  */
7953
7954 /**
7955  * @class Roo.bootstrap.Form
7956  * @extends Roo.bootstrap.Component
7957  * Bootstrap Form class
7958  * @cfg {String} method  GET | POST (default POST)
7959  * @cfg {String} labelAlign top | left (default top)
7960  * @cfg {String} align left  | right - for navbars
7961  * @cfg {Boolean} loadMask load mask when submit (default true)
7962
7963  *
7964  * @constructor
7965  * Create a new Form
7966  * @param {Object} config The config object
7967  */
7968
7969
7970 Roo.bootstrap.Form = function(config){
7971     
7972     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7973     
7974     Roo.bootstrap.Form.popover.apply();
7975     
7976     this.addEvents({
7977         /**
7978          * @event clientvalidation
7979          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7980          * @param {Form} this
7981          * @param {Boolean} valid true if the form has passed client-side validation
7982          */
7983         clientvalidation: true,
7984         /**
7985          * @event beforeaction
7986          * Fires before any action is performed. Return false to cancel the action.
7987          * @param {Form} this
7988          * @param {Action} action The action to be performed
7989          */
7990         beforeaction: true,
7991         /**
7992          * @event actionfailed
7993          * Fires when an action fails.
7994          * @param {Form} this
7995          * @param {Action} action The action that failed
7996          */
7997         actionfailed : true,
7998         /**
7999          * @event actioncomplete
8000          * Fires when an action is completed.
8001          * @param {Form} this
8002          * @param {Action} action The action that completed
8003          */
8004         actioncomplete : true
8005     });
8006 };
8007
8008 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8009
8010      /**
8011      * @cfg {String} method
8012      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8013      */
8014     method : 'POST',
8015     /**
8016      * @cfg {String} url
8017      * The URL to use for form actions if one isn't supplied in the action options.
8018      */
8019     /**
8020      * @cfg {Boolean} fileUpload
8021      * Set to true if this form is a file upload.
8022      */
8023
8024     /**
8025      * @cfg {Object} baseParams
8026      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8027      */
8028
8029     /**
8030      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8031      */
8032     timeout: 30,
8033     /**
8034      * @cfg {Sting} align (left|right) for navbar forms
8035      */
8036     align : 'left',
8037
8038     // private
8039     activeAction : null,
8040
8041     /**
8042      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8043      * element by passing it or its id or mask the form itself by passing in true.
8044      * @type Mixed
8045      */
8046     waitMsgTarget : false,
8047
8048     loadMask : true,
8049     
8050     /**
8051      * @cfg {Boolean} errorMask (true|false) default false
8052      */
8053     errorMask : false,
8054     
8055     /**
8056      * @cfg {Number} maskOffset Default 100
8057      */
8058     maskOffset : 100,
8059     
8060     /**
8061      * @cfg {Boolean} maskBody
8062      */
8063     maskBody : false,
8064
8065     getAutoCreate : function(){
8066
8067         var cfg = {
8068             tag: 'form',
8069             method : this.method || 'POST',
8070             id : this.id || Roo.id(),
8071             cls : ''
8072         };
8073         if (this.parent().xtype.match(/^Nav/)) {
8074             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8075
8076         }
8077
8078         if (this.labelAlign == 'left' ) {
8079             cfg.cls += ' form-horizontal';
8080         }
8081
8082
8083         return cfg;
8084     },
8085     initEvents : function()
8086     {
8087         this.el.on('submit', this.onSubmit, this);
8088         // this was added as random key presses on the form where triggering form submit.
8089         this.el.on('keypress', function(e) {
8090             if (e.getCharCode() != 13) {
8091                 return true;
8092             }
8093             // we might need to allow it for textareas.. and some other items.
8094             // check e.getTarget().
8095
8096             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8097                 return true;
8098             }
8099
8100             Roo.log("keypress blocked");
8101
8102             e.preventDefault();
8103             return false;
8104         });
8105         
8106     },
8107     // private
8108     onSubmit : function(e){
8109         e.stopEvent();
8110     },
8111
8112      /**
8113      * Returns true if client-side validation on the form is successful.
8114      * @return Boolean
8115      */
8116     isValid : function(){
8117         var items = this.getItems();
8118         var valid = true;
8119         var target = false;
8120         
8121         items.each(function(f){
8122             
8123             if(f.validate()){
8124                 return;
8125             }
8126             
8127             Roo.log('invalid field: ' + f.name);
8128             
8129             valid = false;
8130
8131             if(!target && f.el.isVisible(true)){
8132                 target = f;
8133             }
8134            
8135         });
8136         
8137         if(this.errorMask && !valid){
8138             Roo.bootstrap.Form.popover.mask(this, target);
8139         }
8140         
8141         return valid;
8142     },
8143     
8144     /**
8145      * Returns true if any fields in this form have changed since their original load.
8146      * @return Boolean
8147      */
8148     isDirty : function(){
8149         var dirty = false;
8150         var items = this.getItems();
8151         items.each(function(f){
8152            if(f.isDirty()){
8153                dirty = true;
8154                return false;
8155            }
8156            return true;
8157         });
8158         return dirty;
8159     },
8160      /**
8161      * Performs a predefined action (submit or load) or custom actions you define on this form.
8162      * @param {String} actionName The name of the action type
8163      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8164      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8165      * accept other config options):
8166      * <pre>
8167 Property          Type             Description
8168 ----------------  ---------------  ----------------------------------------------------------------------------------
8169 url               String           The url for the action (defaults to the form's url)
8170 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8171 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8172 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8173                                    validate the form on the client (defaults to false)
8174      * </pre>
8175      * @return {BasicForm} this
8176      */
8177     doAction : function(action, options){
8178         if(typeof action == 'string'){
8179             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8180         }
8181         if(this.fireEvent('beforeaction', this, action) !== false){
8182             this.beforeAction(action);
8183             action.run.defer(100, action);
8184         }
8185         return this;
8186     },
8187
8188     // private
8189     beforeAction : function(action){
8190         var o = action.options;
8191         
8192         if(this.loadMask){
8193             
8194             if(this.maskBody){
8195                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8196             } else {
8197                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8198             }
8199         }
8200         // not really supported yet.. ??
8201
8202         //if(this.waitMsgTarget === true){
8203         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8204         //}else if(this.waitMsgTarget){
8205         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8206         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8207         //}else {
8208         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8209        // }
8210
8211     },
8212
8213     // private
8214     afterAction : function(action, success){
8215         this.activeAction = null;
8216         var o = action.options;
8217
8218         if(this.loadMask){
8219             
8220             if(this.maskBody){
8221                 Roo.get(document.body).unmask();
8222             } else {
8223                 this.el.unmask();
8224             }
8225         }
8226         
8227         //if(this.waitMsgTarget === true){
8228 //            this.el.unmask();
8229         //}else if(this.waitMsgTarget){
8230         //    this.waitMsgTarget.unmask();
8231         //}else{
8232         //    Roo.MessageBox.updateProgress(1);
8233         //    Roo.MessageBox.hide();
8234        // }
8235         //
8236         if(success){
8237             if(o.reset){
8238                 this.reset();
8239             }
8240             Roo.callback(o.success, o.scope, [this, action]);
8241             this.fireEvent('actioncomplete', this, action);
8242
8243         }else{
8244
8245             // failure condition..
8246             // we have a scenario where updates need confirming.
8247             // eg. if a locking scenario exists..
8248             // we look for { errors : { needs_confirm : true }} in the response.
8249             if (
8250                 (typeof(action.result) != 'undefined')  &&
8251                 (typeof(action.result.errors) != 'undefined')  &&
8252                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8253            ){
8254                 var _t = this;
8255                 Roo.log("not supported yet");
8256                  /*
8257
8258                 Roo.MessageBox.confirm(
8259                     "Change requires confirmation",
8260                     action.result.errorMsg,
8261                     function(r) {
8262                         if (r != 'yes') {
8263                             return;
8264                         }
8265                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8266                     }
8267
8268                 );
8269                 */
8270
8271
8272                 return;
8273             }
8274
8275             Roo.callback(o.failure, o.scope, [this, action]);
8276             // show an error message if no failed handler is set..
8277             if (!this.hasListener('actionfailed')) {
8278                 Roo.log("need to add dialog support");
8279                 /*
8280                 Roo.MessageBox.alert("Error",
8281                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8282                         action.result.errorMsg :
8283                         "Saving Failed, please check your entries or try again"
8284                 );
8285                 */
8286             }
8287
8288             this.fireEvent('actionfailed', this, action);
8289         }
8290
8291     },
8292     /**
8293      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8294      * @param {String} id The value to search for
8295      * @return Field
8296      */
8297     findField : function(id){
8298         var items = this.getItems();
8299         var field = items.get(id);
8300         if(!field){
8301              items.each(function(f){
8302                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8303                     field = f;
8304                     return false;
8305                 }
8306                 return true;
8307             });
8308         }
8309         return field || null;
8310     },
8311      /**
8312      * Mark fields in this form invalid in bulk.
8313      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8314      * @return {BasicForm} this
8315      */
8316     markInvalid : function(errors){
8317         if(errors instanceof Array){
8318             for(var i = 0, len = errors.length; i < len; i++){
8319                 var fieldError = errors[i];
8320                 var f = this.findField(fieldError.id);
8321                 if(f){
8322                     f.markInvalid(fieldError.msg);
8323                 }
8324             }
8325         }else{
8326             var field, id;
8327             for(id in errors){
8328                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8329                     field.markInvalid(errors[id]);
8330                 }
8331             }
8332         }
8333         //Roo.each(this.childForms || [], function (f) {
8334         //    f.markInvalid(errors);
8335         //});
8336
8337         return this;
8338     },
8339
8340     /**
8341      * Set values for fields in this form in bulk.
8342      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8343      * @return {BasicForm} this
8344      */
8345     setValues : function(values){
8346         if(values instanceof Array){ // array of objects
8347             for(var i = 0, len = values.length; i < len; i++){
8348                 var v = values[i];
8349                 var f = this.findField(v.id);
8350                 if(f){
8351                     f.setValue(v.value);
8352                     if(this.trackResetOnLoad){
8353                         f.originalValue = f.getValue();
8354                     }
8355                 }
8356             }
8357         }else{ // object hash
8358             var field, id;
8359             for(id in values){
8360                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8361
8362                     if (field.setFromData &&
8363                         field.valueField &&
8364                         field.displayField &&
8365                         // combos' with local stores can
8366                         // be queried via setValue()
8367                         // to set their value..
8368                         (field.store && !field.store.isLocal)
8369                         ) {
8370                         // it's a combo
8371                         var sd = { };
8372                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8373                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8374                         field.setFromData(sd);
8375
8376                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8377                         
8378                         field.setFromData(values);
8379                         
8380                     } else {
8381                         field.setValue(values[id]);
8382                     }
8383
8384
8385                     if(this.trackResetOnLoad){
8386                         field.originalValue = field.getValue();
8387                     }
8388                 }
8389             }
8390         }
8391
8392         //Roo.each(this.childForms || [], function (f) {
8393         //    f.setValues(values);
8394         //});
8395
8396         return this;
8397     },
8398
8399     /**
8400      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8401      * they are returned as an array.
8402      * @param {Boolean} asString
8403      * @return {Object}
8404      */
8405     getValues : function(asString){
8406         //if (this.childForms) {
8407             // copy values from the child forms
8408         //    Roo.each(this.childForms, function (f) {
8409         //        this.setValues(f.getValues());
8410         //    }, this);
8411         //}
8412
8413
8414
8415         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8416         if(asString === true){
8417             return fs;
8418         }
8419         return Roo.urlDecode(fs);
8420     },
8421
8422     /**
8423      * Returns the fields in this form as an object with key/value pairs.
8424      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8425      * @return {Object}
8426      */
8427     getFieldValues : function(with_hidden)
8428     {
8429         var items = this.getItems();
8430         var ret = {};
8431         items.each(function(f){
8432             
8433             if (!f.getName()) {
8434                 return;
8435             }
8436             
8437             var v = f.getValue();
8438             
8439             if (f.inputType =='radio') {
8440                 if (typeof(ret[f.getName()]) == 'undefined') {
8441                     ret[f.getName()] = ''; // empty..
8442                 }
8443
8444                 if (!f.el.dom.checked) {
8445                     return;
8446
8447                 }
8448                 v = f.el.dom.value;
8449
8450             }
8451             
8452             if(f.xtype == 'MoneyField'){
8453                 ret[f.currencyName] = f.getCurrency();
8454             }
8455
8456             // not sure if this supported any more..
8457             if ((typeof(v) == 'object') && f.getRawValue) {
8458                 v = f.getRawValue() ; // dates..
8459             }
8460             // combo boxes where name != hiddenName...
8461             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8462                 ret[f.name] = f.getRawValue();
8463             }
8464             ret[f.getName()] = v;
8465         });
8466
8467         return ret;
8468     },
8469
8470     /**
8471      * Clears all invalid messages in this form.
8472      * @return {BasicForm} this
8473      */
8474     clearInvalid : function(){
8475         var items = this.getItems();
8476
8477         items.each(function(f){
8478            f.clearInvalid();
8479         });
8480
8481         return this;
8482     },
8483
8484     /**
8485      * Resets this form.
8486      * @return {BasicForm} this
8487      */
8488     reset : function(){
8489         var items = this.getItems();
8490         items.each(function(f){
8491             f.reset();
8492         });
8493
8494         Roo.each(this.childForms || [], function (f) {
8495             f.reset();
8496         });
8497
8498
8499         return this;
8500     },
8501     
8502     getItems : function()
8503     {
8504         var r=new Roo.util.MixedCollection(false, function(o){
8505             return o.id || (o.id = Roo.id());
8506         });
8507         var iter = function(el) {
8508             if (el.inputEl) {
8509                 r.add(el);
8510             }
8511             if (!el.items) {
8512                 return;
8513             }
8514             Roo.each(el.items,function(e) {
8515                 iter(e);
8516             });
8517         };
8518
8519         iter(this);
8520         return r;
8521     },
8522     
8523     hideFields : function(items)
8524     {
8525         Roo.each(items, function(i){
8526             
8527             var f = this.findField(i);
8528             
8529             if(!f){
8530                 return;
8531             }
8532             
8533             f.hide();
8534             
8535         }, this);
8536     },
8537     
8538     showFields : function(items)
8539     {
8540         Roo.each(items, function(i){
8541             
8542             var f = this.findField(i);
8543             
8544             if(!f){
8545                 return;
8546             }
8547             
8548             f.show();
8549             
8550         }, this);
8551     }
8552
8553 });
8554
8555 Roo.apply(Roo.bootstrap.Form, {
8556     
8557     popover : {
8558         
8559         padding : 5,
8560         
8561         isApplied : false,
8562         
8563         isMasked : false,
8564         
8565         form : false,
8566         
8567         target : false,
8568         
8569         toolTip : false,
8570         
8571         intervalID : false,
8572         
8573         maskEl : false,
8574         
8575         apply : function()
8576         {
8577             if(this.isApplied){
8578                 return;
8579             }
8580             
8581             this.maskEl = {
8582                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8583                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8584                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8585                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8586             };
8587             
8588             this.maskEl.top.enableDisplayMode("block");
8589             this.maskEl.left.enableDisplayMode("block");
8590             this.maskEl.bottom.enableDisplayMode("block");
8591             this.maskEl.right.enableDisplayMode("block");
8592             
8593             this.toolTip = new Roo.bootstrap.Tooltip({
8594                 cls : 'roo-form-error-popover',
8595                 alignment : {
8596                     'left' : ['r-l', [-2,0], 'right'],
8597                     'right' : ['l-r', [2,0], 'left'],
8598                     'bottom' : ['tl-bl', [0,2], 'top'],
8599                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8600                 }
8601             });
8602             
8603             this.toolTip.render(Roo.get(document.body));
8604
8605             this.toolTip.el.enableDisplayMode("block");
8606             
8607             Roo.get(document.body).on('click', function(){
8608                 this.unmask();
8609             }, this);
8610             
8611             Roo.get(document.body).on('touchstart', function(){
8612                 this.unmask();
8613             }, this);
8614             
8615             this.isApplied = true
8616         },
8617         
8618         mask : function(form, target)
8619         {
8620             this.form = form;
8621             
8622             this.target = target;
8623             
8624             if(!this.form.errorMask || !target.el){
8625                 return;
8626             }
8627             
8628             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8629             
8630             Roo.log(scrollable);
8631             
8632             var ot = this.target.el.calcOffsetsTo(scrollable);
8633             
8634             var scrollTo = ot[1] - this.form.maskOffset;
8635             
8636             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8637             
8638             scrollable.scrollTo('top', scrollTo);
8639             
8640             var box = this.target.el.getBox();
8641             Roo.log(box);
8642             var zIndex = Roo.bootstrap.Modal.zIndex++;
8643
8644             
8645             this.maskEl.top.setStyle('position', 'absolute');
8646             this.maskEl.top.setStyle('z-index', zIndex);
8647             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8648             this.maskEl.top.setLeft(0);
8649             this.maskEl.top.setTop(0);
8650             this.maskEl.top.show();
8651             
8652             this.maskEl.left.setStyle('position', 'absolute');
8653             this.maskEl.left.setStyle('z-index', zIndex);
8654             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8655             this.maskEl.left.setLeft(0);
8656             this.maskEl.left.setTop(box.y - this.padding);
8657             this.maskEl.left.show();
8658
8659             this.maskEl.bottom.setStyle('position', 'absolute');
8660             this.maskEl.bottom.setStyle('z-index', zIndex);
8661             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8662             this.maskEl.bottom.setLeft(0);
8663             this.maskEl.bottom.setTop(box.bottom + this.padding);
8664             this.maskEl.bottom.show();
8665
8666             this.maskEl.right.setStyle('position', 'absolute');
8667             this.maskEl.right.setStyle('z-index', zIndex);
8668             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8669             this.maskEl.right.setLeft(box.right + this.padding);
8670             this.maskEl.right.setTop(box.y - this.padding);
8671             this.maskEl.right.show();
8672
8673             this.toolTip.bindEl = this.target.el;
8674
8675             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8676
8677             var tip = this.target.blankText;
8678
8679             if(this.target.getValue() !== '' ) {
8680                 
8681                 if (this.target.invalidText.length) {
8682                     tip = this.target.invalidText;
8683                 } else if (this.target.regexText.length){
8684                     tip = this.target.regexText;
8685                 }
8686             }
8687
8688             this.toolTip.show(tip);
8689
8690             this.intervalID = window.setInterval(function() {
8691                 Roo.bootstrap.Form.popover.unmask();
8692             }, 10000);
8693
8694             window.onwheel = function(){ return false;};
8695             
8696             (function(){ this.isMasked = true; }).defer(500, this);
8697             
8698         },
8699         
8700         unmask : function()
8701         {
8702             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8703                 return;
8704             }
8705             
8706             this.maskEl.top.setStyle('position', 'absolute');
8707             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8708             this.maskEl.top.hide();
8709
8710             this.maskEl.left.setStyle('position', 'absolute');
8711             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8712             this.maskEl.left.hide();
8713
8714             this.maskEl.bottom.setStyle('position', 'absolute');
8715             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8716             this.maskEl.bottom.hide();
8717
8718             this.maskEl.right.setStyle('position', 'absolute');
8719             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8720             this.maskEl.right.hide();
8721             
8722             this.toolTip.hide();
8723             
8724             this.toolTip.el.hide();
8725             
8726             window.onwheel = function(){ return true;};
8727             
8728             if(this.intervalID){
8729                 window.clearInterval(this.intervalID);
8730                 this.intervalID = false;
8731             }
8732             
8733             this.isMasked = false;
8734             
8735         }
8736         
8737     }
8738     
8739 });
8740
8741 /*
8742  * Based on:
8743  * Ext JS Library 1.1.1
8744  * Copyright(c) 2006-2007, Ext JS, LLC.
8745  *
8746  * Originally Released Under LGPL - original licence link has changed is not relivant.
8747  *
8748  * Fork - LGPL
8749  * <script type="text/javascript">
8750  */
8751 /**
8752  * @class Roo.form.VTypes
8753  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8754  * @singleton
8755  */
8756 Roo.form.VTypes = function(){
8757     // closure these in so they are only created once.
8758     var alpha = /^[a-zA-Z_]+$/;
8759     var alphanum = /^[a-zA-Z0-9_]+$/;
8760     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8761     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8762
8763     // All these messages and functions are configurable
8764     return {
8765         /**
8766          * The function used to validate email addresses
8767          * @param {String} value The email address
8768          */
8769         'email' : function(v){
8770             return email.test(v);
8771         },
8772         /**
8773          * The error text to display when the email validation function returns false
8774          * @type String
8775          */
8776         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8777         /**
8778          * The keystroke filter mask to be applied on email input
8779          * @type RegExp
8780          */
8781         'emailMask' : /[a-z0-9_\.\-@]/i,
8782
8783         /**
8784          * The function used to validate URLs
8785          * @param {String} value The URL
8786          */
8787         'url' : function(v){
8788             return url.test(v);
8789         },
8790         /**
8791          * The error text to display when the url validation function returns false
8792          * @type String
8793          */
8794         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8795         
8796         /**
8797          * The function used to validate alpha values
8798          * @param {String} value The value
8799          */
8800         'alpha' : function(v){
8801             return alpha.test(v);
8802         },
8803         /**
8804          * The error text to display when the alpha validation function returns false
8805          * @type String
8806          */
8807         'alphaText' : 'This field should only contain letters and _',
8808         /**
8809          * The keystroke filter mask to be applied on alpha input
8810          * @type RegExp
8811          */
8812         'alphaMask' : /[a-z_]/i,
8813
8814         /**
8815          * The function used to validate alphanumeric values
8816          * @param {String} value The value
8817          */
8818         'alphanum' : function(v){
8819             return alphanum.test(v);
8820         },
8821         /**
8822          * The error text to display when the alphanumeric validation function returns false
8823          * @type String
8824          */
8825         'alphanumText' : 'This field should only contain letters, numbers and _',
8826         /**
8827          * The keystroke filter mask to be applied on alphanumeric input
8828          * @type RegExp
8829          */
8830         'alphanumMask' : /[a-z0-9_]/i
8831     };
8832 }();/*
8833  * - LGPL
8834  *
8835  * Input
8836  * 
8837  */
8838
8839 /**
8840  * @class Roo.bootstrap.Input
8841  * @extends Roo.bootstrap.Component
8842  * Bootstrap Input class
8843  * @cfg {Boolean} disabled is it disabled
8844  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8845  * @cfg {String} name name of the input
8846  * @cfg {string} fieldLabel - the label associated
8847  * @cfg {string} placeholder - placeholder to put in text.
8848  * @cfg {string}  before - input group add on before
8849  * @cfg {string} after - input group add on after
8850  * @cfg {string} size - (lg|sm) or leave empty..
8851  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8852  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8853  * @cfg {Number} md colspan out of 12 for computer-sized screens
8854  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8855  * @cfg {string} value default value of the input
8856  * @cfg {Number} labelWidth set the width of label 
8857  * @cfg {Number} labellg set the width of label (1-12)
8858  * @cfg {Number} labelmd set the width of label (1-12)
8859  * @cfg {Number} labelsm set the width of label (1-12)
8860  * @cfg {Number} labelxs set the width of label (1-12)
8861  * @cfg {String} labelAlign (top|left)
8862  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8863  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8864  * @cfg {String} indicatorpos (left|right) default left
8865  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8866  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8867
8868  * @cfg {String} align (left|center|right) Default left
8869  * @cfg {Boolean} forceFeedback (true|false) Default false
8870  * 
8871  * @constructor
8872  * Create a new Input
8873  * @param {Object} config The config object
8874  */
8875
8876 Roo.bootstrap.Input = function(config){
8877     
8878     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8879     
8880     this.addEvents({
8881         /**
8882          * @event focus
8883          * Fires when this field receives input focus.
8884          * @param {Roo.form.Field} this
8885          */
8886         focus : true,
8887         /**
8888          * @event blur
8889          * Fires when this field loses input focus.
8890          * @param {Roo.form.Field} this
8891          */
8892         blur : true,
8893         /**
8894          * @event specialkey
8895          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8896          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8897          * @param {Roo.form.Field} this
8898          * @param {Roo.EventObject} e The event object
8899          */
8900         specialkey : true,
8901         /**
8902          * @event change
8903          * Fires just before the field blurs if the field value has changed.
8904          * @param {Roo.form.Field} this
8905          * @param {Mixed} newValue The new value
8906          * @param {Mixed} oldValue The original value
8907          */
8908         change : true,
8909         /**
8910          * @event invalid
8911          * Fires after the field has been marked as invalid.
8912          * @param {Roo.form.Field} this
8913          * @param {String} msg The validation message
8914          */
8915         invalid : true,
8916         /**
8917          * @event valid
8918          * Fires after the field has been validated with no errors.
8919          * @param {Roo.form.Field} this
8920          */
8921         valid : true,
8922          /**
8923          * @event keyup
8924          * Fires after the key up
8925          * @param {Roo.form.Field} this
8926          * @param {Roo.EventObject}  e The event Object
8927          */
8928         keyup : true
8929     });
8930 };
8931
8932 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8933      /**
8934      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8935       automatic validation (defaults to "keyup").
8936      */
8937     validationEvent : "keyup",
8938      /**
8939      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8940      */
8941     validateOnBlur : true,
8942     /**
8943      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8944      */
8945     validationDelay : 250,
8946      /**
8947      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8948      */
8949     focusClass : "x-form-focus",  // not needed???
8950     
8951        
8952     /**
8953      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8954      */
8955     invalidClass : "has-warning",
8956     
8957     /**
8958      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8959      */
8960     validClass : "has-success",
8961     
8962     /**
8963      * @cfg {Boolean} hasFeedback (true|false) default true
8964      */
8965     hasFeedback : true,
8966     
8967     /**
8968      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8969      */
8970     invalidFeedbackClass : "glyphicon-warning-sign",
8971     
8972     /**
8973      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8974      */
8975     validFeedbackClass : "glyphicon-ok",
8976     
8977     /**
8978      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8979      */
8980     selectOnFocus : false,
8981     
8982      /**
8983      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8984      */
8985     maskRe : null,
8986        /**
8987      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8988      */
8989     vtype : null,
8990     
8991       /**
8992      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8993      */
8994     disableKeyFilter : false,
8995     
8996        /**
8997      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8998      */
8999     disabled : false,
9000      /**
9001      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9002      */
9003     allowBlank : true,
9004     /**
9005      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9006      */
9007     blankText : "Please complete this mandatory field",
9008     
9009      /**
9010      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9011      */
9012     minLength : 0,
9013     /**
9014      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9015      */
9016     maxLength : Number.MAX_VALUE,
9017     /**
9018      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9019      */
9020     minLengthText : "The minimum length for this field is {0}",
9021     /**
9022      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9023      */
9024     maxLengthText : "The maximum length for this field is {0}",
9025   
9026     
9027     /**
9028      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9029      * If available, this function will be called only after the basic validators all return true, and will be passed the
9030      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9031      */
9032     validator : null,
9033     /**
9034      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9035      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9036      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9037      */
9038     regex : null,
9039     /**
9040      * @cfg {String} regexText -- Depricated - use Invalid Text
9041      */
9042     regexText : "",
9043     
9044     /**
9045      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9046      */
9047     invalidText : "",
9048     
9049     
9050     
9051     autocomplete: false,
9052     
9053     
9054     fieldLabel : '',
9055     inputType : 'text',
9056     
9057     name : false,
9058     placeholder: false,
9059     before : false,
9060     after : false,
9061     size : false,
9062     hasFocus : false,
9063     preventMark: false,
9064     isFormField : true,
9065     value : '',
9066     labelWidth : 2,
9067     labelAlign : false,
9068     readOnly : false,
9069     align : false,
9070     formatedValue : false,
9071     forceFeedback : false,
9072     
9073     indicatorpos : 'left',
9074     
9075     labellg : 0,
9076     labelmd : 0,
9077     labelsm : 0,
9078     labelxs : 0,
9079     
9080     capture : '',
9081     accept : '',
9082     
9083     parentLabelAlign : function()
9084     {
9085         var parent = this;
9086         while (parent.parent()) {
9087             parent = parent.parent();
9088             if (typeof(parent.labelAlign) !='undefined') {
9089                 return parent.labelAlign;
9090             }
9091         }
9092         return 'left';
9093         
9094     },
9095     
9096     getAutoCreate : function()
9097     {
9098         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9099         
9100         var id = Roo.id();
9101         
9102         var cfg = {};
9103         
9104         if(this.inputType != 'hidden'){
9105             cfg.cls = 'form-group' //input-group
9106         }
9107         
9108         var input =  {
9109             tag: 'input',
9110             id : id,
9111             type : this.inputType,
9112             value : this.value,
9113             cls : 'form-control',
9114             placeholder : this.placeholder || '',
9115             autocomplete : this.autocomplete || 'new-password'
9116         };
9117         
9118         if(this.capture.length){
9119             input.capture = this.capture;
9120         }
9121         
9122         if(this.accept.length){
9123             input.accept = this.accept + "/*";
9124         }
9125         
9126         if(this.align){
9127             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9128         }
9129         
9130         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9131             input.maxLength = this.maxLength;
9132         }
9133         
9134         if (this.disabled) {
9135             input.disabled=true;
9136         }
9137         
9138         if (this.readOnly) {
9139             input.readonly=true;
9140         }
9141         
9142         if (this.name) {
9143             input.name = this.name;
9144         }
9145         
9146         if (this.size) {
9147             input.cls += ' input-' + this.size;
9148         }
9149         
9150         var settings=this;
9151         ['xs','sm','md','lg'].map(function(size){
9152             if (settings[size]) {
9153                 cfg.cls += ' col-' + size + '-' + settings[size];
9154             }
9155         });
9156         
9157         var inputblock = input;
9158         
9159         var feedback = {
9160             tag: 'span',
9161             cls: 'glyphicon form-control-feedback'
9162         };
9163             
9164         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9165             
9166             inputblock = {
9167                 cls : 'has-feedback',
9168                 cn :  [
9169                     input,
9170                     feedback
9171                 ] 
9172             };  
9173         }
9174         
9175         if (this.before || this.after) {
9176             
9177             inputblock = {
9178                 cls : 'input-group',
9179                 cn :  [] 
9180             };
9181             
9182             if (this.before && typeof(this.before) == 'string') {
9183                 
9184                 inputblock.cn.push({
9185                     tag :'span',
9186                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9187                     html : this.before
9188                 });
9189             }
9190             if (this.before && typeof(this.before) == 'object') {
9191                 this.before = Roo.factory(this.before);
9192                 
9193                 inputblock.cn.push({
9194                     tag :'span',
9195                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9196                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9197                 });
9198             }
9199             
9200             inputblock.cn.push(input);
9201             
9202             if (this.after && typeof(this.after) == 'string') {
9203                 inputblock.cn.push({
9204                     tag :'span',
9205                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9206                     html : this.after
9207                 });
9208             }
9209             if (this.after && typeof(this.after) == 'object') {
9210                 this.after = Roo.factory(this.after);
9211                 
9212                 inputblock.cn.push({
9213                     tag :'span',
9214                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9215                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9216                 });
9217             }
9218             
9219             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9220                 inputblock.cls += ' has-feedback';
9221                 inputblock.cn.push(feedback);
9222             }
9223         };
9224         var indicator = {
9225             tag : 'i',
9226             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9227             tooltip : 'This field is required'
9228         };
9229         if (Roo.bootstrap.version == 4) {
9230             indicator = {
9231                 tag : 'i',
9232                 style : 'display-none'
9233             };
9234         }
9235         if (align ==='left' && this.fieldLabel.length) {
9236             
9237             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9238             
9239             cfg.cn = [
9240                 indicator,
9241                 {
9242                     tag: 'label',
9243                     'for' :  id,
9244                     cls : 'control-label col-form-label',
9245                     html : this.fieldLabel
9246
9247                 },
9248                 {
9249                     cls : "", 
9250                     cn: [
9251                         inputblock
9252                     ]
9253                 }
9254             ];
9255             
9256             var labelCfg = cfg.cn[1];
9257             var contentCfg = cfg.cn[2];
9258             
9259             if(this.indicatorpos == 'right'){
9260                 cfg.cn = [
9261                     {
9262                         tag: 'label',
9263                         'for' :  id,
9264                         cls : 'control-label col-form-label',
9265                         cn : [
9266                             {
9267                                 tag : 'span',
9268                                 html : this.fieldLabel
9269                             },
9270                             indicator
9271                         ]
9272                     },
9273                     {
9274                         cls : "",
9275                         cn: [
9276                             inputblock
9277                         ]
9278                     }
9279
9280                 ];
9281                 
9282                 labelCfg = cfg.cn[0];
9283                 contentCfg = cfg.cn[1];
9284             
9285             }
9286             
9287             if(this.labelWidth > 12){
9288                 labelCfg.style = "width: " + this.labelWidth + 'px';
9289             }
9290             
9291             if(this.labelWidth < 13 && this.labelmd == 0){
9292                 this.labelmd = this.labelWidth;
9293             }
9294             
9295             if(this.labellg > 0){
9296                 labelCfg.cls += ' col-lg-' + this.labellg;
9297                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9298             }
9299             
9300             if(this.labelmd > 0){
9301                 labelCfg.cls += ' col-md-' + this.labelmd;
9302                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9303             }
9304             
9305             if(this.labelsm > 0){
9306                 labelCfg.cls += ' col-sm-' + this.labelsm;
9307                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9308             }
9309             
9310             if(this.labelxs > 0){
9311                 labelCfg.cls += ' col-xs-' + this.labelxs;
9312                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9313             }
9314             
9315             
9316         } else if ( this.fieldLabel.length) {
9317                 
9318             cfg.cn = [
9319                 {
9320                     tag : 'i',
9321                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9322                     tooltip : 'This field is required'
9323                 },
9324                 {
9325                     tag: 'label',
9326                    //cls : 'input-group-addon',
9327                     html : this.fieldLabel
9328
9329                 },
9330
9331                inputblock
9332
9333            ];
9334            
9335            if(this.indicatorpos == 'right'){
9336                 
9337                 cfg.cn = [
9338                     {
9339                         tag: 'label',
9340                        //cls : 'input-group-addon',
9341                         html : this.fieldLabel
9342
9343                     },
9344                     {
9345                         tag : 'i',
9346                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9347                         tooltip : 'This field is required'
9348                     },
9349
9350                    inputblock
9351
9352                ];
9353
9354             }
9355
9356         } else {
9357             
9358             cfg.cn = [
9359
9360                     inputblock
9361
9362             ];
9363                 
9364                 
9365         };
9366         
9367         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9368            cfg.cls += ' navbar-form';
9369         }
9370         
9371         if (this.parentType === 'NavGroup') {
9372            cfg.cls += ' navbar-form';
9373            cfg.tag = 'li';
9374         }
9375         
9376         return cfg;
9377         
9378     },
9379     /**
9380      * return the real input element.
9381      */
9382     inputEl: function ()
9383     {
9384         return this.el.select('input.form-control',true).first();
9385     },
9386     
9387     tooltipEl : function()
9388     {
9389         return this.inputEl();
9390     },
9391     
9392     indicatorEl : function()
9393     {
9394         if (Roo.bootstrap.version == 4) {
9395             return false; // not enabled in v4 yet.
9396         }
9397         
9398         var indicator = this.el.select('i.roo-required-indicator',true).first();
9399         
9400         if(!indicator){
9401             return false;
9402         }
9403         
9404         return indicator;
9405         
9406     },
9407     
9408     setDisabled : function(v)
9409     {
9410         var i  = this.inputEl().dom;
9411         if (!v) {
9412             i.removeAttribute('disabled');
9413             return;
9414             
9415         }
9416         i.setAttribute('disabled','true');
9417     },
9418     initEvents : function()
9419     {
9420           
9421         this.inputEl().on("keydown" , this.fireKey,  this);
9422         this.inputEl().on("focus", this.onFocus,  this);
9423         this.inputEl().on("blur", this.onBlur,  this);
9424         
9425         this.inputEl().relayEvent('keyup', this);
9426         
9427         this.indicator = this.indicatorEl();
9428         
9429         if(this.indicator){
9430             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9431         }
9432  
9433         // reference to original value for reset
9434         this.originalValue = this.getValue();
9435         //Roo.form.TextField.superclass.initEvents.call(this);
9436         if(this.validationEvent == 'keyup'){
9437             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9438             this.inputEl().on('keyup', this.filterValidation, this);
9439         }
9440         else if(this.validationEvent !== false){
9441             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9442         }
9443         
9444         if(this.selectOnFocus){
9445             this.on("focus", this.preFocus, this);
9446             
9447         }
9448         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9449             this.inputEl().on("keypress", this.filterKeys, this);
9450         } else {
9451             this.inputEl().relayEvent('keypress', this);
9452         }
9453        /* if(this.grow){
9454             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9455             this.el.on("click", this.autoSize,  this);
9456         }
9457         */
9458         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9459             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9460         }
9461         
9462         if (typeof(this.before) == 'object') {
9463             this.before.render(this.el.select('.roo-input-before',true).first());
9464         }
9465         if (typeof(this.after) == 'object') {
9466             this.after.render(this.el.select('.roo-input-after',true).first());
9467         }
9468         
9469         this.inputEl().on('change', this.onChange, this);
9470         
9471     },
9472     filterValidation : function(e){
9473         if(!e.isNavKeyPress()){
9474             this.validationTask.delay(this.validationDelay);
9475         }
9476     },
9477      /**
9478      * Validates the field value
9479      * @return {Boolean} True if the value is valid, else false
9480      */
9481     validate : function(){
9482         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9483         if(this.disabled || this.validateValue(this.getRawValue())){
9484             this.markValid();
9485             return true;
9486         }
9487         
9488         this.markInvalid();
9489         return false;
9490     },
9491     
9492     
9493     /**
9494      * Validates a value according to the field's validation rules and marks the field as invalid
9495      * if the validation fails
9496      * @param {Mixed} value The value to validate
9497      * @return {Boolean} True if the value is valid, else false
9498      */
9499     validateValue : function(value)
9500     {
9501         if(this.getVisibilityEl().hasClass('hidden')){
9502             return true;
9503         }
9504         
9505         if(value.length < 1)  { // if it's blank
9506             if(this.allowBlank){
9507                 return true;
9508             }
9509             return false;
9510         }
9511         
9512         if(value.length < this.minLength){
9513             return false;
9514         }
9515         if(value.length > this.maxLength){
9516             return false;
9517         }
9518         if(this.vtype){
9519             var vt = Roo.form.VTypes;
9520             if(!vt[this.vtype](value, this)){
9521                 return false;
9522             }
9523         }
9524         if(typeof this.validator == "function"){
9525             var msg = this.validator(value);
9526             if(msg !== true){
9527                 return false;
9528             }
9529             if (typeof(msg) == 'string') {
9530                 this.invalidText = msg;
9531             }
9532         }
9533         
9534         if(this.regex && !this.regex.test(value)){
9535             return false;
9536         }
9537         
9538         return true;
9539     },
9540     
9541      // private
9542     fireKey : function(e){
9543         //Roo.log('field ' + e.getKey());
9544         if(e.isNavKeyPress()){
9545             this.fireEvent("specialkey", this, e);
9546         }
9547     },
9548     focus : function (selectText){
9549         if(this.rendered){
9550             this.inputEl().focus();
9551             if(selectText === true){
9552                 this.inputEl().dom.select();
9553             }
9554         }
9555         return this;
9556     } ,
9557     
9558     onFocus : function(){
9559         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9560            // this.el.addClass(this.focusClass);
9561         }
9562         if(!this.hasFocus){
9563             this.hasFocus = true;
9564             this.startValue = this.getValue();
9565             this.fireEvent("focus", this);
9566         }
9567     },
9568     
9569     beforeBlur : Roo.emptyFn,
9570
9571     
9572     // private
9573     onBlur : function(){
9574         this.beforeBlur();
9575         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9576             //this.el.removeClass(this.focusClass);
9577         }
9578         this.hasFocus = false;
9579         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9580             this.validate();
9581         }
9582         var v = this.getValue();
9583         if(String(v) !== String(this.startValue)){
9584             this.fireEvent('change', this, v, this.startValue);
9585         }
9586         this.fireEvent("blur", this);
9587     },
9588     
9589     onChange : function(e)
9590     {
9591         var v = this.getValue();
9592         if(String(v) !== String(this.startValue)){
9593             this.fireEvent('change', this, v, this.startValue);
9594         }
9595         
9596     },
9597     
9598     /**
9599      * Resets the current field value to the originally loaded value and clears any validation messages
9600      */
9601     reset : function(){
9602         this.setValue(this.originalValue);
9603         this.validate();
9604     },
9605      /**
9606      * Returns the name of the field
9607      * @return {Mixed} name The name field
9608      */
9609     getName: function(){
9610         return this.name;
9611     },
9612      /**
9613      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9614      * @return {Mixed} value The field value
9615      */
9616     getValue : function(){
9617         
9618         var v = this.inputEl().getValue();
9619         
9620         return v;
9621     },
9622     /**
9623      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9624      * @return {Mixed} value The field value
9625      */
9626     getRawValue : function(){
9627         var v = this.inputEl().getValue();
9628         
9629         return v;
9630     },
9631     
9632     /**
9633      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9634      * @param {Mixed} value The value to set
9635      */
9636     setRawValue : function(v){
9637         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9638     },
9639     
9640     selectText : function(start, end){
9641         var v = this.getRawValue();
9642         if(v.length > 0){
9643             start = start === undefined ? 0 : start;
9644             end = end === undefined ? v.length : end;
9645             var d = this.inputEl().dom;
9646             if(d.setSelectionRange){
9647                 d.setSelectionRange(start, end);
9648             }else if(d.createTextRange){
9649                 var range = d.createTextRange();
9650                 range.moveStart("character", start);
9651                 range.moveEnd("character", v.length-end);
9652                 range.select();
9653             }
9654         }
9655     },
9656     
9657     /**
9658      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9659      * @param {Mixed} value The value to set
9660      */
9661     setValue : function(v){
9662         this.value = v;
9663         if(this.rendered){
9664             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9665             this.validate();
9666         }
9667     },
9668     
9669     /*
9670     processValue : function(value){
9671         if(this.stripCharsRe){
9672             var newValue = value.replace(this.stripCharsRe, '');
9673             if(newValue !== value){
9674                 this.setRawValue(newValue);
9675                 return newValue;
9676             }
9677         }
9678         return value;
9679     },
9680   */
9681     preFocus : function(){
9682         
9683         if(this.selectOnFocus){
9684             this.inputEl().dom.select();
9685         }
9686     },
9687     filterKeys : function(e){
9688         var k = e.getKey();
9689         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9690             return;
9691         }
9692         var c = e.getCharCode(), cc = String.fromCharCode(c);
9693         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9694             return;
9695         }
9696         if(!this.maskRe.test(cc)){
9697             e.stopEvent();
9698         }
9699     },
9700      /**
9701      * Clear any invalid styles/messages for this field
9702      */
9703     clearInvalid : function(){
9704         
9705         if(!this.el || this.preventMark){ // not rendered
9706             return;
9707         }
9708         
9709      
9710         this.el.removeClass(this.invalidClass);
9711         
9712         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9713             
9714             var feedback = this.el.select('.form-control-feedback', true).first();
9715             
9716             if(feedback){
9717                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9718             }
9719             
9720         }
9721         
9722         if(this.indicator){
9723             this.indicator.removeClass('visible');
9724             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9725         }
9726         
9727         this.fireEvent('valid', this);
9728     },
9729     
9730      /**
9731      * Mark this field as valid
9732      */
9733     markValid : function()
9734     {
9735         if(!this.el  || this.preventMark){ // not rendered...
9736             return;
9737         }
9738         
9739         this.el.removeClass([this.invalidClass, this.validClass]);
9740         
9741         var feedback = this.el.select('.form-control-feedback', true).first();
9742             
9743         if(feedback){
9744             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9745         }
9746         
9747         if(this.indicator){
9748             this.indicator.removeClass('visible');
9749             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9750         }
9751         
9752         if(this.disabled){
9753             return;
9754         }
9755         
9756         if(this.allowBlank && !this.getRawValue().length){
9757             return;
9758         }
9759         
9760         this.el.addClass(this.validClass);
9761         
9762         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9763             
9764             var feedback = this.el.select('.form-control-feedback', true).first();
9765             
9766             if(feedback){
9767                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9768                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9769             }
9770             
9771         }
9772         
9773         this.fireEvent('valid', this);
9774     },
9775     
9776      /**
9777      * Mark this field as invalid
9778      * @param {String} msg The validation message
9779      */
9780     markInvalid : function(msg)
9781     {
9782         if(!this.el  || this.preventMark){ // not rendered
9783             return;
9784         }
9785         
9786         this.el.removeClass([this.invalidClass, this.validClass]);
9787         
9788         var feedback = this.el.select('.form-control-feedback', true).first();
9789             
9790         if(feedback){
9791             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9792         }
9793
9794         if(this.disabled){
9795             return;
9796         }
9797         
9798         if(this.allowBlank && !this.getRawValue().length){
9799             return;
9800         }
9801         
9802         if(this.indicator){
9803             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9804             this.indicator.addClass('visible');
9805         }
9806         
9807         this.el.addClass(this.invalidClass);
9808         
9809         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9810             
9811             var feedback = this.el.select('.form-control-feedback', true).first();
9812             
9813             if(feedback){
9814                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9815                 
9816                 if(this.getValue().length || this.forceFeedback){
9817                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9818                 }
9819                 
9820             }
9821             
9822         }
9823         
9824         this.fireEvent('invalid', this, msg);
9825     },
9826     // private
9827     SafariOnKeyDown : function(event)
9828     {
9829         // this is a workaround for a password hang bug on chrome/ webkit.
9830         if (this.inputEl().dom.type != 'password') {
9831             return;
9832         }
9833         
9834         var isSelectAll = false;
9835         
9836         if(this.inputEl().dom.selectionEnd > 0){
9837             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9838         }
9839         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9840             event.preventDefault();
9841             this.setValue('');
9842             return;
9843         }
9844         
9845         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9846             
9847             event.preventDefault();
9848             // this is very hacky as keydown always get's upper case.
9849             //
9850             var cc = String.fromCharCode(event.getCharCode());
9851             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9852             
9853         }
9854     },
9855     adjustWidth : function(tag, w){
9856         tag = tag.toLowerCase();
9857         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9858             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9859                 if(tag == 'input'){
9860                     return w + 2;
9861                 }
9862                 if(tag == 'textarea'){
9863                     return w-2;
9864                 }
9865             }else if(Roo.isOpera){
9866                 if(tag == 'input'){
9867                     return w + 2;
9868                 }
9869                 if(tag == 'textarea'){
9870                     return w-2;
9871                 }
9872             }
9873         }
9874         return w;
9875     },
9876     
9877     setFieldLabel : function(v)
9878     {
9879         if(!this.rendered){
9880             return;
9881         }
9882         
9883         if(this.indicatorEl()){
9884             var ar = this.el.select('label > span',true);
9885             
9886             if (ar.elements.length) {
9887                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9888                 this.fieldLabel = v;
9889                 return;
9890             }
9891             
9892             var br = this.el.select('label',true);
9893             
9894             if(br.elements.length) {
9895                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9896                 this.fieldLabel = v;
9897                 return;
9898             }
9899             
9900             Roo.log('Cannot Found any of label > span || label in input');
9901             return;
9902         }
9903         
9904         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9905         this.fieldLabel = v;
9906         
9907         
9908     }
9909 });
9910
9911  
9912 /*
9913  * - LGPL
9914  *
9915  * Input
9916  * 
9917  */
9918
9919 /**
9920  * @class Roo.bootstrap.TextArea
9921  * @extends Roo.bootstrap.Input
9922  * Bootstrap TextArea class
9923  * @cfg {Number} cols Specifies the visible width of a text area
9924  * @cfg {Number} rows Specifies the visible number of lines in a text area
9925  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9926  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9927  * @cfg {string} html text
9928  * 
9929  * @constructor
9930  * Create a new TextArea
9931  * @param {Object} config The config object
9932  */
9933
9934 Roo.bootstrap.TextArea = function(config){
9935     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9936    
9937 };
9938
9939 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9940      
9941     cols : false,
9942     rows : 5,
9943     readOnly : false,
9944     warp : 'soft',
9945     resize : false,
9946     value: false,
9947     html: false,
9948     
9949     getAutoCreate : function(){
9950         
9951         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9952         
9953         var id = Roo.id();
9954         
9955         var cfg = {};
9956         
9957         if(this.inputType != 'hidden'){
9958             cfg.cls = 'form-group' //input-group
9959         }
9960         
9961         var input =  {
9962             tag: 'textarea',
9963             id : id,
9964             warp : this.warp,
9965             rows : this.rows,
9966             value : this.value || '',
9967             html: this.html || '',
9968             cls : 'form-control',
9969             placeholder : this.placeholder || '' 
9970             
9971         };
9972         
9973         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9974             input.maxLength = this.maxLength;
9975         }
9976         
9977         if(this.resize){
9978             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9979         }
9980         
9981         if(this.cols){
9982             input.cols = this.cols;
9983         }
9984         
9985         if (this.readOnly) {
9986             input.readonly = true;
9987         }
9988         
9989         if (this.name) {
9990             input.name = this.name;
9991         }
9992         
9993         if (this.size) {
9994             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9995         }
9996         
9997         var settings=this;
9998         ['xs','sm','md','lg'].map(function(size){
9999             if (settings[size]) {
10000                 cfg.cls += ' col-' + size + '-' + settings[size];
10001             }
10002         });
10003         
10004         var inputblock = input;
10005         
10006         if(this.hasFeedback && !this.allowBlank){
10007             
10008             var feedback = {
10009                 tag: 'span',
10010                 cls: 'glyphicon form-control-feedback'
10011             };
10012
10013             inputblock = {
10014                 cls : 'has-feedback',
10015                 cn :  [
10016                     input,
10017                     feedback
10018                 ] 
10019             };  
10020         }
10021         
10022         
10023         if (this.before || this.after) {
10024             
10025             inputblock = {
10026                 cls : 'input-group',
10027                 cn :  [] 
10028             };
10029             if (this.before) {
10030                 inputblock.cn.push({
10031                     tag :'span',
10032                     cls : 'input-group-addon',
10033                     html : this.before
10034                 });
10035             }
10036             
10037             inputblock.cn.push(input);
10038             
10039             if(this.hasFeedback && !this.allowBlank){
10040                 inputblock.cls += ' has-feedback';
10041                 inputblock.cn.push(feedback);
10042             }
10043             
10044             if (this.after) {
10045                 inputblock.cn.push({
10046                     tag :'span',
10047                     cls : 'input-group-addon',
10048                     html : this.after
10049                 });
10050             }
10051             
10052         }
10053         
10054         if (align ==='left' && this.fieldLabel.length) {
10055             cfg.cn = [
10056                 {
10057                     tag: 'label',
10058                     'for' :  id,
10059                     cls : 'control-label',
10060                     html : this.fieldLabel
10061                 },
10062                 {
10063                     cls : "",
10064                     cn: [
10065                         inputblock
10066                     ]
10067                 }
10068
10069             ];
10070             
10071             if(this.labelWidth > 12){
10072                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10073             }
10074
10075             if(this.labelWidth < 13 && this.labelmd == 0){
10076                 this.labelmd = this.labelWidth;
10077             }
10078
10079             if(this.labellg > 0){
10080                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10081                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10082             }
10083
10084             if(this.labelmd > 0){
10085                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10086                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10087             }
10088
10089             if(this.labelsm > 0){
10090                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10091                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10092             }
10093
10094             if(this.labelxs > 0){
10095                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10096                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10097             }
10098             
10099         } else if ( this.fieldLabel.length) {
10100             cfg.cn = [
10101
10102                {
10103                    tag: 'label',
10104                    //cls : 'input-group-addon',
10105                    html : this.fieldLabel
10106
10107                },
10108
10109                inputblock
10110
10111            ];
10112
10113         } else {
10114
10115             cfg.cn = [
10116
10117                 inputblock
10118
10119             ];
10120                 
10121         }
10122         
10123         if (this.disabled) {
10124             input.disabled=true;
10125         }
10126         
10127         return cfg;
10128         
10129     },
10130     /**
10131      * return the real textarea element.
10132      */
10133     inputEl: function ()
10134     {
10135         return this.el.select('textarea.form-control',true).first();
10136     },
10137     
10138     /**
10139      * Clear any invalid styles/messages for this field
10140      */
10141     clearInvalid : function()
10142     {
10143         
10144         if(!this.el || this.preventMark){ // not rendered
10145             return;
10146         }
10147         
10148         var label = this.el.select('label', true).first();
10149         var icon = this.el.select('i.fa-star', true).first();
10150         
10151         if(label && icon){
10152             icon.remove();
10153         }
10154         
10155         this.el.removeClass(this.invalidClass);
10156         
10157         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10158             
10159             var feedback = this.el.select('.form-control-feedback', true).first();
10160             
10161             if(feedback){
10162                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10163             }
10164             
10165         }
10166         
10167         this.fireEvent('valid', this);
10168     },
10169     
10170      /**
10171      * Mark this field as valid
10172      */
10173     markValid : function()
10174     {
10175         if(!this.el  || this.preventMark){ // not rendered
10176             return;
10177         }
10178         
10179         this.el.removeClass([this.invalidClass, this.validClass]);
10180         
10181         var feedback = this.el.select('.form-control-feedback', true).first();
10182             
10183         if(feedback){
10184             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10185         }
10186
10187         if(this.disabled || this.allowBlank){
10188             return;
10189         }
10190         
10191         var label = this.el.select('label', true).first();
10192         var icon = this.el.select('i.fa-star', true).first();
10193         
10194         if(label && icon){
10195             icon.remove();
10196         }
10197         
10198         this.el.addClass(this.validClass);
10199         
10200         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10201             
10202             var feedback = this.el.select('.form-control-feedback', true).first();
10203             
10204             if(feedback){
10205                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10206                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10207             }
10208             
10209         }
10210         
10211         this.fireEvent('valid', this);
10212     },
10213     
10214      /**
10215      * Mark this field as invalid
10216      * @param {String} msg The validation message
10217      */
10218     markInvalid : function(msg)
10219     {
10220         if(!this.el  || this.preventMark){ // not rendered
10221             return;
10222         }
10223         
10224         this.el.removeClass([this.invalidClass, this.validClass]);
10225         
10226         var feedback = this.el.select('.form-control-feedback', true).first();
10227             
10228         if(feedback){
10229             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10230         }
10231
10232         if(this.disabled || this.allowBlank){
10233             return;
10234         }
10235         
10236         var label = this.el.select('label', true).first();
10237         var icon = this.el.select('i.fa-star', true).first();
10238         
10239         if(!this.getValue().length && label && !icon){
10240             this.el.createChild({
10241                 tag : 'i',
10242                 cls : 'text-danger fa fa-lg fa-star',
10243                 tooltip : 'This field is required',
10244                 style : 'margin-right:5px;'
10245             }, label, true);
10246         }
10247
10248         this.el.addClass(this.invalidClass);
10249         
10250         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10251             
10252             var feedback = this.el.select('.form-control-feedback', true).first();
10253             
10254             if(feedback){
10255                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10256                 
10257                 if(this.getValue().length || this.forceFeedback){
10258                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10259                 }
10260                 
10261             }
10262             
10263         }
10264         
10265         this.fireEvent('invalid', this, msg);
10266     }
10267 });
10268
10269  
10270 /*
10271  * - LGPL
10272  *
10273  * trigger field - base class for combo..
10274  * 
10275  */
10276  
10277 /**
10278  * @class Roo.bootstrap.TriggerField
10279  * @extends Roo.bootstrap.Input
10280  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10281  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10282  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10283  * for which you can provide a custom implementation.  For example:
10284  * <pre><code>
10285 var trigger = new Roo.bootstrap.TriggerField();
10286 trigger.onTriggerClick = myTriggerFn;
10287 trigger.applyTo('my-field');
10288 </code></pre>
10289  *
10290  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10291  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10292  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10293  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10294  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10295
10296  * @constructor
10297  * Create a new TriggerField.
10298  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10299  * to the base TextField)
10300  */
10301 Roo.bootstrap.TriggerField = function(config){
10302     this.mimicing = false;
10303     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10304 };
10305
10306 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10307     /**
10308      * @cfg {String} triggerClass A CSS class to apply to the trigger
10309      */
10310      /**
10311      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10312      */
10313     hideTrigger:false,
10314
10315     /**
10316      * @cfg {Boolean} removable (true|false) special filter default false
10317      */
10318     removable : false,
10319     
10320     /** @cfg {Boolean} grow @hide */
10321     /** @cfg {Number} growMin @hide */
10322     /** @cfg {Number} growMax @hide */
10323
10324     /**
10325      * @hide 
10326      * @method
10327      */
10328     autoSize: Roo.emptyFn,
10329     // private
10330     monitorTab : true,
10331     // private
10332     deferHeight : true,
10333
10334     
10335     actionMode : 'wrap',
10336     
10337     caret : false,
10338     
10339     
10340     getAutoCreate : function(){
10341        
10342         var align = this.labelAlign || this.parentLabelAlign();
10343         
10344         var id = Roo.id();
10345         
10346         var cfg = {
10347             cls: 'form-group' //input-group
10348         };
10349         
10350         
10351         var input =  {
10352             tag: 'input',
10353             id : id,
10354             type : this.inputType,
10355             cls : 'form-control',
10356             autocomplete: 'new-password',
10357             placeholder : this.placeholder || '' 
10358             
10359         };
10360         if (this.name) {
10361             input.name = this.name;
10362         }
10363         if (this.size) {
10364             input.cls += ' input-' + this.size;
10365         }
10366         
10367         if (this.disabled) {
10368             input.disabled=true;
10369         }
10370         
10371         var inputblock = input;
10372         
10373         if(this.hasFeedback && !this.allowBlank){
10374             
10375             var feedback = {
10376                 tag: 'span',
10377                 cls: 'glyphicon form-control-feedback'
10378             };
10379             
10380             if(this.removable && !this.editable && !this.tickable){
10381                 inputblock = {
10382                     cls : 'has-feedback',
10383                     cn :  [
10384                         inputblock,
10385                         {
10386                             tag: 'button',
10387                             html : 'x',
10388                             cls : 'roo-combo-removable-btn close'
10389                         },
10390                         feedback
10391                     ] 
10392                 };
10393             } else {
10394                 inputblock = {
10395                     cls : 'has-feedback',
10396                     cn :  [
10397                         inputblock,
10398                         feedback
10399                     ] 
10400                 };
10401             }
10402
10403         } else {
10404             if(this.removable && !this.editable && !this.tickable){
10405                 inputblock = {
10406                     cls : 'roo-removable',
10407                     cn :  [
10408                         inputblock,
10409                         {
10410                             tag: 'button',
10411                             html : 'x',
10412                             cls : 'roo-combo-removable-btn close'
10413                         }
10414                     ] 
10415                 };
10416             }
10417         }
10418         
10419         if (this.before || this.after) {
10420             
10421             inputblock = {
10422                 cls : 'input-group',
10423                 cn :  [] 
10424             };
10425             if (this.before) {
10426                 inputblock.cn.push({
10427                     tag :'span',
10428                     cls : 'input-group-addon input-group-prepend input-group-text',
10429                     html : this.before
10430                 });
10431             }
10432             
10433             inputblock.cn.push(input);
10434             
10435             if(this.hasFeedback && !this.allowBlank){
10436                 inputblock.cls += ' has-feedback';
10437                 inputblock.cn.push(feedback);
10438             }
10439             
10440             if (this.after) {
10441                 inputblock.cn.push({
10442                     tag :'span',
10443                     cls : 'input-group-addon input-group-append input-group-text',
10444                     html : this.after
10445                 });
10446             }
10447             
10448         };
10449         
10450       
10451         
10452         var ibwrap = inputblock;
10453         
10454         if(this.multiple){
10455             ibwrap = {
10456                 tag: 'ul',
10457                 cls: 'roo-select2-choices',
10458                 cn:[
10459                     {
10460                         tag: 'li',
10461                         cls: 'roo-select2-search-field',
10462                         cn: [
10463
10464                             inputblock
10465                         ]
10466                     }
10467                 ]
10468             };
10469                 
10470         }
10471         
10472         var combobox = {
10473             cls: 'roo-select2-container input-group',
10474             cn: [
10475                  {
10476                     tag: 'input',
10477                     type : 'hidden',
10478                     cls: 'form-hidden-field'
10479                 },
10480                 ibwrap
10481             ]
10482         };
10483         
10484         if(!this.multiple && this.showToggleBtn){
10485             
10486             var caret = {
10487                         tag: 'span',
10488                         cls: 'caret'
10489              };
10490             if (this.caret != false) {
10491                 caret = {
10492                      tag: 'i',
10493                      cls: 'fa fa-' + this.caret
10494                 };
10495                 
10496             }
10497             
10498             combobox.cn.push({
10499                 tag :'span',
10500                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10501                 cn : [
10502                     caret,
10503                     {
10504                         tag: 'span',
10505                         cls: 'combobox-clear',
10506                         cn  : [
10507                             {
10508                                 tag : 'i',
10509                                 cls: 'icon-remove'
10510                             }
10511                         ]
10512                     }
10513                 ]
10514
10515             })
10516         }
10517         
10518         if(this.multiple){
10519             combobox.cls += ' roo-select2-container-multi';
10520         }
10521          var indicator = {
10522             tag : 'i',
10523             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10524             tooltip : 'This field is required'
10525         };
10526         if (Roo.bootstrap.version == 4) {
10527             indicator = {
10528                 tag : 'i',
10529                 style : 'display:none'
10530             };
10531         }
10532         
10533         
10534         if (align ==='left' && this.fieldLabel.length) {
10535             
10536             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10537
10538             cfg.cn = [
10539                 indicator,
10540                 {
10541                     tag: 'label',
10542                     'for' :  id,
10543                     cls : 'control-label',
10544                     html : this.fieldLabel
10545
10546                 },
10547                 {
10548                     cls : "", 
10549                     cn: [
10550                         combobox
10551                     ]
10552                 }
10553
10554             ];
10555             
10556             var labelCfg = cfg.cn[1];
10557             var contentCfg = cfg.cn[2];
10558             
10559             if(this.indicatorpos == 'right'){
10560                 cfg.cn = [
10561                     {
10562                         tag: 'label',
10563                         'for' :  id,
10564                         cls : 'control-label',
10565                         cn : [
10566                             {
10567                                 tag : 'span',
10568                                 html : this.fieldLabel
10569                             },
10570                             indicator
10571                         ]
10572                     },
10573                     {
10574                         cls : "", 
10575                         cn: [
10576                             combobox
10577                         ]
10578                     }
10579
10580                 ];
10581                 
10582                 labelCfg = cfg.cn[0];
10583                 contentCfg = cfg.cn[1];
10584             }
10585             
10586             if(this.labelWidth > 12){
10587                 labelCfg.style = "width: " + this.labelWidth + 'px';
10588             }
10589             
10590             if(this.labelWidth < 13 && this.labelmd == 0){
10591                 this.labelmd = this.labelWidth;
10592             }
10593             
10594             if(this.labellg > 0){
10595                 labelCfg.cls += ' col-lg-' + this.labellg;
10596                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10597             }
10598             
10599             if(this.labelmd > 0){
10600                 labelCfg.cls += ' col-md-' + this.labelmd;
10601                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10602             }
10603             
10604             if(this.labelsm > 0){
10605                 labelCfg.cls += ' col-sm-' + this.labelsm;
10606                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10607             }
10608             
10609             if(this.labelxs > 0){
10610                 labelCfg.cls += ' col-xs-' + this.labelxs;
10611                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10612             }
10613             
10614         } else if ( this.fieldLabel.length) {
10615 //                Roo.log(" label");
10616             cfg.cn = [
10617                 indicator,
10618                {
10619                    tag: 'label',
10620                    //cls : 'input-group-addon',
10621                    html : this.fieldLabel
10622
10623                },
10624
10625                combobox
10626
10627             ];
10628             
10629             if(this.indicatorpos == 'right'){
10630                 
10631                 cfg.cn = [
10632                     {
10633                        tag: 'label',
10634                        cn : [
10635                            {
10636                                tag : 'span',
10637                                html : this.fieldLabel
10638                            },
10639                            indicator
10640                        ]
10641
10642                     },
10643                     combobox
10644
10645                 ];
10646
10647             }
10648
10649         } else {
10650             
10651 //                Roo.log(" no label && no align");
10652                 cfg = combobox
10653                      
10654                 
10655         }
10656         
10657         var settings=this;
10658         ['xs','sm','md','lg'].map(function(size){
10659             if (settings[size]) {
10660                 cfg.cls += ' col-' + size + '-' + settings[size];
10661             }
10662         });
10663         
10664         return cfg;
10665         
10666     },
10667     
10668     
10669     
10670     // private
10671     onResize : function(w, h){
10672 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10673 //        if(typeof w == 'number'){
10674 //            var x = w - this.trigger.getWidth();
10675 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10676 //            this.trigger.setStyle('left', x+'px');
10677 //        }
10678     },
10679
10680     // private
10681     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10682
10683     // private
10684     getResizeEl : function(){
10685         return this.inputEl();
10686     },
10687
10688     // private
10689     getPositionEl : function(){
10690         return this.inputEl();
10691     },
10692
10693     // private
10694     alignErrorIcon : function(){
10695         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10696     },
10697
10698     // private
10699     initEvents : function(){
10700         
10701         this.createList();
10702         
10703         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10704         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10705         if(!this.multiple && this.showToggleBtn){
10706             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10707             if(this.hideTrigger){
10708                 this.trigger.setDisplayed(false);
10709             }
10710             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10711         }
10712         
10713         if(this.multiple){
10714             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10715         }
10716         
10717         if(this.removable && !this.editable && !this.tickable){
10718             var close = this.closeTriggerEl();
10719             
10720             if(close){
10721                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10722                 close.on('click', this.removeBtnClick, this, close);
10723             }
10724         }
10725         
10726         //this.trigger.addClassOnOver('x-form-trigger-over');
10727         //this.trigger.addClassOnClick('x-form-trigger-click');
10728         
10729         //if(!this.width){
10730         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10731         //}
10732     },
10733     
10734     closeTriggerEl : function()
10735     {
10736         var close = this.el.select('.roo-combo-removable-btn', true).first();
10737         return close ? close : false;
10738     },
10739     
10740     removeBtnClick : function(e, h, el)
10741     {
10742         e.preventDefault();
10743         
10744         if(this.fireEvent("remove", this) !== false){
10745             this.reset();
10746             this.fireEvent("afterremove", this)
10747         }
10748     },
10749     
10750     createList : function()
10751     {
10752         this.list = Roo.get(document.body).createChild({
10753             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10754             cls: 'typeahead typeahead-long dropdown-menu',
10755             style: 'display:none'
10756         });
10757         
10758         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10759         
10760     },
10761
10762     // private
10763     initTrigger : function(){
10764        
10765     },
10766
10767     // private
10768     onDestroy : function(){
10769         if(this.trigger){
10770             this.trigger.removeAllListeners();
10771           //  this.trigger.remove();
10772         }
10773         //if(this.wrap){
10774         //    this.wrap.remove();
10775         //}
10776         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10777     },
10778
10779     // private
10780     onFocus : function(){
10781         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10782         /*
10783         if(!this.mimicing){
10784             this.wrap.addClass('x-trigger-wrap-focus');
10785             this.mimicing = true;
10786             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10787             if(this.monitorTab){
10788                 this.el.on("keydown", this.checkTab, this);
10789             }
10790         }
10791         */
10792     },
10793
10794     // private
10795     checkTab : function(e){
10796         if(e.getKey() == e.TAB){
10797             this.triggerBlur();
10798         }
10799     },
10800
10801     // private
10802     onBlur : function(){
10803         // do nothing
10804     },
10805
10806     // private
10807     mimicBlur : function(e, t){
10808         /*
10809         if(!this.wrap.contains(t) && this.validateBlur()){
10810             this.triggerBlur();
10811         }
10812         */
10813     },
10814
10815     // private
10816     triggerBlur : function(){
10817         this.mimicing = false;
10818         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10819         if(this.monitorTab){
10820             this.el.un("keydown", this.checkTab, this);
10821         }
10822         //this.wrap.removeClass('x-trigger-wrap-focus');
10823         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10824     },
10825
10826     // private
10827     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10828     validateBlur : function(e, t){
10829         return true;
10830     },
10831
10832     // private
10833     onDisable : function(){
10834         this.inputEl().dom.disabled = true;
10835         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10836         //if(this.wrap){
10837         //    this.wrap.addClass('x-item-disabled');
10838         //}
10839     },
10840
10841     // private
10842     onEnable : function(){
10843         this.inputEl().dom.disabled = false;
10844         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10845         //if(this.wrap){
10846         //    this.el.removeClass('x-item-disabled');
10847         //}
10848     },
10849
10850     // private
10851     onShow : function(){
10852         var ae = this.getActionEl();
10853         
10854         if(ae){
10855             ae.dom.style.display = '';
10856             ae.dom.style.visibility = 'visible';
10857         }
10858     },
10859
10860     // private
10861     
10862     onHide : function(){
10863         var ae = this.getActionEl();
10864         ae.dom.style.display = 'none';
10865     },
10866
10867     /**
10868      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10869      * by an implementing function.
10870      * @method
10871      * @param {EventObject} e
10872      */
10873     onTriggerClick : Roo.emptyFn
10874 });
10875  /*
10876  * Based on:
10877  * Ext JS Library 1.1.1
10878  * Copyright(c) 2006-2007, Ext JS, LLC.
10879  *
10880  * Originally Released Under LGPL - original licence link has changed is not relivant.
10881  *
10882  * Fork - LGPL
10883  * <script type="text/javascript">
10884  */
10885
10886
10887 /**
10888  * @class Roo.data.SortTypes
10889  * @singleton
10890  * Defines the default sorting (casting?) comparison functions used when sorting data.
10891  */
10892 Roo.data.SortTypes = {
10893     /**
10894      * Default sort that does nothing
10895      * @param {Mixed} s The value being converted
10896      * @return {Mixed} The comparison value
10897      */
10898     none : function(s){
10899         return s;
10900     },
10901     
10902     /**
10903      * The regular expression used to strip tags
10904      * @type {RegExp}
10905      * @property
10906      */
10907     stripTagsRE : /<\/?[^>]+>/gi,
10908     
10909     /**
10910      * Strips all HTML tags to sort on text only
10911      * @param {Mixed} s The value being converted
10912      * @return {String} The comparison value
10913      */
10914     asText : function(s){
10915         return String(s).replace(this.stripTagsRE, "");
10916     },
10917     
10918     /**
10919      * Strips all HTML tags to sort on text only - Case insensitive
10920      * @param {Mixed} s The value being converted
10921      * @return {String} The comparison value
10922      */
10923     asUCText : function(s){
10924         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10925     },
10926     
10927     /**
10928      * Case insensitive string
10929      * @param {Mixed} s The value being converted
10930      * @return {String} The comparison value
10931      */
10932     asUCString : function(s) {
10933         return String(s).toUpperCase();
10934     },
10935     
10936     /**
10937      * Date sorting
10938      * @param {Mixed} s The value being converted
10939      * @return {Number} The comparison value
10940      */
10941     asDate : function(s) {
10942         if(!s){
10943             return 0;
10944         }
10945         if(s instanceof Date){
10946             return s.getTime();
10947         }
10948         return Date.parse(String(s));
10949     },
10950     
10951     /**
10952      * Float sorting
10953      * @param {Mixed} s The value being converted
10954      * @return {Float} The comparison value
10955      */
10956     asFloat : function(s) {
10957         var val = parseFloat(String(s).replace(/,/g, ""));
10958         if(isNaN(val)) {
10959             val = 0;
10960         }
10961         return val;
10962     },
10963     
10964     /**
10965      * Integer sorting
10966      * @param {Mixed} s The value being converted
10967      * @return {Number} The comparison value
10968      */
10969     asInt : function(s) {
10970         var val = parseInt(String(s).replace(/,/g, ""));
10971         if(isNaN(val)) {
10972             val = 0;
10973         }
10974         return val;
10975     }
10976 };/*
10977  * Based on:
10978  * Ext JS Library 1.1.1
10979  * Copyright(c) 2006-2007, Ext JS, LLC.
10980  *
10981  * Originally Released Under LGPL - original licence link has changed is not relivant.
10982  *
10983  * Fork - LGPL
10984  * <script type="text/javascript">
10985  */
10986
10987 /**
10988 * @class Roo.data.Record
10989  * Instances of this class encapsulate both record <em>definition</em> information, and record
10990  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10991  * to access Records cached in an {@link Roo.data.Store} object.<br>
10992  * <p>
10993  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10994  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10995  * objects.<br>
10996  * <p>
10997  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10998  * @constructor
10999  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11000  * {@link #create}. The parameters are the same.
11001  * @param {Array} data An associative Array of data values keyed by the field name.
11002  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11003  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11004  * not specified an integer id is generated.
11005  */
11006 Roo.data.Record = function(data, id){
11007     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11008     this.data = data;
11009 };
11010
11011 /**
11012  * Generate a constructor for a specific record layout.
11013  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11014  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11015  * Each field definition object may contain the following properties: <ul>
11016  * <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,
11017  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11018  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11019  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11020  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11021  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11022  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11023  * this may be omitted.</p></li>
11024  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11025  * <ul><li>auto (Default, implies no conversion)</li>
11026  * <li>string</li>
11027  * <li>int</li>
11028  * <li>float</li>
11029  * <li>boolean</li>
11030  * <li>date</li></ul></p></li>
11031  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11032  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11033  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11034  * by the Reader into an object that will be stored in the Record. It is passed the
11035  * following parameters:<ul>
11036  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11037  * </ul></p></li>
11038  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11039  * </ul>
11040  * <br>usage:<br><pre><code>
11041 var TopicRecord = Roo.data.Record.create(
11042     {name: 'title', mapping: 'topic_title'},
11043     {name: 'author', mapping: 'username'},
11044     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11045     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11046     {name: 'lastPoster', mapping: 'user2'},
11047     {name: 'excerpt', mapping: 'post_text'}
11048 );
11049
11050 var myNewRecord = new TopicRecord({
11051     title: 'Do my job please',
11052     author: 'noobie',
11053     totalPosts: 1,
11054     lastPost: new Date(),
11055     lastPoster: 'Animal',
11056     excerpt: 'No way dude!'
11057 });
11058 myStore.add(myNewRecord);
11059 </code></pre>
11060  * @method create
11061  * @static
11062  */
11063 Roo.data.Record.create = function(o){
11064     var f = function(){
11065         f.superclass.constructor.apply(this, arguments);
11066     };
11067     Roo.extend(f, Roo.data.Record);
11068     var p = f.prototype;
11069     p.fields = new Roo.util.MixedCollection(false, function(field){
11070         return field.name;
11071     });
11072     for(var i = 0, len = o.length; i < len; i++){
11073         p.fields.add(new Roo.data.Field(o[i]));
11074     }
11075     f.getField = function(name){
11076         return p.fields.get(name);  
11077     };
11078     return f;
11079 };
11080
11081 Roo.data.Record.AUTO_ID = 1000;
11082 Roo.data.Record.EDIT = 'edit';
11083 Roo.data.Record.REJECT = 'reject';
11084 Roo.data.Record.COMMIT = 'commit';
11085
11086 Roo.data.Record.prototype = {
11087     /**
11088      * Readonly flag - true if this record has been modified.
11089      * @type Boolean
11090      */
11091     dirty : false,
11092     editing : false,
11093     error: null,
11094     modified: null,
11095
11096     // private
11097     join : function(store){
11098         this.store = store;
11099     },
11100
11101     /**
11102      * Set the named field to the specified value.
11103      * @param {String} name The name of the field to set.
11104      * @param {Object} value The value to set the field to.
11105      */
11106     set : function(name, value){
11107         if(this.data[name] == value){
11108             return;
11109         }
11110         this.dirty = true;
11111         if(!this.modified){
11112             this.modified = {};
11113         }
11114         if(typeof this.modified[name] == 'undefined'){
11115             this.modified[name] = this.data[name];
11116         }
11117         this.data[name] = value;
11118         if(!this.editing && this.store){
11119             this.store.afterEdit(this);
11120         }       
11121     },
11122
11123     /**
11124      * Get the value of the named field.
11125      * @param {String} name The name of the field to get the value of.
11126      * @return {Object} The value of the field.
11127      */
11128     get : function(name){
11129         return this.data[name]; 
11130     },
11131
11132     // private
11133     beginEdit : function(){
11134         this.editing = true;
11135         this.modified = {}; 
11136     },
11137
11138     // private
11139     cancelEdit : function(){
11140         this.editing = false;
11141         delete this.modified;
11142     },
11143
11144     // private
11145     endEdit : function(){
11146         this.editing = false;
11147         if(this.dirty && this.store){
11148             this.store.afterEdit(this);
11149         }
11150     },
11151
11152     /**
11153      * Usually called by the {@link Roo.data.Store} which owns the Record.
11154      * Rejects all changes made to the Record since either creation, or the last commit operation.
11155      * Modified fields are reverted to their original values.
11156      * <p>
11157      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11158      * of reject operations.
11159      */
11160     reject : function(){
11161         var m = this.modified;
11162         for(var n in m){
11163             if(typeof m[n] != "function"){
11164                 this.data[n] = m[n];
11165             }
11166         }
11167         this.dirty = false;
11168         delete this.modified;
11169         this.editing = false;
11170         if(this.store){
11171             this.store.afterReject(this);
11172         }
11173     },
11174
11175     /**
11176      * Usually called by the {@link Roo.data.Store} which owns the Record.
11177      * Commits all changes made to the Record since either creation, or the last commit operation.
11178      * <p>
11179      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11180      * of commit operations.
11181      */
11182     commit : function(){
11183         this.dirty = false;
11184         delete this.modified;
11185         this.editing = false;
11186         if(this.store){
11187             this.store.afterCommit(this);
11188         }
11189     },
11190
11191     // private
11192     hasError : function(){
11193         return this.error != null;
11194     },
11195
11196     // private
11197     clearError : function(){
11198         this.error = null;
11199     },
11200
11201     /**
11202      * Creates a copy of this record.
11203      * @param {String} id (optional) A new record id if you don't want to use this record's id
11204      * @return {Record}
11205      */
11206     copy : function(newId) {
11207         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11208     }
11209 };/*
11210  * Based on:
11211  * Ext JS Library 1.1.1
11212  * Copyright(c) 2006-2007, Ext JS, LLC.
11213  *
11214  * Originally Released Under LGPL - original licence link has changed is not relivant.
11215  *
11216  * Fork - LGPL
11217  * <script type="text/javascript">
11218  */
11219
11220
11221
11222 /**
11223  * @class Roo.data.Store
11224  * @extends Roo.util.Observable
11225  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11226  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11227  * <p>
11228  * 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
11229  * has no knowledge of the format of the data returned by the Proxy.<br>
11230  * <p>
11231  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11232  * instances from the data object. These records are cached and made available through accessor functions.
11233  * @constructor
11234  * Creates a new Store.
11235  * @param {Object} config A config object containing the objects needed for the Store to access data,
11236  * and read the data into Records.
11237  */
11238 Roo.data.Store = function(config){
11239     this.data = new Roo.util.MixedCollection(false);
11240     this.data.getKey = function(o){
11241         return o.id;
11242     };
11243     this.baseParams = {};
11244     // private
11245     this.paramNames = {
11246         "start" : "start",
11247         "limit" : "limit",
11248         "sort" : "sort",
11249         "dir" : "dir",
11250         "multisort" : "_multisort"
11251     };
11252
11253     if(config && config.data){
11254         this.inlineData = config.data;
11255         delete config.data;
11256     }
11257
11258     Roo.apply(this, config);
11259     
11260     if(this.reader){ // reader passed
11261         this.reader = Roo.factory(this.reader, Roo.data);
11262         this.reader.xmodule = this.xmodule || false;
11263         if(!this.recordType){
11264             this.recordType = this.reader.recordType;
11265         }
11266         if(this.reader.onMetaChange){
11267             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11268         }
11269     }
11270
11271     if(this.recordType){
11272         this.fields = this.recordType.prototype.fields;
11273     }
11274     this.modified = [];
11275
11276     this.addEvents({
11277         /**
11278          * @event datachanged
11279          * Fires when the data cache has changed, and a widget which is using this Store
11280          * as a Record cache should refresh its view.
11281          * @param {Store} this
11282          */
11283         datachanged : true,
11284         /**
11285          * @event metachange
11286          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11287          * @param {Store} this
11288          * @param {Object} meta The JSON metadata
11289          */
11290         metachange : true,
11291         /**
11292          * @event add
11293          * Fires when Records have been added to the Store
11294          * @param {Store} this
11295          * @param {Roo.data.Record[]} records The array of Records added
11296          * @param {Number} index The index at which the record(s) were added
11297          */
11298         add : true,
11299         /**
11300          * @event remove
11301          * Fires when a Record has been removed from the Store
11302          * @param {Store} this
11303          * @param {Roo.data.Record} record The Record that was removed
11304          * @param {Number} index The index at which the record was removed
11305          */
11306         remove : true,
11307         /**
11308          * @event update
11309          * Fires when a Record has been updated
11310          * @param {Store} this
11311          * @param {Roo.data.Record} record The Record that was updated
11312          * @param {String} operation The update operation being performed.  Value may be one of:
11313          * <pre><code>
11314  Roo.data.Record.EDIT
11315  Roo.data.Record.REJECT
11316  Roo.data.Record.COMMIT
11317          * </code></pre>
11318          */
11319         update : true,
11320         /**
11321          * @event clear
11322          * Fires when the data cache has been cleared.
11323          * @param {Store} this
11324          */
11325         clear : true,
11326         /**
11327          * @event beforeload
11328          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11329          * the load action will be canceled.
11330          * @param {Store} this
11331          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11332          */
11333         beforeload : true,
11334         /**
11335          * @event beforeloadadd
11336          * Fires after a new set of Records has been loaded.
11337          * @param {Store} this
11338          * @param {Roo.data.Record[]} records The Records that were loaded
11339          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11340          */
11341         beforeloadadd : true,
11342         /**
11343          * @event load
11344          * Fires after a new set of Records has been loaded, before they are added to the store.
11345          * @param {Store} this
11346          * @param {Roo.data.Record[]} records The Records that were loaded
11347          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11348          * @params {Object} return from reader
11349          */
11350         load : true,
11351         /**
11352          * @event loadexception
11353          * Fires if an exception occurs in the Proxy during loading.
11354          * Called with the signature of the Proxy's "loadexception" event.
11355          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11356          * 
11357          * @param {Proxy} 
11358          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11359          * @param {Object} load options 
11360          * @param {Object} jsonData from your request (normally this contains the Exception)
11361          */
11362         loadexception : true
11363     });
11364     
11365     if(this.proxy){
11366         this.proxy = Roo.factory(this.proxy, Roo.data);
11367         this.proxy.xmodule = this.xmodule || false;
11368         this.relayEvents(this.proxy,  ["loadexception"]);
11369     }
11370     this.sortToggle = {};
11371     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11372
11373     Roo.data.Store.superclass.constructor.call(this);
11374
11375     if(this.inlineData){
11376         this.loadData(this.inlineData);
11377         delete this.inlineData;
11378     }
11379 };
11380
11381 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11382      /**
11383     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11384     * without a remote query - used by combo/forms at present.
11385     */
11386     
11387     /**
11388     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11389     */
11390     /**
11391     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11392     */
11393     /**
11394     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11395     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11396     */
11397     /**
11398     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11399     * on any HTTP request
11400     */
11401     /**
11402     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11403     */
11404     /**
11405     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11406     */
11407     multiSort: false,
11408     /**
11409     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11410     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11411     */
11412     remoteSort : false,
11413
11414     /**
11415     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11416      * loaded or when a record is removed. (defaults to false).
11417     */
11418     pruneModifiedRecords : false,
11419
11420     // private
11421     lastOptions : null,
11422
11423     /**
11424      * Add Records to the Store and fires the add event.
11425      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11426      */
11427     add : function(records){
11428         records = [].concat(records);
11429         for(var i = 0, len = records.length; i < len; i++){
11430             records[i].join(this);
11431         }
11432         var index = this.data.length;
11433         this.data.addAll(records);
11434         this.fireEvent("add", this, records, index);
11435     },
11436
11437     /**
11438      * Remove a Record from the Store and fires the remove event.
11439      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11440      */
11441     remove : function(record){
11442         var index = this.data.indexOf(record);
11443         this.data.removeAt(index);
11444  
11445         if(this.pruneModifiedRecords){
11446             this.modified.remove(record);
11447         }
11448         this.fireEvent("remove", this, record, index);
11449     },
11450
11451     /**
11452      * Remove all Records from the Store and fires the clear event.
11453      */
11454     removeAll : function(){
11455         this.data.clear();
11456         if(this.pruneModifiedRecords){
11457             this.modified = [];
11458         }
11459         this.fireEvent("clear", this);
11460     },
11461
11462     /**
11463      * Inserts Records to the Store at the given index and fires the add event.
11464      * @param {Number} index The start index at which to insert the passed Records.
11465      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11466      */
11467     insert : function(index, records){
11468         records = [].concat(records);
11469         for(var i = 0, len = records.length; i < len; i++){
11470             this.data.insert(index, records[i]);
11471             records[i].join(this);
11472         }
11473         this.fireEvent("add", this, records, index);
11474     },
11475
11476     /**
11477      * Get the index within the cache of the passed Record.
11478      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11479      * @return {Number} The index of the passed Record. Returns -1 if not found.
11480      */
11481     indexOf : function(record){
11482         return this.data.indexOf(record);
11483     },
11484
11485     /**
11486      * Get the index within the cache of the Record with the passed id.
11487      * @param {String} id The id of the Record to find.
11488      * @return {Number} The index of the Record. Returns -1 if not found.
11489      */
11490     indexOfId : function(id){
11491         return this.data.indexOfKey(id);
11492     },
11493
11494     /**
11495      * Get the Record with the specified id.
11496      * @param {String} id The id of the Record to find.
11497      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11498      */
11499     getById : function(id){
11500         return this.data.key(id);
11501     },
11502
11503     /**
11504      * Get the Record at the specified index.
11505      * @param {Number} index The index of the Record to find.
11506      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11507      */
11508     getAt : function(index){
11509         return this.data.itemAt(index);
11510     },
11511
11512     /**
11513      * Returns a range of Records between specified indices.
11514      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11515      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11516      * @return {Roo.data.Record[]} An array of Records
11517      */
11518     getRange : function(start, end){
11519         return this.data.getRange(start, end);
11520     },
11521
11522     // private
11523     storeOptions : function(o){
11524         o = Roo.apply({}, o);
11525         delete o.callback;
11526         delete o.scope;
11527         this.lastOptions = o;
11528     },
11529
11530     /**
11531      * Loads the Record cache from the configured Proxy using the configured Reader.
11532      * <p>
11533      * If using remote paging, then the first load call must specify the <em>start</em>
11534      * and <em>limit</em> properties in the options.params property to establish the initial
11535      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11536      * <p>
11537      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11538      * and this call will return before the new data has been loaded. Perform any post-processing
11539      * in a callback function, or in a "load" event handler.</strong>
11540      * <p>
11541      * @param {Object} options An object containing properties which control loading options:<ul>
11542      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11543      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11544      * passed the following arguments:<ul>
11545      * <li>r : Roo.data.Record[]</li>
11546      * <li>options: Options object from the load call</li>
11547      * <li>success: Boolean success indicator</li></ul></li>
11548      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11549      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11550      * </ul>
11551      */
11552     load : function(options){
11553         options = options || {};
11554         if(this.fireEvent("beforeload", this, options) !== false){
11555             this.storeOptions(options);
11556             var p = Roo.apply(options.params || {}, this.baseParams);
11557             // if meta was not loaded from remote source.. try requesting it.
11558             if (!this.reader.metaFromRemote) {
11559                 p._requestMeta = 1;
11560             }
11561             if(this.sortInfo && this.remoteSort){
11562                 var pn = this.paramNames;
11563                 p[pn["sort"]] = this.sortInfo.field;
11564                 p[pn["dir"]] = this.sortInfo.direction;
11565             }
11566             if (this.multiSort) {
11567                 var pn = this.paramNames;
11568                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11569             }
11570             
11571             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11572         }
11573     },
11574
11575     /**
11576      * Reloads the Record cache from the configured Proxy using the configured Reader and
11577      * the options from the last load operation performed.
11578      * @param {Object} options (optional) An object containing properties which may override the options
11579      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11580      * the most recently used options are reused).
11581      */
11582     reload : function(options){
11583         this.load(Roo.applyIf(options||{}, this.lastOptions));
11584     },
11585
11586     // private
11587     // Called as a callback by the Reader during a load operation.
11588     loadRecords : function(o, options, success){
11589         if(!o || success === false){
11590             if(success !== false){
11591                 this.fireEvent("load", this, [], options, o);
11592             }
11593             if(options.callback){
11594                 options.callback.call(options.scope || this, [], options, false);
11595             }
11596             return;
11597         }
11598         // if data returned failure - throw an exception.
11599         if (o.success === false) {
11600             // show a message if no listener is registered.
11601             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11602                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11603             }
11604             // loadmask wil be hooked into this..
11605             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11606             return;
11607         }
11608         var r = o.records, t = o.totalRecords || r.length;
11609         
11610         this.fireEvent("beforeloadadd", this, r, options, o);
11611         
11612         if(!options || options.add !== true){
11613             if(this.pruneModifiedRecords){
11614                 this.modified = [];
11615             }
11616             for(var i = 0, len = r.length; i < len; i++){
11617                 r[i].join(this);
11618             }
11619             if(this.snapshot){
11620                 this.data = this.snapshot;
11621                 delete this.snapshot;
11622             }
11623             this.data.clear();
11624             this.data.addAll(r);
11625             this.totalLength = t;
11626             this.applySort();
11627             this.fireEvent("datachanged", this);
11628         }else{
11629             this.totalLength = Math.max(t, this.data.length+r.length);
11630             this.add(r);
11631         }
11632         
11633         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11634                 
11635             var e = new Roo.data.Record({});
11636
11637             e.set(this.parent.displayField, this.parent.emptyTitle);
11638             e.set(this.parent.valueField, '');
11639
11640             this.insert(0, e);
11641         }
11642             
11643         this.fireEvent("load", this, r, options, o);
11644         if(options.callback){
11645             options.callback.call(options.scope || this, r, options, true);
11646         }
11647     },
11648
11649
11650     /**
11651      * Loads data from a passed data block. A Reader which understands the format of the data
11652      * must have been configured in the constructor.
11653      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11654      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11655      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11656      */
11657     loadData : function(o, append){
11658         var r = this.reader.readRecords(o);
11659         this.loadRecords(r, {add: append}, true);
11660     },
11661
11662     /**
11663      * Gets the number of cached records.
11664      * <p>
11665      * <em>If using paging, this may not be the total size of the dataset. If the data object
11666      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11667      * the data set size</em>
11668      */
11669     getCount : function(){
11670         return this.data.length || 0;
11671     },
11672
11673     /**
11674      * Gets the total number of records in the dataset as returned by the server.
11675      * <p>
11676      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11677      * the dataset size</em>
11678      */
11679     getTotalCount : function(){
11680         return this.totalLength || 0;
11681     },
11682
11683     /**
11684      * Returns the sort state of the Store as an object with two properties:
11685      * <pre><code>
11686  field {String} The name of the field by which the Records are sorted
11687  direction {String} The sort order, "ASC" or "DESC"
11688      * </code></pre>
11689      */
11690     getSortState : function(){
11691         return this.sortInfo;
11692     },
11693
11694     // private
11695     applySort : function(){
11696         if(this.sortInfo && !this.remoteSort){
11697             var s = this.sortInfo, f = s.field;
11698             var st = this.fields.get(f).sortType;
11699             var fn = function(r1, r2){
11700                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11701                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11702             };
11703             this.data.sort(s.direction, fn);
11704             if(this.snapshot && this.snapshot != this.data){
11705                 this.snapshot.sort(s.direction, fn);
11706             }
11707         }
11708     },
11709
11710     /**
11711      * Sets the default sort column and order to be used by the next load operation.
11712      * @param {String} fieldName The name of the field to sort by.
11713      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11714      */
11715     setDefaultSort : function(field, dir){
11716         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11717     },
11718
11719     /**
11720      * Sort the Records.
11721      * If remote sorting is used, the sort is performed on the server, and the cache is
11722      * reloaded. If local sorting is used, the cache is sorted internally.
11723      * @param {String} fieldName The name of the field to sort by.
11724      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11725      */
11726     sort : function(fieldName, dir){
11727         var f = this.fields.get(fieldName);
11728         if(!dir){
11729             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11730             
11731             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11732                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11733             }else{
11734                 dir = f.sortDir;
11735             }
11736         }
11737         this.sortToggle[f.name] = dir;
11738         this.sortInfo = {field: f.name, direction: dir};
11739         if(!this.remoteSort){
11740             this.applySort();
11741             this.fireEvent("datachanged", this);
11742         }else{
11743             this.load(this.lastOptions);
11744         }
11745     },
11746
11747     /**
11748      * Calls the specified function for each of the Records in the cache.
11749      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11750      * Returning <em>false</em> aborts and exits the iteration.
11751      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11752      */
11753     each : function(fn, scope){
11754         this.data.each(fn, scope);
11755     },
11756
11757     /**
11758      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11759      * (e.g., during paging).
11760      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11761      */
11762     getModifiedRecords : function(){
11763         return this.modified;
11764     },
11765
11766     // private
11767     createFilterFn : function(property, value, anyMatch){
11768         if(!value.exec){ // not a regex
11769             value = String(value);
11770             if(value.length == 0){
11771                 return false;
11772             }
11773             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11774         }
11775         return function(r){
11776             return value.test(r.data[property]);
11777         };
11778     },
11779
11780     /**
11781      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11782      * @param {String} property A field on your records
11783      * @param {Number} start The record index to start at (defaults to 0)
11784      * @param {Number} end The last record index to include (defaults to length - 1)
11785      * @return {Number} The sum
11786      */
11787     sum : function(property, start, end){
11788         var rs = this.data.items, v = 0;
11789         start = start || 0;
11790         end = (end || end === 0) ? end : rs.length-1;
11791
11792         for(var i = start; i <= end; i++){
11793             v += (rs[i].data[property] || 0);
11794         }
11795         return v;
11796     },
11797
11798     /**
11799      * Filter the records by a specified property.
11800      * @param {String} field A field on your records
11801      * @param {String/RegExp} value Either a string that the field
11802      * should start with or a RegExp to test against the field
11803      * @param {Boolean} anyMatch True to match any part not just the beginning
11804      */
11805     filter : function(property, value, anyMatch){
11806         var fn = this.createFilterFn(property, value, anyMatch);
11807         return fn ? this.filterBy(fn) : this.clearFilter();
11808     },
11809
11810     /**
11811      * Filter by a function. The specified function will be called with each
11812      * record in this data source. If the function returns true the record is included,
11813      * otherwise it is filtered.
11814      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11815      * @param {Object} scope (optional) The scope of the function (defaults to this)
11816      */
11817     filterBy : function(fn, scope){
11818         this.snapshot = this.snapshot || this.data;
11819         this.data = this.queryBy(fn, scope||this);
11820         this.fireEvent("datachanged", this);
11821     },
11822
11823     /**
11824      * Query the records by a specified property.
11825      * @param {String} field A field on your records
11826      * @param {String/RegExp} value Either a string that the field
11827      * should start with or a RegExp to test against the field
11828      * @param {Boolean} anyMatch True to match any part not just the beginning
11829      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11830      */
11831     query : function(property, value, anyMatch){
11832         var fn = this.createFilterFn(property, value, anyMatch);
11833         return fn ? this.queryBy(fn) : this.data.clone();
11834     },
11835
11836     /**
11837      * Query by a function. The specified function will be called with each
11838      * record in this data source. If the function returns true the record is included
11839      * in the results.
11840      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11841      * @param {Object} scope (optional) The scope of the function (defaults to this)
11842       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11843      **/
11844     queryBy : function(fn, scope){
11845         var data = this.snapshot || this.data;
11846         return data.filterBy(fn, scope||this);
11847     },
11848
11849     /**
11850      * Collects unique values for a particular dataIndex from this store.
11851      * @param {String} dataIndex The property to collect
11852      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11853      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11854      * @return {Array} An array of the unique values
11855      **/
11856     collect : function(dataIndex, allowNull, bypassFilter){
11857         var d = (bypassFilter === true && this.snapshot) ?
11858                 this.snapshot.items : this.data.items;
11859         var v, sv, r = [], l = {};
11860         for(var i = 0, len = d.length; i < len; i++){
11861             v = d[i].data[dataIndex];
11862             sv = String(v);
11863             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11864                 l[sv] = true;
11865                 r[r.length] = v;
11866             }
11867         }
11868         return r;
11869     },
11870
11871     /**
11872      * Revert to a view of the Record cache with no filtering applied.
11873      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11874      */
11875     clearFilter : function(suppressEvent){
11876         if(this.snapshot && this.snapshot != this.data){
11877             this.data = this.snapshot;
11878             delete this.snapshot;
11879             if(suppressEvent !== true){
11880                 this.fireEvent("datachanged", this);
11881             }
11882         }
11883     },
11884
11885     // private
11886     afterEdit : function(record){
11887         if(this.modified.indexOf(record) == -1){
11888             this.modified.push(record);
11889         }
11890         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11891     },
11892     
11893     // private
11894     afterReject : function(record){
11895         this.modified.remove(record);
11896         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11897     },
11898
11899     // private
11900     afterCommit : function(record){
11901         this.modified.remove(record);
11902         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11903     },
11904
11905     /**
11906      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11907      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11908      */
11909     commitChanges : function(){
11910         var m = this.modified.slice(0);
11911         this.modified = [];
11912         for(var i = 0, len = m.length; i < len; i++){
11913             m[i].commit();
11914         }
11915     },
11916
11917     /**
11918      * Cancel outstanding changes on all changed records.
11919      */
11920     rejectChanges : function(){
11921         var m = this.modified.slice(0);
11922         this.modified = [];
11923         for(var i = 0, len = m.length; i < len; i++){
11924             m[i].reject();
11925         }
11926     },
11927
11928     onMetaChange : function(meta, rtype, o){
11929         this.recordType = rtype;
11930         this.fields = rtype.prototype.fields;
11931         delete this.snapshot;
11932         this.sortInfo = meta.sortInfo || this.sortInfo;
11933         this.modified = [];
11934         this.fireEvent('metachange', this, this.reader.meta);
11935     },
11936     
11937     moveIndex : function(data, type)
11938     {
11939         var index = this.indexOf(data);
11940         
11941         var newIndex = index + type;
11942         
11943         this.remove(data);
11944         
11945         this.insert(newIndex, data);
11946         
11947     }
11948 });/*
11949  * Based on:
11950  * Ext JS Library 1.1.1
11951  * Copyright(c) 2006-2007, Ext JS, LLC.
11952  *
11953  * Originally Released Under LGPL - original licence link has changed is not relivant.
11954  *
11955  * Fork - LGPL
11956  * <script type="text/javascript">
11957  */
11958
11959 /**
11960  * @class Roo.data.SimpleStore
11961  * @extends Roo.data.Store
11962  * Small helper class to make creating Stores from Array data easier.
11963  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11964  * @cfg {Array} fields An array of field definition objects, or field name strings.
11965  * @cfg {Array} data The multi-dimensional array of data
11966  * @constructor
11967  * @param {Object} config
11968  */
11969 Roo.data.SimpleStore = function(config){
11970     Roo.data.SimpleStore.superclass.constructor.call(this, {
11971         isLocal : true,
11972         reader: new Roo.data.ArrayReader({
11973                 id: config.id
11974             },
11975             Roo.data.Record.create(config.fields)
11976         ),
11977         proxy : new Roo.data.MemoryProxy(config.data)
11978     });
11979     this.load();
11980 };
11981 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11982  * Based on:
11983  * Ext JS Library 1.1.1
11984  * Copyright(c) 2006-2007, Ext JS, LLC.
11985  *
11986  * Originally Released Under LGPL - original licence link has changed is not relivant.
11987  *
11988  * Fork - LGPL
11989  * <script type="text/javascript">
11990  */
11991
11992 /**
11993 /**
11994  * @extends Roo.data.Store
11995  * @class Roo.data.JsonStore
11996  * Small helper class to make creating Stores for JSON data easier. <br/>
11997 <pre><code>
11998 var store = new Roo.data.JsonStore({
11999     url: 'get-images.php',
12000     root: 'images',
12001     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12002 });
12003 </code></pre>
12004  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12005  * JsonReader and HttpProxy (unless inline data is provided).</b>
12006  * @cfg {Array} fields An array of field definition objects, or field name strings.
12007  * @constructor
12008  * @param {Object} config
12009  */
12010 Roo.data.JsonStore = function(c){
12011     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12012         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12013         reader: new Roo.data.JsonReader(c, c.fields)
12014     }));
12015 };
12016 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12017  * Based on:
12018  * Ext JS Library 1.1.1
12019  * Copyright(c) 2006-2007, Ext JS, LLC.
12020  *
12021  * Originally Released Under LGPL - original licence link has changed is not relivant.
12022  *
12023  * Fork - LGPL
12024  * <script type="text/javascript">
12025  */
12026
12027  
12028 Roo.data.Field = function(config){
12029     if(typeof config == "string"){
12030         config = {name: config};
12031     }
12032     Roo.apply(this, config);
12033     
12034     if(!this.type){
12035         this.type = "auto";
12036     }
12037     
12038     var st = Roo.data.SortTypes;
12039     // named sortTypes are supported, here we look them up
12040     if(typeof this.sortType == "string"){
12041         this.sortType = st[this.sortType];
12042     }
12043     
12044     // set default sortType for strings and dates
12045     if(!this.sortType){
12046         switch(this.type){
12047             case "string":
12048                 this.sortType = st.asUCString;
12049                 break;
12050             case "date":
12051                 this.sortType = st.asDate;
12052                 break;
12053             default:
12054                 this.sortType = st.none;
12055         }
12056     }
12057
12058     // define once
12059     var stripRe = /[\$,%]/g;
12060
12061     // prebuilt conversion function for this field, instead of
12062     // switching every time we're reading a value
12063     if(!this.convert){
12064         var cv, dateFormat = this.dateFormat;
12065         switch(this.type){
12066             case "":
12067             case "auto":
12068             case undefined:
12069                 cv = function(v){ return v; };
12070                 break;
12071             case "string":
12072                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12073                 break;
12074             case "int":
12075                 cv = function(v){
12076                     return v !== undefined && v !== null && v !== '' ?
12077                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12078                     };
12079                 break;
12080             case "float":
12081                 cv = function(v){
12082                     return v !== undefined && v !== null && v !== '' ?
12083                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12084                     };
12085                 break;
12086             case "bool":
12087             case "boolean":
12088                 cv = function(v){ return v === true || v === "true" || v == 1; };
12089                 break;
12090             case "date":
12091                 cv = function(v){
12092                     if(!v){
12093                         return '';
12094                     }
12095                     if(v instanceof Date){
12096                         return v;
12097                     }
12098                     if(dateFormat){
12099                         if(dateFormat == "timestamp"){
12100                             return new Date(v*1000);
12101                         }
12102                         return Date.parseDate(v, dateFormat);
12103                     }
12104                     var parsed = Date.parse(v);
12105                     return parsed ? new Date(parsed) : null;
12106                 };
12107              break;
12108             
12109         }
12110         this.convert = cv;
12111     }
12112 };
12113
12114 Roo.data.Field.prototype = {
12115     dateFormat: null,
12116     defaultValue: "",
12117     mapping: null,
12118     sortType : null,
12119     sortDir : "ASC"
12120 };/*
12121  * Based on:
12122  * Ext JS Library 1.1.1
12123  * Copyright(c) 2006-2007, Ext JS, LLC.
12124  *
12125  * Originally Released Under LGPL - original licence link has changed is not relivant.
12126  *
12127  * Fork - LGPL
12128  * <script type="text/javascript">
12129  */
12130  
12131 // Base class for reading structured data from a data source.  This class is intended to be
12132 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12133
12134 /**
12135  * @class Roo.data.DataReader
12136  * Base class for reading structured data from a data source.  This class is intended to be
12137  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12138  */
12139
12140 Roo.data.DataReader = function(meta, recordType){
12141     
12142     this.meta = meta;
12143     
12144     this.recordType = recordType instanceof Array ? 
12145         Roo.data.Record.create(recordType) : recordType;
12146 };
12147
12148 Roo.data.DataReader.prototype = {
12149      /**
12150      * Create an empty record
12151      * @param {Object} data (optional) - overlay some values
12152      * @return {Roo.data.Record} record created.
12153      */
12154     newRow :  function(d) {
12155         var da =  {};
12156         this.recordType.prototype.fields.each(function(c) {
12157             switch( c.type) {
12158                 case 'int' : da[c.name] = 0; break;
12159                 case 'date' : da[c.name] = new Date(); break;
12160                 case 'float' : da[c.name] = 0.0; break;
12161                 case 'boolean' : da[c.name] = false; break;
12162                 default : da[c.name] = ""; break;
12163             }
12164             
12165         });
12166         return new this.recordType(Roo.apply(da, d));
12167     }
12168     
12169 };/*
12170  * Based on:
12171  * Ext JS Library 1.1.1
12172  * Copyright(c) 2006-2007, Ext JS, LLC.
12173  *
12174  * Originally Released Under LGPL - original licence link has changed is not relivant.
12175  *
12176  * Fork - LGPL
12177  * <script type="text/javascript">
12178  */
12179
12180 /**
12181  * @class Roo.data.DataProxy
12182  * @extends Roo.data.Observable
12183  * This class is an abstract base class for implementations which provide retrieval of
12184  * unformatted data objects.<br>
12185  * <p>
12186  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12187  * (of the appropriate type which knows how to parse the data object) to provide a block of
12188  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12189  * <p>
12190  * Custom implementations must implement the load method as described in
12191  * {@link Roo.data.HttpProxy#load}.
12192  */
12193 Roo.data.DataProxy = function(){
12194     this.addEvents({
12195         /**
12196          * @event beforeload
12197          * Fires before a network request is made to retrieve a data object.
12198          * @param {Object} This DataProxy object.
12199          * @param {Object} params The params parameter to the load function.
12200          */
12201         beforeload : true,
12202         /**
12203          * @event load
12204          * Fires before the load method's callback is called.
12205          * @param {Object} This DataProxy object.
12206          * @param {Object} o The data object.
12207          * @param {Object} arg The callback argument object passed to the load function.
12208          */
12209         load : true,
12210         /**
12211          * @event loadexception
12212          * Fires if an Exception occurs during data retrieval.
12213          * @param {Object} This DataProxy object.
12214          * @param {Object} o The data object.
12215          * @param {Object} arg The callback argument object passed to the load function.
12216          * @param {Object} e The Exception.
12217          */
12218         loadexception : true
12219     });
12220     Roo.data.DataProxy.superclass.constructor.call(this);
12221 };
12222
12223 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12224
12225     /**
12226      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12227      */
12228 /*
12229  * Based on:
12230  * Ext JS Library 1.1.1
12231  * Copyright(c) 2006-2007, Ext JS, LLC.
12232  *
12233  * Originally Released Under LGPL - original licence link has changed is not relivant.
12234  *
12235  * Fork - LGPL
12236  * <script type="text/javascript">
12237  */
12238 /**
12239  * @class Roo.data.MemoryProxy
12240  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12241  * to the Reader when its load method is called.
12242  * @constructor
12243  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12244  */
12245 Roo.data.MemoryProxy = function(data){
12246     if (data.data) {
12247         data = data.data;
12248     }
12249     Roo.data.MemoryProxy.superclass.constructor.call(this);
12250     this.data = data;
12251 };
12252
12253 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12254     
12255     /**
12256      * Load data from the requested source (in this case an in-memory
12257      * data object passed to the constructor), read the data object into
12258      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12259      * process that block using the passed callback.
12260      * @param {Object} params This parameter is not used by the MemoryProxy class.
12261      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12262      * object into a block of Roo.data.Records.
12263      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12264      * The function must be passed <ul>
12265      * <li>The Record block object</li>
12266      * <li>The "arg" argument from the load function</li>
12267      * <li>A boolean success indicator</li>
12268      * </ul>
12269      * @param {Object} scope The scope in which to call the callback
12270      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12271      */
12272     load : function(params, reader, callback, scope, arg){
12273         params = params || {};
12274         var result;
12275         try {
12276             result = reader.readRecords(this.data);
12277         }catch(e){
12278             this.fireEvent("loadexception", this, arg, null, e);
12279             callback.call(scope, null, arg, false);
12280             return;
12281         }
12282         callback.call(scope, result, arg, true);
12283     },
12284     
12285     // private
12286     update : function(params, records){
12287         
12288     }
12289 });/*
12290  * Based on:
12291  * Ext JS Library 1.1.1
12292  * Copyright(c) 2006-2007, Ext JS, LLC.
12293  *
12294  * Originally Released Under LGPL - original licence link has changed is not relivant.
12295  *
12296  * Fork - LGPL
12297  * <script type="text/javascript">
12298  */
12299 /**
12300  * @class Roo.data.HttpProxy
12301  * @extends Roo.data.DataProxy
12302  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12303  * configured to reference a certain URL.<br><br>
12304  * <p>
12305  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12306  * from which the running page was served.<br><br>
12307  * <p>
12308  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12309  * <p>
12310  * Be aware that to enable the browser to parse an XML document, the server must set
12311  * the Content-Type header in the HTTP response to "text/xml".
12312  * @constructor
12313  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12314  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12315  * will be used to make the request.
12316  */
12317 Roo.data.HttpProxy = function(conn){
12318     Roo.data.HttpProxy.superclass.constructor.call(this);
12319     // is conn a conn config or a real conn?
12320     this.conn = conn;
12321     this.useAjax = !conn || !conn.events;
12322   
12323 };
12324
12325 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12326     // thse are take from connection...
12327     
12328     /**
12329      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12330      */
12331     /**
12332      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12333      * extra parameters to each request made by this object. (defaults to undefined)
12334      */
12335     /**
12336      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12337      *  to each request made by this object. (defaults to undefined)
12338      */
12339     /**
12340      * @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)
12341      */
12342     /**
12343      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12344      */
12345      /**
12346      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12347      * @type Boolean
12348      */
12349   
12350
12351     /**
12352      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12353      * @type Boolean
12354      */
12355     /**
12356      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12357      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12358      * a finer-grained basis than the DataProxy events.
12359      */
12360     getConnection : function(){
12361         return this.useAjax ? Roo.Ajax : this.conn;
12362     },
12363
12364     /**
12365      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12366      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12367      * process that block using the passed callback.
12368      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12369      * for the request to the remote server.
12370      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12371      * object into a block of Roo.data.Records.
12372      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12373      * The function must be passed <ul>
12374      * <li>The Record block object</li>
12375      * <li>The "arg" argument from the load function</li>
12376      * <li>A boolean success indicator</li>
12377      * </ul>
12378      * @param {Object} scope The scope in which to call the callback
12379      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12380      */
12381     load : function(params, reader, callback, scope, arg){
12382         if(this.fireEvent("beforeload", this, params) !== false){
12383             var  o = {
12384                 params : params || {},
12385                 request: {
12386                     callback : callback,
12387                     scope : scope,
12388                     arg : arg
12389                 },
12390                 reader: reader,
12391                 callback : this.loadResponse,
12392                 scope: this
12393             };
12394             if(this.useAjax){
12395                 Roo.applyIf(o, this.conn);
12396                 if(this.activeRequest){
12397                     Roo.Ajax.abort(this.activeRequest);
12398                 }
12399                 this.activeRequest = Roo.Ajax.request(o);
12400             }else{
12401                 this.conn.request(o);
12402             }
12403         }else{
12404             callback.call(scope||this, null, arg, false);
12405         }
12406     },
12407
12408     // private
12409     loadResponse : function(o, success, response){
12410         delete this.activeRequest;
12411         if(!success){
12412             this.fireEvent("loadexception", this, o, response);
12413             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12414             return;
12415         }
12416         var result;
12417         try {
12418             result = o.reader.read(response);
12419         }catch(e){
12420             this.fireEvent("loadexception", this, o, response, e);
12421             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12422             return;
12423         }
12424         
12425         this.fireEvent("load", this, o, o.request.arg);
12426         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12427     },
12428
12429     // private
12430     update : function(dataSet){
12431
12432     },
12433
12434     // private
12435     updateResponse : function(dataSet){
12436
12437     }
12438 });/*
12439  * Based on:
12440  * Ext JS Library 1.1.1
12441  * Copyright(c) 2006-2007, Ext JS, LLC.
12442  *
12443  * Originally Released Under LGPL - original licence link has changed is not relivant.
12444  *
12445  * Fork - LGPL
12446  * <script type="text/javascript">
12447  */
12448
12449 /**
12450  * @class Roo.data.ScriptTagProxy
12451  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12452  * other than the originating domain of the running page.<br><br>
12453  * <p>
12454  * <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
12455  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12456  * <p>
12457  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12458  * source code that is used as the source inside a &lt;script> tag.<br><br>
12459  * <p>
12460  * In order for the browser to process the returned data, the server must wrap the data object
12461  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12462  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12463  * depending on whether the callback name was passed:
12464  * <p>
12465  * <pre><code>
12466 boolean scriptTag = false;
12467 String cb = request.getParameter("callback");
12468 if (cb != null) {
12469     scriptTag = true;
12470     response.setContentType("text/javascript");
12471 } else {
12472     response.setContentType("application/x-json");
12473 }
12474 Writer out = response.getWriter();
12475 if (scriptTag) {
12476     out.write(cb + "(");
12477 }
12478 out.print(dataBlock.toJsonString());
12479 if (scriptTag) {
12480     out.write(");");
12481 }
12482 </pre></code>
12483  *
12484  * @constructor
12485  * @param {Object} config A configuration object.
12486  */
12487 Roo.data.ScriptTagProxy = function(config){
12488     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12489     Roo.apply(this, config);
12490     this.head = document.getElementsByTagName("head")[0];
12491 };
12492
12493 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12494
12495 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12496     /**
12497      * @cfg {String} url The URL from which to request the data object.
12498      */
12499     /**
12500      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12501      */
12502     timeout : 30000,
12503     /**
12504      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12505      * the server the name of the callback function set up by the load call to process the returned data object.
12506      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12507      * javascript output which calls this named function passing the data object as its only parameter.
12508      */
12509     callbackParam : "callback",
12510     /**
12511      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12512      * name to the request.
12513      */
12514     nocache : true,
12515
12516     /**
12517      * Load data from the configured URL, read the data object into
12518      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12519      * process that block using the passed callback.
12520      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12521      * for the request to the remote server.
12522      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12523      * object into a block of Roo.data.Records.
12524      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12525      * The function must be passed <ul>
12526      * <li>The Record block object</li>
12527      * <li>The "arg" argument from the load function</li>
12528      * <li>A boolean success indicator</li>
12529      * </ul>
12530      * @param {Object} scope The scope in which to call the callback
12531      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12532      */
12533     load : function(params, reader, callback, scope, arg){
12534         if(this.fireEvent("beforeload", this, params) !== false){
12535
12536             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12537
12538             var url = this.url;
12539             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12540             if(this.nocache){
12541                 url += "&_dc=" + (new Date().getTime());
12542             }
12543             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12544             var trans = {
12545                 id : transId,
12546                 cb : "stcCallback"+transId,
12547                 scriptId : "stcScript"+transId,
12548                 params : params,
12549                 arg : arg,
12550                 url : url,
12551                 callback : callback,
12552                 scope : scope,
12553                 reader : reader
12554             };
12555             var conn = this;
12556
12557             window[trans.cb] = function(o){
12558                 conn.handleResponse(o, trans);
12559             };
12560
12561             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12562
12563             if(this.autoAbort !== false){
12564                 this.abort();
12565             }
12566
12567             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12568
12569             var script = document.createElement("script");
12570             script.setAttribute("src", url);
12571             script.setAttribute("type", "text/javascript");
12572             script.setAttribute("id", trans.scriptId);
12573             this.head.appendChild(script);
12574
12575             this.trans = trans;
12576         }else{
12577             callback.call(scope||this, null, arg, false);
12578         }
12579     },
12580
12581     // private
12582     isLoading : function(){
12583         return this.trans ? true : false;
12584     },
12585
12586     /**
12587      * Abort the current server request.
12588      */
12589     abort : function(){
12590         if(this.isLoading()){
12591             this.destroyTrans(this.trans);
12592         }
12593     },
12594
12595     // private
12596     destroyTrans : function(trans, isLoaded){
12597         this.head.removeChild(document.getElementById(trans.scriptId));
12598         clearTimeout(trans.timeoutId);
12599         if(isLoaded){
12600             window[trans.cb] = undefined;
12601             try{
12602                 delete window[trans.cb];
12603             }catch(e){}
12604         }else{
12605             // if hasn't been loaded, wait for load to remove it to prevent script error
12606             window[trans.cb] = function(){
12607                 window[trans.cb] = undefined;
12608                 try{
12609                     delete window[trans.cb];
12610                 }catch(e){}
12611             };
12612         }
12613     },
12614
12615     // private
12616     handleResponse : function(o, trans){
12617         this.trans = false;
12618         this.destroyTrans(trans, true);
12619         var result;
12620         try {
12621             result = trans.reader.readRecords(o);
12622         }catch(e){
12623             this.fireEvent("loadexception", this, o, trans.arg, e);
12624             trans.callback.call(trans.scope||window, null, trans.arg, false);
12625             return;
12626         }
12627         this.fireEvent("load", this, o, trans.arg);
12628         trans.callback.call(trans.scope||window, result, trans.arg, true);
12629     },
12630
12631     // private
12632     handleFailure : function(trans){
12633         this.trans = false;
12634         this.destroyTrans(trans, false);
12635         this.fireEvent("loadexception", this, null, trans.arg);
12636         trans.callback.call(trans.scope||window, null, trans.arg, false);
12637     }
12638 });/*
12639  * Based on:
12640  * Ext JS Library 1.1.1
12641  * Copyright(c) 2006-2007, Ext JS, LLC.
12642  *
12643  * Originally Released Under LGPL - original licence link has changed is not relivant.
12644  *
12645  * Fork - LGPL
12646  * <script type="text/javascript">
12647  */
12648
12649 /**
12650  * @class Roo.data.JsonReader
12651  * @extends Roo.data.DataReader
12652  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12653  * based on mappings in a provided Roo.data.Record constructor.
12654  * 
12655  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12656  * in the reply previously. 
12657  * 
12658  * <p>
12659  * Example code:
12660  * <pre><code>
12661 var RecordDef = Roo.data.Record.create([
12662     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12663     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12664 ]);
12665 var myReader = new Roo.data.JsonReader({
12666     totalProperty: "results",    // The property which contains the total dataset size (optional)
12667     root: "rows",                // The property which contains an Array of row objects
12668     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12669 }, RecordDef);
12670 </code></pre>
12671  * <p>
12672  * This would consume a JSON file like this:
12673  * <pre><code>
12674 { 'results': 2, 'rows': [
12675     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12676     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12677 }
12678 </code></pre>
12679  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12680  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12681  * paged from the remote server.
12682  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12683  * @cfg {String} root name of the property which contains the Array of row objects.
12684  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12685  * @cfg {Array} fields Array of field definition objects
12686  * @constructor
12687  * Create a new JsonReader
12688  * @param {Object} meta Metadata configuration options
12689  * @param {Object} recordType Either an Array of field definition objects,
12690  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12691  */
12692 Roo.data.JsonReader = function(meta, recordType){
12693     
12694     meta = meta || {};
12695     // set some defaults:
12696     Roo.applyIf(meta, {
12697         totalProperty: 'total',
12698         successProperty : 'success',
12699         root : 'data',
12700         id : 'id'
12701     });
12702     
12703     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12704 };
12705 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12706     
12707     /**
12708      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12709      * Used by Store query builder to append _requestMeta to params.
12710      * 
12711      */
12712     metaFromRemote : false,
12713     /**
12714      * This method is only used by a DataProxy which has retrieved data from a remote server.
12715      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12716      * @return {Object} data A data block which is used by an Roo.data.Store object as
12717      * a cache of Roo.data.Records.
12718      */
12719     read : function(response){
12720         var json = response.responseText;
12721        
12722         var o = /* eval:var:o */ eval("("+json+")");
12723         if(!o) {
12724             throw {message: "JsonReader.read: Json object not found"};
12725         }
12726         
12727         if(o.metaData){
12728             
12729             delete this.ef;
12730             this.metaFromRemote = true;
12731             this.meta = o.metaData;
12732             this.recordType = Roo.data.Record.create(o.metaData.fields);
12733             this.onMetaChange(this.meta, this.recordType, o);
12734         }
12735         return this.readRecords(o);
12736     },
12737
12738     // private function a store will implement
12739     onMetaChange : function(meta, recordType, o){
12740
12741     },
12742
12743     /**
12744          * @ignore
12745          */
12746     simpleAccess: function(obj, subsc) {
12747         return obj[subsc];
12748     },
12749
12750         /**
12751          * @ignore
12752          */
12753     getJsonAccessor: function(){
12754         var re = /[\[\.]/;
12755         return function(expr) {
12756             try {
12757                 return(re.test(expr))
12758                     ? new Function("obj", "return obj." + expr)
12759                     : function(obj){
12760                         return obj[expr];
12761                     };
12762             } catch(e){}
12763             return Roo.emptyFn;
12764         };
12765     }(),
12766
12767     /**
12768      * Create a data block containing Roo.data.Records from an XML document.
12769      * @param {Object} o An object which contains an Array of row objects in the property specified
12770      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12771      * which contains the total size of the dataset.
12772      * @return {Object} data A data block which is used by an Roo.data.Store object as
12773      * a cache of Roo.data.Records.
12774      */
12775     readRecords : function(o){
12776         /**
12777          * After any data loads, the raw JSON data is available for further custom processing.
12778          * @type Object
12779          */
12780         this.o = o;
12781         var s = this.meta, Record = this.recordType,
12782             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12783
12784 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12785         if (!this.ef) {
12786             if(s.totalProperty) {
12787                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12788                 }
12789                 if(s.successProperty) {
12790                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12791                 }
12792                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12793                 if (s.id) {
12794                         var g = this.getJsonAccessor(s.id);
12795                         this.getId = function(rec) {
12796                                 var r = g(rec);  
12797                                 return (r === undefined || r === "") ? null : r;
12798                         };
12799                 } else {
12800                         this.getId = function(){return null;};
12801                 }
12802             this.ef = [];
12803             for(var jj = 0; jj < fl; jj++){
12804                 f = fi[jj];
12805                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12806                 this.ef[jj] = this.getJsonAccessor(map);
12807             }
12808         }
12809
12810         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12811         if(s.totalProperty){
12812             var vt = parseInt(this.getTotal(o), 10);
12813             if(!isNaN(vt)){
12814                 totalRecords = vt;
12815             }
12816         }
12817         if(s.successProperty){
12818             var vs = this.getSuccess(o);
12819             if(vs === false || vs === 'false'){
12820                 success = false;
12821             }
12822         }
12823         var records = [];
12824         for(var i = 0; i < c; i++){
12825                 var n = root[i];
12826             var values = {};
12827             var id = this.getId(n);
12828             for(var j = 0; j < fl; j++){
12829                 f = fi[j];
12830             var v = this.ef[j](n);
12831             if (!f.convert) {
12832                 Roo.log('missing convert for ' + f.name);
12833                 Roo.log(f);
12834                 continue;
12835             }
12836             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12837             }
12838             var record = new Record(values, id);
12839             record.json = n;
12840             records[i] = record;
12841         }
12842         return {
12843             raw : o,
12844             success : success,
12845             records : records,
12846             totalRecords : totalRecords
12847         };
12848     }
12849 });/*
12850  * Based on:
12851  * Ext JS Library 1.1.1
12852  * Copyright(c) 2006-2007, Ext JS, LLC.
12853  *
12854  * Originally Released Under LGPL - original licence link has changed is not relivant.
12855  *
12856  * Fork - LGPL
12857  * <script type="text/javascript">
12858  */
12859
12860 /**
12861  * @class Roo.data.ArrayReader
12862  * @extends Roo.data.DataReader
12863  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12864  * Each element of that Array represents a row of data fields. The
12865  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12866  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12867  * <p>
12868  * Example code:.
12869  * <pre><code>
12870 var RecordDef = Roo.data.Record.create([
12871     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12872     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12873 ]);
12874 var myReader = new Roo.data.ArrayReader({
12875     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12876 }, RecordDef);
12877 </code></pre>
12878  * <p>
12879  * This would consume an Array like this:
12880  * <pre><code>
12881 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12882   </code></pre>
12883  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12884  * @constructor
12885  * Create a new JsonReader
12886  * @param {Object} meta Metadata configuration options.
12887  * @param {Object} recordType Either an Array of field definition objects
12888  * as specified to {@link Roo.data.Record#create},
12889  * or an {@link Roo.data.Record} object
12890  * created using {@link Roo.data.Record#create}.
12891  */
12892 Roo.data.ArrayReader = function(meta, recordType){
12893     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12894 };
12895
12896 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12897     /**
12898      * Create a data block containing Roo.data.Records from an XML document.
12899      * @param {Object} o An Array of row objects which represents the dataset.
12900      * @return {Object} data A data block which is used by an Roo.data.Store object as
12901      * a cache of Roo.data.Records.
12902      */
12903     readRecords : function(o){
12904         var sid = this.meta ? this.meta.id : null;
12905         var recordType = this.recordType, fields = recordType.prototype.fields;
12906         var records = [];
12907         var root = o;
12908             for(var i = 0; i < root.length; i++){
12909                     var n = root[i];
12910                 var values = {};
12911                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12912                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12913                 var f = fields.items[j];
12914                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12915                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12916                 v = f.convert(v);
12917                 values[f.name] = v;
12918             }
12919                 var record = new recordType(values, id);
12920                 record.json = n;
12921                 records[records.length] = record;
12922             }
12923             return {
12924                 records : records,
12925                 totalRecords : records.length
12926             };
12927     }
12928 });/*
12929  * - LGPL
12930  * * 
12931  */
12932
12933 /**
12934  * @class Roo.bootstrap.ComboBox
12935  * @extends Roo.bootstrap.TriggerField
12936  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12937  * @cfg {Boolean} append (true|false) default false
12938  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12939  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12940  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12941  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12942  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12943  * @cfg {Boolean} animate default true
12944  * @cfg {Boolean} emptyResultText only for touch device
12945  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12946  * @cfg {String} emptyTitle default ''
12947  * @constructor
12948  * Create a new ComboBox.
12949  * @param {Object} config Configuration options
12950  */
12951 Roo.bootstrap.ComboBox = function(config){
12952     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12953     this.addEvents({
12954         /**
12955          * @event expand
12956          * Fires when the dropdown list is expanded
12957         * @param {Roo.bootstrap.ComboBox} combo This combo box
12958         */
12959         'expand' : true,
12960         /**
12961          * @event collapse
12962          * Fires when the dropdown list is collapsed
12963         * @param {Roo.bootstrap.ComboBox} combo This combo box
12964         */
12965         'collapse' : true,
12966         /**
12967          * @event beforeselect
12968          * Fires before a list item is selected. Return false to cancel the selection.
12969         * @param {Roo.bootstrap.ComboBox} combo This combo box
12970         * @param {Roo.data.Record} record The data record returned from the underlying store
12971         * @param {Number} index The index of the selected item in the dropdown list
12972         */
12973         'beforeselect' : true,
12974         /**
12975          * @event select
12976          * Fires when a list item is selected
12977         * @param {Roo.bootstrap.ComboBox} combo This combo box
12978         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12979         * @param {Number} index The index of the selected item in the dropdown list
12980         */
12981         'select' : true,
12982         /**
12983          * @event beforequery
12984          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12985          * The event object passed has these properties:
12986         * @param {Roo.bootstrap.ComboBox} combo This combo box
12987         * @param {String} query The query
12988         * @param {Boolean} forceAll true to force "all" query
12989         * @param {Boolean} cancel true to cancel the query
12990         * @param {Object} e The query event object
12991         */
12992         'beforequery': true,
12993          /**
12994          * @event add
12995          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12996         * @param {Roo.bootstrap.ComboBox} combo This combo box
12997         */
12998         'add' : true,
12999         /**
13000          * @event edit
13001          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13002         * @param {Roo.bootstrap.ComboBox} combo This combo box
13003         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13004         */
13005         'edit' : true,
13006         /**
13007          * @event remove
13008          * Fires when the remove value from the combobox array
13009         * @param {Roo.bootstrap.ComboBox} combo This combo box
13010         */
13011         'remove' : true,
13012         /**
13013          * @event afterremove
13014          * Fires when the remove value from the combobox array
13015         * @param {Roo.bootstrap.ComboBox} combo This combo box
13016         */
13017         'afterremove' : true,
13018         /**
13019          * @event specialfilter
13020          * Fires when specialfilter
13021             * @param {Roo.bootstrap.ComboBox} combo This combo box
13022             */
13023         'specialfilter' : true,
13024         /**
13025          * @event tick
13026          * Fires when tick the element
13027             * @param {Roo.bootstrap.ComboBox} combo This combo box
13028             */
13029         'tick' : true,
13030         /**
13031          * @event touchviewdisplay
13032          * Fires when touch view require special display (default is using displayField)
13033             * @param {Roo.bootstrap.ComboBox} combo This combo box
13034             * @param {Object} cfg set html .
13035             */
13036         'touchviewdisplay' : true
13037         
13038     });
13039     
13040     this.item = [];
13041     this.tickItems = [];
13042     
13043     this.selectedIndex = -1;
13044     if(this.mode == 'local'){
13045         if(config.queryDelay === undefined){
13046             this.queryDelay = 10;
13047         }
13048         if(config.minChars === undefined){
13049             this.minChars = 0;
13050         }
13051     }
13052 };
13053
13054 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13055      
13056     /**
13057      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13058      * rendering into an Roo.Editor, defaults to false)
13059      */
13060     /**
13061      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13062      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13063      */
13064     /**
13065      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13066      */
13067     /**
13068      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13069      * the dropdown list (defaults to undefined, with no header element)
13070      */
13071
13072      /**
13073      * @cfg {String/Roo.Template} tpl The template to use to render the output
13074      */
13075      
13076      /**
13077      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13078      */
13079     listWidth: undefined,
13080     /**
13081      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13082      * mode = 'remote' or 'text' if mode = 'local')
13083      */
13084     displayField: undefined,
13085     
13086     /**
13087      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13088      * mode = 'remote' or 'value' if mode = 'local'). 
13089      * Note: use of a valueField requires the user make a selection
13090      * in order for a value to be mapped.
13091      */
13092     valueField: undefined,
13093     /**
13094      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13095      */
13096     modalTitle : '',
13097     
13098     /**
13099      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13100      * field's data value (defaults to the underlying DOM element's name)
13101      */
13102     hiddenName: undefined,
13103     /**
13104      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13105      */
13106     listClass: '',
13107     /**
13108      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13109      */
13110     selectedClass: 'active',
13111     
13112     /**
13113      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13114      */
13115     shadow:'sides',
13116     /**
13117      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13118      * anchor positions (defaults to 'tl-bl')
13119      */
13120     listAlign: 'tl-bl?',
13121     /**
13122      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13123      */
13124     maxHeight: 300,
13125     /**
13126      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13127      * query specified by the allQuery config option (defaults to 'query')
13128      */
13129     triggerAction: 'query',
13130     /**
13131      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13132      * (defaults to 4, does not apply if editable = false)
13133      */
13134     minChars : 4,
13135     /**
13136      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13137      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13138      */
13139     typeAhead: false,
13140     /**
13141      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13142      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13143      */
13144     queryDelay: 500,
13145     /**
13146      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13147      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13148      */
13149     pageSize: 0,
13150     /**
13151      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13152      * when editable = true (defaults to false)
13153      */
13154     selectOnFocus:false,
13155     /**
13156      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13157      */
13158     queryParam: 'query',
13159     /**
13160      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13161      * when mode = 'remote' (defaults to 'Loading...')
13162      */
13163     loadingText: 'Loading...',
13164     /**
13165      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13166      */
13167     resizable: false,
13168     /**
13169      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13170      */
13171     handleHeight : 8,
13172     /**
13173      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13174      * traditional select (defaults to true)
13175      */
13176     editable: true,
13177     /**
13178      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13179      */
13180     allQuery: '',
13181     /**
13182      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13183      */
13184     mode: 'remote',
13185     /**
13186      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13187      * listWidth has a higher value)
13188      */
13189     minListWidth : 70,
13190     /**
13191      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13192      * allow the user to set arbitrary text into the field (defaults to false)
13193      */
13194     forceSelection:false,
13195     /**
13196      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13197      * if typeAhead = true (defaults to 250)
13198      */
13199     typeAheadDelay : 250,
13200     /**
13201      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13202      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13203      */
13204     valueNotFoundText : undefined,
13205     /**
13206      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13207      */
13208     blockFocus : false,
13209     
13210     /**
13211      * @cfg {Boolean} disableClear Disable showing of clear button.
13212      */
13213     disableClear : false,
13214     /**
13215      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13216      */
13217     alwaysQuery : false,
13218     
13219     /**
13220      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13221      */
13222     multiple : false,
13223     
13224     /**
13225      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13226      */
13227     invalidClass : "has-warning",
13228     
13229     /**
13230      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13231      */
13232     validClass : "has-success",
13233     
13234     /**
13235      * @cfg {Boolean} specialFilter (true|false) special filter default false
13236      */
13237     specialFilter : false,
13238     
13239     /**
13240      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13241      */
13242     mobileTouchView : true,
13243     
13244     /**
13245      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13246      */
13247     useNativeIOS : false,
13248     
13249     /**
13250      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13251      */
13252     mobile_restrict_height : false,
13253     
13254     ios_options : false,
13255     
13256     //private
13257     addicon : false,
13258     editicon: false,
13259     
13260     page: 0,
13261     hasQuery: false,
13262     append: false,
13263     loadNext: false,
13264     autoFocus : true,
13265     tickable : false,
13266     btnPosition : 'right',
13267     triggerList : true,
13268     showToggleBtn : true,
13269     animate : true,
13270     emptyResultText: 'Empty',
13271     triggerText : 'Select',
13272     emptyTitle : '',
13273     
13274     // element that contains real text value.. (when hidden is used..)
13275     
13276     getAutoCreate : function()
13277     {   
13278         var cfg = false;
13279         //render
13280         /*
13281          * Render classic select for iso
13282          */
13283         
13284         if(Roo.isIOS && this.useNativeIOS){
13285             cfg = this.getAutoCreateNativeIOS();
13286             return cfg;
13287         }
13288         
13289         /*
13290          * Touch Devices
13291          */
13292         
13293         if(Roo.isTouch && this.mobileTouchView){
13294             cfg = this.getAutoCreateTouchView();
13295             return cfg;;
13296         }
13297         
13298         /*
13299          *  Normal ComboBox
13300          */
13301         if(!this.tickable){
13302             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13303             return cfg;
13304         }
13305         
13306         /*
13307          *  ComboBox with tickable selections
13308          */
13309              
13310         var align = this.labelAlign || this.parentLabelAlign();
13311         
13312         cfg = {
13313             cls : 'form-group roo-combobox-tickable' //input-group
13314         };
13315         
13316         var btn_text_select = '';
13317         var btn_text_done = '';
13318         var btn_text_cancel = '';
13319         
13320         if (this.btn_text_show) {
13321             btn_text_select = 'Select';
13322             btn_text_done = 'Done';
13323             btn_text_cancel = 'Cancel'; 
13324         }
13325         
13326         var buttons = {
13327             tag : 'div',
13328             cls : 'tickable-buttons',
13329             cn : [
13330                 {
13331                     tag : 'button',
13332                     type : 'button',
13333                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13334                     //html : this.triggerText
13335                     html: btn_text_select
13336                 },
13337                 {
13338                     tag : 'button',
13339                     type : 'button',
13340                     name : 'ok',
13341                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13342                     //html : 'Done'
13343                     html: btn_text_done
13344                 },
13345                 {
13346                     tag : 'button',
13347                     type : 'button',
13348                     name : 'cancel',
13349                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13350                     //html : 'Cancel'
13351                     html: btn_text_cancel
13352                 }
13353             ]
13354         };
13355         
13356         if(this.editable){
13357             buttons.cn.unshift({
13358                 tag: 'input',
13359                 cls: 'roo-select2-search-field-input'
13360             });
13361         }
13362         
13363         var _this = this;
13364         
13365         Roo.each(buttons.cn, function(c){
13366             if (_this.size) {
13367                 c.cls += ' btn-' + _this.size;
13368             }
13369
13370             if (_this.disabled) {
13371                 c.disabled = true;
13372             }
13373         });
13374         
13375         var box = {
13376             tag: 'div',
13377             style : 'display: contents',
13378             cn: [
13379                 {
13380                     tag: 'input',
13381                     type : 'hidden',
13382                     cls: 'form-hidden-field'
13383                 },
13384                 {
13385                     tag: 'ul',
13386                     cls: 'roo-select2-choices',
13387                     cn:[
13388                         {
13389                             tag: 'li',
13390                             cls: 'roo-select2-search-field',
13391                             cn: [
13392                                 buttons
13393                             ]
13394                         }
13395                     ]
13396                 }
13397             ]
13398         };
13399         
13400         var combobox = {
13401             cls: 'roo-select2-container input-group roo-select2-container-multi',
13402             cn: [
13403                 
13404                 box
13405 //                {
13406 //                    tag: 'ul',
13407 //                    cls: 'typeahead typeahead-long dropdown-menu',
13408 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13409 //                }
13410             ]
13411         };
13412         
13413         if(this.hasFeedback && !this.allowBlank){
13414             
13415             var feedback = {
13416                 tag: 'span',
13417                 cls: 'glyphicon form-control-feedback'
13418             };
13419
13420             combobox.cn.push(feedback);
13421         }
13422         
13423         var indicator = {
13424             tag : 'i',
13425             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13426             tooltip : 'This field is required'
13427         };
13428         if (Roo.bootstrap.version == 4) {
13429             indicator = {
13430                 tag : 'i',
13431                 style : 'display:none'
13432             };
13433         }
13434         if (align ==='left' && this.fieldLabel.length) {
13435             
13436             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13437             
13438             cfg.cn = [
13439                 indicator,
13440                 {
13441                     tag: 'label',
13442                     'for' :  id,
13443                     cls : 'control-label col-form-label',
13444                     html : this.fieldLabel
13445
13446                 },
13447                 {
13448                     cls : "", 
13449                     cn: [
13450                         combobox
13451                     ]
13452                 }
13453
13454             ];
13455             
13456             var labelCfg = cfg.cn[1];
13457             var contentCfg = cfg.cn[2];
13458             
13459
13460             if(this.indicatorpos == 'right'){
13461                 
13462                 cfg.cn = [
13463                     {
13464                         tag: 'label',
13465                         'for' :  id,
13466                         cls : 'control-label col-form-label',
13467                         cn : [
13468                             {
13469                                 tag : 'span',
13470                                 html : this.fieldLabel
13471                             },
13472                             indicator
13473                         ]
13474                     },
13475                     {
13476                         cls : "",
13477                         cn: [
13478                             combobox
13479                         ]
13480                     }
13481
13482                 ];
13483                 
13484                 
13485                 
13486                 labelCfg = cfg.cn[0];
13487                 contentCfg = cfg.cn[1];
13488             
13489             }
13490             
13491             if(this.labelWidth > 12){
13492                 labelCfg.style = "width: " + this.labelWidth + 'px';
13493             }
13494             
13495             if(this.labelWidth < 13 && this.labelmd == 0){
13496                 this.labelmd = this.labelWidth;
13497             }
13498             
13499             if(this.labellg > 0){
13500                 labelCfg.cls += ' col-lg-' + this.labellg;
13501                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13502             }
13503             
13504             if(this.labelmd > 0){
13505                 labelCfg.cls += ' col-md-' + this.labelmd;
13506                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13507             }
13508             
13509             if(this.labelsm > 0){
13510                 labelCfg.cls += ' col-sm-' + this.labelsm;
13511                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13512             }
13513             
13514             if(this.labelxs > 0){
13515                 labelCfg.cls += ' col-xs-' + this.labelxs;
13516                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13517             }
13518                 
13519                 
13520         } else if ( this.fieldLabel.length) {
13521 //                Roo.log(" label");
13522                  cfg.cn = [
13523                    indicator,
13524                     {
13525                         tag: 'label',
13526                         //cls : 'input-group-addon',
13527                         html : this.fieldLabel
13528                     },
13529                     combobox
13530                 ];
13531                 
13532                 if(this.indicatorpos == 'right'){
13533                     cfg.cn = [
13534                         {
13535                             tag: 'label',
13536                             //cls : 'input-group-addon',
13537                             html : this.fieldLabel
13538                         },
13539                         indicator,
13540                         combobox
13541                     ];
13542                     
13543                 }
13544
13545         } else {
13546             
13547 //                Roo.log(" no label && no align");
13548                 cfg = combobox
13549                      
13550                 
13551         }
13552          
13553         var settings=this;
13554         ['xs','sm','md','lg'].map(function(size){
13555             if (settings[size]) {
13556                 cfg.cls += ' col-' + size + '-' + settings[size];
13557             }
13558         });
13559         
13560         return cfg;
13561         
13562     },
13563     
13564     _initEventsCalled : false,
13565     
13566     // private
13567     initEvents: function()
13568     {   
13569         if (this._initEventsCalled) { // as we call render... prevent looping...
13570             return;
13571         }
13572         this._initEventsCalled = true;
13573         
13574         if (!this.store) {
13575             throw "can not find store for combo";
13576         }
13577         
13578         this.indicator = this.indicatorEl();
13579         
13580         this.store = Roo.factory(this.store, Roo.data);
13581         this.store.parent = this;
13582         
13583         // if we are building from html. then this element is so complex, that we can not really
13584         // use the rendered HTML.
13585         // so we have to trash and replace the previous code.
13586         if (Roo.XComponent.build_from_html) {
13587             // remove this element....
13588             var e = this.el.dom, k=0;
13589             while (e ) { e = e.previousSibling;  ++k;}
13590
13591             this.el.remove();
13592             
13593             this.el=false;
13594             this.rendered = false;
13595             
13596             this.render(this.parent().getChildContainer(true), k);
13597         }
13598         
13599         if(Roo.isIOS && this.useNativeIOS){
13600             this.initIOSView();
13601             return;
13602         }
13603         
13604         /*
13605          * Touch Devices
13606          */
13607         
13608         if(Roo.isTouch && this.mobileTouchView){
13609             this.initTouchView();
13610             return;
13611         }
13612         
13613         if(this.tickable){
13614             this.initTickableEvents();
13615             return;
13616         }
13617         
13618         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13619         
13620         if(this.hiddenName){
13621             
13622             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13623             
13624             this.hiddenField.dom.value =
13625                 this.hiddenValue !== undefined ? this.hiddenValue :
13626                 this.value !== undefined ? this.value : '';
13627
13628             // prevent input submission
13629             this.el.dom.removeAttribute('name');
13630             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13631              
13632              
13633         }
13634         //if(Roo.isGecko){
13635         //    this.el.dom.setAttribute('autocomplete', 'off');
13636         //}
13637         
13638         var cls = 'x-combo-list';
13639         
13640         //this.list = new Roo.Layer({
13641         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13642         //});
13643         
13644         var _this = this;
13645         
13646         (function(){
13647             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13648             _this.list.setWidth(lw);
13649         }).defer(100);
13650         
13651         this.list.on('mouseover', this.onViewOver, this);
13652         this.list.on('mousemove', this.onViewMove, this);
13653         this.list.on('scroll', this.onViewScroll, this);
13654         
13655         /*
13656         this.list.swallowEvent('mousewheel');
13657         this.assetHeight = 0;
13658
13659         if(this.title){
13660             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13661             this.assetHeight += this.header.getHeight();
13662         }
13663
13664         this.innerList = this.list.createChild({cls:cls+'-inner'});
13665         this.innerList.on('mouseover', this.onViewOver, this);
13666         this.innerList.on('mousemove', this.onViewMove, this);
13667         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13668         
13669         if(this.allowBlank && !this.pageSize && !this.disableClear){
13670             this.footer = this.list.createChild({cls:cls+'-ft'});
13671             this.pageTb = new Roo.Toolbar(this.footer);
13672            
13673         }
13674         if(this.pageSize){
13675             this.footer = this.list.createChild({cls:cls+'-ft'});
13676             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13677                     {pageSize: this.pageSize});
13678             
13679         }
13680         
13681         if (this.pageTb && this.allowBlank && !this.disableClear) {
13682             var _this = this;
13683             this.pageTb.add(new Roo.Toolbar.Fill(), {
13684                 cls: 'x-btn-icon x-btn-clear',
13685                 text: '&#160;',
13686                 handler: function()
13687                 {
13688                     _this.collapse();
13689                     _this.clearValue();
13690                     _this.onSelect(false, -1);
13691                 }
13692             });
13693         }
13694         if (this.footer) {
13695             this.assetHeight += this.footer.getHeight();
13696         }
13697         */
13698             
13699         if(!this.tpl){
13700             this.tpl = Roo.bootstrap.version == 4 ?
13701                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13702                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13703         }
13704
13705         this.view = new Roo.View(this.list, this.tpl, {
13706             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13707         });
13708         //this.view.wrapEl.setDisplayed(false);
13709         this.view.on('click', this.onViewClick, this);
13710         
13711         
13712         this.store.on('beforeload', this.onBeforeLoad, this);
13713         this.store.on('load', this.onLoad, this);
13714         this.store.on('loadexception', this.onLoadException, this);
13715         /*
13716         if(this.resizable){
13717             this.resizer = new Roo.Resizable(this.list,  {
13718                pinned:true, handles:'se'
13719             });
13720             this.resizer.on('resize', function(r, w, h){
13721                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13722                 this.listWidth = w;
13723                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13724                 this.restrictHeight();
13725             }, this);
13726             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13727         }
13728         */
13729         if(!this.editable){
13730             this.editable = true;
13731             this.setEditable(false);
13732         }
13733         
13734         /*
13735         
13736         if (typeof(this.events.add.listeners) != 'undefined') {
13737             
13738             this.addicon = this.wrap.createChild(
13739                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13740        
13741             this.addicon.on('click', function(e) {
13742                 this.fireEvent('add', this);
13743             }, this);
13744         }
13745         if (typeof(this.events.edit.listeners) != 'undefined') {
13746             
13747             this.editicon = this.wrap.createChild(
13748                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13749             if (this.addicon) {
13750                 this.editicon.setStyle('margin-left', '40px');
13751             }
13752             this.editicon.on('click', function(e) {
13753                 
13754                 // we fire even  if inothing is selected..
13755                 this.fireEvent('edit', this, this.lastData );
13756                 
13757             }, this);
13758         }
13759         */
13760         
13761         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13762             "up" : function(e){
13763                 this.inKeyMode = true;
13764                 this.selectPrev();
13765             },
13766
13767             "down" : function(e){
13768                 if(!this.isExpanded()){
13769                     this.onTriggerClick();
13770                 }else{
13771                     this.inKeyMode = true;
13772                     this.selectNext();
13773                 }
13774             },
13775
13776             "enter" : function(e){
13777 //                this.onViewClick();
13778                 //return true;
13779                 this.collapse();
13780                 
13781                 if(this.fireEvent("specialkey", this, e)){
13782                     this.onViewClick(false);
13783                 }
13784                 
13785                 return true;
13786             },
13787
13788             "esc" : function(e){
13789                 this.collapse();
13790             },
13791
13792             "tab" : function(e){
13793                 this.collapse();
13794                 
13795                 if(this.fireEvent("specialkey", this, e)){
13796                     this.onViewClick(false);
13797                 }
13798                 
13799                 return true;
13800             },
13801
13802             scope : this,
13803
13804             doRelay : function(foo, bar, hname){
13805                 if(hname == 'down' || this.scope.isExpanded()){
13806                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13807                 }
13808                 return true;
13809             },
13810
13811             forceKeyDown: true
13812         });
13813         
13814         
13815         this.queryDelay = Math.max(this.queryDelay || 10,
13816                 this.mode == 'local' ? 10 : 250);
13817         
13818         
13819         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13820         
13821         if(this.typeAhead){
13822             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13823         }
13824         if(this.editable !== false){
13825             this.inputEl().on("keyup", this.onKeyUp, this);
13826         }
13827         if(this.forceSelection){
13828             this.inputEl().on('blur', this.doForce, this);
13829         }
13830         
13831         if(this.multiple){
13832             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13833             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13834         }
13835     },
13836     
13837     initTickableEvents: function()
13838     {   
13839         this.createList();
13840         
13841         if(this.hiddenName){
13842             
13843             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13844             
13845             this.hiddenField.dom.value =
13846                 this.hiddenValue !== undefined ? this.hiddenValue :
13847                 this.value !== undefined ? this.value : '';
13848
13849             // prevent input submission
13850             this.el.dom.removeAttribute('name');
13851             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13852              
13853              
13854         }
13855         
13856 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13857         
13858         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13859         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13860         if(this.triggerList){
13861             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13862         }
13863          
13864         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13865         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13866         
13867         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13868         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13869         
13870         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13871         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13872         
13873         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13874         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13875         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13876         
13877         this.okBtn.hide();
13878         this.cancelBtn.hide();
13879         
13880         var _this = this;
13881         
13882         (function(){
13883             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13884             _this.list.setWidth(lw);
13885         }).defer(100);
13886         
13887         this.list.on('mouseover', this.onViewOver, this);
13888         this.list.on('mousemove', this.onViewMove, this);
13889         
13890         this.list.on('scroll', this.onViewScroll, this);
13891         
13892         if(!this.tpl){
13893             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13894                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13895         }
13896
13897         this.view = new Roo.View(this.list, this.tpl, {
13898             singleSelect:true,
13899             tickable:true,
13900             parent:this,
13901             store: this.store,
13902             selectedClass: this.selectedClass
13903         });
13904         
13905         //this.view.wrapEl.setDisplayed(false);
13906         this.view.on('click', this.onViewClick, this);
13907         
13908         
13909         
13910         this.store.on('beforeload', this.onBeforeLoad, this);
13911         this.store.on('load', this.onLoad, this);
13912         this.store.on('loadexception', this.onLoadException, this);
13913         
13914         if(this.editable){
13915             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13916                 "up" : function(e){
13917                     this.inKeyMode = true;
13918                     this.selectPrev();
13919                 },
13920
13921                 "down" : function(e){
13922                     this.inKeyMode = true;
13923                     this.selectNext();
13924                 },
13925
13926                 "enter" : function(e){
13927                     if(this.fireEvent("specialkey", this, e)){
13928                         this.onViewClick(false);
13929                     }
13930                     
13931                     return true;
13932                 },
13933
13934                 "esc" : function(e){
13935                     this.onTickableFooterButtonClick(e, false, false);
13936                 },
13937
13938                 "tab" : function(e){
13939                     this.fireEvent("specialkey", this, e);
13940                     
13941                     this.onTickableFooterButtonClick(e, false, false);
13942                     
13943                     return true;
13944                 },
13945
13946                 scope : this,
13947
13948                 doRelay : function(e, fn, key){
13949                     if(this.scope.isExpanded()){
13950                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13951                     }
13952                     return true;
13953                 },
13954
13955                 forceKeyDown: true
13956             });
13957         }
13958         
13959         this.queryDelay = Math.max(this.queryDelay || 10,
13960                 this.mode == 'local' ? 10 : 250);
13961         
13962         
13963         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13964         
13965         if(this.typeAhead){
13966             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13967         }
13968         
13969         if(this.editable !== false){
13970             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13971         }
13972         
13973         this.indicator = this.indicatorEl();
13974         
13975         if(this.indicator){
13976             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13977             this.indicator.hide();
13978         }
13979         
13980     },
13981
13982     onDestroy : function(){
13983         if(this.view){
13984             this.view.setStore(null);
13985             this.view.el.removeAllListeners();
13986             this.view.el.remove();
13987             this.view.purgeListeners();
13988         }
13989         if(this.list){
13990             this.list.dom.innerHTML  = '';
13991         }
13992         
13993         if(this.store){
13994             this.store.un('beforeload', this.onBeforeLoad, this);
13995             this.store.un('load', this.onLoad, this);
13996             this.store.un('loadexception', this.onLoadException, this);
13997         }
13998         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13999     },
14000
14001     // private
14002     fireKey : function(e){
14003         if(e.isNavKeyPress() && !this.list.isVisible()){
14004             this.fireEvent("specialkey", this, e);
14005         }
14006     },
14007
14008     // private
14009     onResize: function(w, h){
14010 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14011 //        
14012 //        if(typeof w != 'number'){
14013 //            // we do not handle it!?!?
14014 //            return;
14015 //        }
14016 //        var tw = this.trigger.getWidth();
14017 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14018 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14019 //        var x = w - tw;
14020 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14021 //            
14022 //        //this.trigger.setStyle('left', x+'px');
14023 //        
14024 //        if(this.list && this.listWidth === undefined){
14025 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14026 //            this.list.setWidth(lw);
14027 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14028 //        }
14029         
14030     
14031         
14032     },
14033
14034     /**
14035      * Allow or prevent the user from directly editing the field text.  If false is passed,
14036      * the user will only be able to select from the items defined in the dropdown list.  This method
14037      * is the runtime equivalent of setting the 'editable' config option at config time.
14038      * @param {Boolean} value True to allow the user to directly edit the field text
14039      */
14040     setEditable : function(value){
14041         if(value == this.editable){
14042             return;
14043         }
14044         this.editable = value;
14045         if(!value){
14046             this.inputEl().dom.setAttribute('readOnly', true);
14047             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14048             this.inputEl().addClass('x-combo-noedit');
14049         }else{
14050             this.inputEl().dom.setAttribute('readOnly', false);
14051             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14052             this.inputEl().removeClass('x-combo-noedit');
14053         }
14054     },
14055
14056     // private
14057     
14058     onBeforeLoad : function(combo,opts){
14059         if(!this.hasFocus){
14060             return;
14061         }
14062          if (!opts.add) {
14063             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14064          }
14065         this.restrictHeight();
14066         this.selectedIndex = -1;
14067     },
14068
14069     // private
14070     onLoad : function(){
14071         
14072         this.hasQuery = false;
14073         
14074         if(!this.hasFocus){
14075             return;
14076         }
14077         
14078         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14079             this.loading.hide();
14080         }
14081         
14082         if(this.store.getCount() > 0){
14083             
14084             this.expand();
14085             this.restrictHeight();
14086             if(this.lastQuery == this.allQuery){
14087                 if(this.editable && !this.tickable){
14088                     this.inputEl().dom.select();
14089                 }
14090                 
14091                 if(
14092                     !this.selectByValue(this.value, true) &&
14093                     this.autoFocus && 
14094                     (
14095                         !this.store.lastOptions ||
14096                         typeof(this.store.lastOptions.add) == 'undefined' || 
14097                         this.store.lastOptions.add != true
14098                     )
14099                 ){
14100                     this.select(0, true);
14101                 }
14102             }else{
14103                 if(this.autoFocus){
14104                     this.selectNext();
14105                 }
14106                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14107                     this.taTask.delay(this.typeAheadDelay);
14108                 }
14109             }
14110         }else{
14111             this.onEmptyResults();
14112         }
14113         
14114         //this.el.focus();
14115     },
14116     // private
14117     onLoadException : function()
14118     {
14119         this.hasQuery = false;
14120         
14121         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14122             this.loading.hide();
14123         }
14124         
14125         if(this.tickable && this.editable){
14126             return;
14127         }
14128         
14129         this.collapse();
14130         // only causes errors at present
14131         //Roo.log(this.store.reader.jsonData);
14132         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14133             // fixme
14134             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14135         //}
14136         
14137         
14138     },
14139     // private
14140     onTypeAhead : function(){
14141         if(this.store.getCount() > 0){
14142             var r = this.store.getAt(0);
14143             var newValue = r.data[this.displayField];
14144             var len = newValue.length;
14145             var selStart = this.getRawValue().length;
14146             
14147             if(selStart != len){
14148                 this.setRawValue(newValue);
14149                 this.selectText(selStart, newValue.length);
14150             }
14151         }
14152     },
14153
14154     // private
14155     onSelect : function(record, index){
14156         
14157         if(this.fireEvent('beforeselect', this, record, index) !== false){
14158         
14159             this.setFromData(index > -1 ? record.data : false);
14160             
14161             this.collapse();
14162             this.fireEvent('select', this, record, index);
14163         }
14164     },
14165
14166     /**
14167      * Returns the currently selected field value or empty string if no value is set.
14168      * @return {String} value The selected value
14169      */
14170     getValue : function()
14171     {
14172         if(Roo.isIOS && this.useNativeIOS){
14173             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14174         }
14175         
14176         if(this.multiple){
14177             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14178         }
14179         
14180         if(this.valueField){
14181             return typeof this.value != 'undefined' ? this.value : '';
14182         }else{
14183             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14184         }
14185     },
14186     
14187     getRawValue : function()
14188     {
14189         if(Roo.isIOS && this.useNativeIOS){
14190             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14191         }
14192         
14193         var v = this.inputEl().getValue();
14194         
14195         return v;
14196     },
14197
14198     /**
14199      * Clears any text/value currently set in the field
14200      */
14201     clearValue : function(){
14202         
14203         if(this.hiddenField){
14204             this.hiddenField.dom.value = '';
14205         }
14206         this.value = '';
14207         this.setRawValue('');
14208         this.lastSelectionText = '';
14209         this.lastData = false;
14210         
14211         var close = this.closeTriggerEl();
14212         
14213         if(close){
14214             close.hide();
14215         }
14216         
14217         this.validate();
14218         
14219     },
14220
14221     /**
14222      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14223      * will be displayed in the field.  If the value does not match the data value of an existing item,
14224      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14225      * Otherwise the field will be blank (although the value will still be set).
14226      * @param {String} value The value to match
14227      */
14228     setValue : function(v)
14229     {
14230         if(Roo.isIOS && this.useNativeIOS){
14231             this.setIOSValue(v);
14232             return;
14233         }
14234         
14235         if(this.multiple){
14236             this.syncValue();
14237             return;
14238         }
14239         
14240         var text = v;
14241         if(this.valueField){
14242             var r = this.findRecord(this.valueField, v);
14243             if(r){
14244                 text = r.data[this.displayField];
14245             }else if(this.valueNotFoundText !== undefined){
14246                 text = this.valueNotFoundText;
14247             }
14248         }
14249         this.lastSelectionText = text;
14250         if(this.hiddenField){
14251             this.hiddenField.dom.value = v;
14252         }
14253         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14254         this.value = v;
14255         
14256         var close = this.closeTriggerEl();
14257         
14258         if(close){
14259             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14260         }
14261         
14262         this.validate();
14263     },
14264     /**
14265      * @property {Object} the last set data for the element
14266      */
14267     
14268     lastData : false,
14269     /**
14270      * Sets the value of the field based on a object which is related to the record format for the store.
14271      * @param {Object} value the value to set as. or false on reset?
14272      */
14273     setFromData : function(o){
14274         
14275         if(this.multiple){
14276             this.addItem(o);
14277             return;
14278         }
14279             
14280         var dv = ''; // display value
14281         var vv = ''; // value value..
14282         this.lastData = o;
14283         if (this.displayField) {
14284             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14285         } else {
14286             // this is an error condition!!!
14287             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14288         }
14289         
14290         if(this.valueField){
14291             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14292         }
14293         
14294         var close = this.closeTriggerEl();
14295         
14296         if(close){
14297             if(dv.length || vv * 1 > 0){
14298                 close.show() ;
14299                 this.blockFocus=true;
14300             } else {
14301                 close.hide();
14302             }             
14303         }
14304         
14305         if(this.hiddenField){
14306             this.hiddenField.dom.value = vv;
14307             
14308             this.lastSelectionText = dv;
14309             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14310             this.value = vv;
14311             return;
14312         }
14313         // no hidden field.. - we store the value in 'value', but still display
14314         // display field!!!!
14315         this.lastSelectionText = dv;
14316         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14317         this.value = vv;
14318         
14319         
14320         
14321     },
14322     // private
14323     reset : function(){
14324         // overridden so that last data is reset..
14325         
14326         if(this.multiple){
14327             this.clearItem();
14328             return;
14329         }
14330         
14331         this.setValue(this.originalValue);
14332         //this.clearInvalid();
14333         this.lastData = false;
14334         if (this.view) {
14335             this.view.clearSelections();
14336         }
14337         
14338         this.validate();
14339     },
14340     // private
14341     findRecord : function(prop, value){
14342         var record;
14343         if(this.store.getCount() > 0){
14344             this.store.each(function(r){
14345                 if(r.data[prop] == value){
14346                     record = r;
14347                     return false;
14348                 }
14349                 return true;
14350             });
14351         }
14352         return record;
14353     },
14354     
14355     getName: function()
14356     {
14357         // returns hidden if it's set..
14358         if (!this.rendered) {return ''};
14359         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14360         
14361     },
14362     // private
14363     onViewMove : function(e, t){
14364         this.inKeyMode = false;
14365     },
14366
14367     // private
14368     onViewOver : function(e, t){
14369         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14370             return;
14371         }
14372         var item = this.view.findItemFromChild(t);
14373         
14374         if(item){
14375             var index = this.view.indexOf(item);
14376             this.select(index, false);
14377         }
14378     },
14379
14380     // private
14381     onViewClick : function(view, doFocus, el, e)
14382     {
14383         var index = this.view.getSelectedIndexes()[0];
14384         
14385         var r = this.store.getAt(index);
14386         
14387         if(this.tickable){
14388             
14389             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14390                 return;
14391             }
14392             
14393             var rm = false;
14394             var _this = this;
14395             
14396             Roo.each(this.tickItems, function(v,k){
14397                 
14398                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14399                     Roo.log(v);
14400                     _this.tickItems.splice(k, 1);
14401                     
14402                     if(typeof(e) == 'undefined' && view == false){
14403                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14404                     }
14405                     
14406                     rm = true;
14407                     return;
14408                 }
14409             });
14410             
14411             if(rm){
14412                 return;
14413             }
14414             
14415             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14416                 this.tickItems.push(r.data);
14417             }
14418             
14419             if(typeof(e) == 'undefined' && view == false){
14420                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14421             }
14422                     
14423             return;
14424         }
14425         
14426         if(r){
14427             this.onSelect(r, index);
14428         }
14429         if(doFocus !== false && !this.blockFocus){
14430             this.inputEl().focus();
14431         }
14432     },
14433
14434     // private
14435     restrictHeight : function(){
14436         //this.innerList.dom.style.height = '';
14437         //var inner = this.innerList.dom;
14438         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14439         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14440         //this.list.beginUpdate();
14441         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14442         this.list.alignTo(this.inputEl(), this.listAlign);
14443         this.list.alignTo(this.inputEl(), this.listAlign);
14444         //this.list.endUpdate();
14445     },
14446
14447     // private
14448     onEmptyResults : function(){
14449         
14450         if(this.tickable && this.editable){
14451             this.hasFocus = false;
14452             this.restrictHeight();
14453             return;
14454         }
14455         
14456         this.collapse();
14457     },
14458
14459     /**
14460      * Returns true if the dropdown list is expanded, else false.
14461      */
14462     isExpanded : function(){
14463         return this.list.isVisible();
14464     },
14465
14466     /**
14467      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14468      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14469      * @param {String} value The data value of the item to select
14470      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14471      * selected item if it is not currently in view (defaults to true)
14472      * @return {Boolean} True if the value matched an item in the list, else false
14473      */
14474     selectByValue : function(v, scrollIntoView){
14475         if(v !== undefined && v !== null){
14476             var r = this.findRecord(this.valueField || this.displayField, v);
14477             if(r){
14478                 this.select(this.store.indexOf(r), scrollIntoView);
14479                 return true;
14480             }
14481         }
14482         return false;
14483     },
14484
14485     /**
14486      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14487      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14488      * @param {Number} index The zero-based index of the list item to select
14489      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14490      * selected item if it is not currently in view (defaults to true)
14491      */
14492     select : function(index, scrollIntoView){
14493         this.selectedIndex = index;
14494         this.view.select(index);
14495         if(scrollIntoView !== false){
14496             var el = this.view.getNode(index);
14497             /*
14498              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14499              */
14500             if(el){
14501                 this.list.scrollChildIntoView(el, false);
14502             }
14503         }
14504     },
14505
14506     // private
14507     selectNext : function(){
14508         var ct = this.store.getCount();
14509         if(ct > 0){
14510             if(this.selectedIndex == -1){
14511                 this.select(0);
14512             }else if(this.selectedIndex < ct-1){
14513                 this.select(this.selectedIndex+1);
14514             }
14515         }
14516     },
14517
14518     // private
14519     selectPrev : function(){
14520         var ct = this.store.getCount();
14521         if(ct > 0){
14522             if(this.selectedIndex == -1){
14523                 this.select(0);
14524             }else if(this.selectedIndex != 0){
14525                 this.select(this.selectedIndex-1);
14526             }
14527         }
14528     },
14529
14530     // private
14531     onKeyUp : function(e){
14532         if(this.editable !== false && !e.isSpecialKey()){
14533             this.lastKey = e.getKey();
14534             this.dqTask.delay(this.queryDelay);
14535         }
14536     },
14537
14538     // private
14539     validateBlur : function(){
14540         return !this.list || !this.list.isVisible();   
14541     },
14542
14543     // private
14544     initQuery : function(){
14545         
14546         var v = this.getRawValue();
14547         
14548         if(this.tickable && this.editable){
14549             v = this.tickableInputEl().getValue();
14550         }
14551         
14552         this.doQuery(v);
14553     },
14554
14555     // private
14556     doForce : function(){
14557         if(this.inputEl().dom.value.length > 0){
14558             this.inputEl().dom.value =
14559                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14560              
14561         }
14562     },
14563
14564     /**
14565      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14566      * query allowing the query action to be canceled if needed.
14567      * @param {String} query The SQL query to execute
14568      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14569      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14570      * saved in the current store (defaults to false)
14571      */
14572     doQuery : function(q, forceAll){
14573         
14574         if(q === undefined || q === null){
14575             q = '';
14576         }
14577         var qe = {
14578             query: q,
14579             forceAll: forceAll,
14580             combo: this,
14581             cancel:false
14582         };
14583         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14584             return false;
14585         }
14586         q = qe.query;
14587         
14588         forceAll = qe.forceAll;
14589         if(forceAll === true || (q.length >= this.minChars)){
14590             
14591             this.hasQuery = true;
14592             
14593             if(this.lastQuery != q || this.alwaysQuery){
14594                 this.lastQuery = q;
14595                 if(this.mode == 'local'){
14596                     this.selectedIndex = -1;
14597                     if(forceAll){
14598                         this.store.clearFilter();
14599                     }else{
14600                         
14601                         if(this.specialFilter){
14602                             this.fireEvent('specialfilter', this);
14603                             this.onLoad();
14604                             return;
14605                         }
14606                         
14607                         this.store.filter(this.displayField, q);
14608                     }
14609                     
14610                     this.store.fireEvent("datachanged", this.store);
14611                     
14612                     this.onLoad();
14613                     
14614                     
14615                 }else{
14616                     
14617                     this.store.baseParams[this.queryParam] = q;
14618                     
14619                     var options = {params : this.getParams(q)};
14620                     
14621                     if(this.loadNext){
14622                         options.add = true;
14623                         options.params.start = this.page * this.pageSize;
14624                     }
14625                     
14626                     this.store.load(options);
14627                     
14628                     /*
14629                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14630                      *  we should expand the list on onLoad
14631                      *  so command out it
14632                      */
14633 //                    this.expand();
14634                 }
14635             }else{
14636                 this.selectedIndex = -1;
14637                 this.onLoad();   
14638             }
14639         }
14640         
14641         this.loadNext = false;
14642     },
14643     
14644     // private
14645     getParams : function(q){
14646         var p = {};
14647         //p[this.queryParam] = q;
14648         
14649         if(this.pageSize){
14650             p.start = 0;
14651             p.limit = this.pageSize;
14652         }
14653         return p;
14654     },
14655
14656     /**
14657      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14658      */
14659     collapse : function(){
14660         if(!this.isExpanded()){
14661             return;
14662         }
14663         
14664         this.list.hide();
14665         
14666         this.hasFocus = false;
14667         
14668         if(this.tickable){
14669             this.okBtn.hide();
14670             this.cancelBtn.hide();
14671             this.trigger.show();
14672             
14673             if(this.editable){
14674                 this.tickableInputEl().dom.value = '';
14675                 this.tickableInputEl().blur();
14676             }
14677             
14678         }
14679         
14680         Roo.get(document).un('mousedown', this.collapseIf, this);
14681         Roo.get(document).un('mousewheel', this.collapseIf, this);
14682         if (!this.editable) {
14683             Roo.get(document).un('keydown', this.listKeyPress, this);
14684         }
14685         this.fireEvent('collapse', this);
14686         
14687         this.validate();
14688     },
14689
14690     // private
14691     collapseIf : function(e){
14692         var in_combo  = e.within(this.el);
14693         var in_list =  e.within(this.list);
14694         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14695         
14696         if (in_combo || in_list || is_list) {
14697             //e.stopPropagation();
14698             return;
14699         }
14700         
14701         if(this.tickable){
14702             this.onTickableFooterButtonClick(e, false, false);
14703         }
14704
14705         this.collapse();
14706         
14707     },
14708
14709     /**
14710      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14711      */
14712     expand : function(){
14713        
14714         if(this.isExpanded() || !this.hasFocus){
14715             return;
14716         }
14717         
14718         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14719         this.list.setWidth(lw);
14720         
14721         Roo.log('expand');
14722         
14723         this.list.show();
14724         
14725         this.restrictHeight();
14726         
14727         if(this.tickable){
14728             
14729             this.tickItems = Roo.apply([], this.item);
14730             
14731             this.okBtn.show();
14732             this.cancelBtn.show();
14733             this.trigger.hide();
14734             
14735             if(this.editable){
14736                 this.tickableInputEl().focus();
14737             }
14738             
14739         }
14740         
14741         Roo.get(document).on('mousedown', this.collapseIf, this);
14742         Roo.get(document).on('mousewheel', this.collapseIf, this);
14743         if (!this.editable) {
14744             Roo.get(document).on('keydown', this.listKeyPress, this);
14745         }
14746         
14747         this.fireEvent('expand', this);
14748     },
14749
14750     // private
14751     // Implements the default empty TriggerField.onTriggerClick function
14752     onTriggerClick : function(e)
14753     {
14754         Roo.log('trigger click');
14755         
14756         if(this.disabled || !this.triggerList){
14757             return;
14758         }
14759         
14760         this.page = 0;
14761         this.loadNext = false;
14762         
14763         if(this.isExpanded()){
14764             this.collapse();
14765             if (!this.blockFocus) {
14766                 this.inputEl().focus();
14767             }
14768             
14769         }else {
14770             this.hasFocus = true;
14771             if(this.triggerAction == 'all') {
14772                 this.doQuery(this.allQuery, true);
14773             } else {
14774                 this.doQuery(this.getRawValue());
14775             }
14776             if (!this.blockFocus) {
14777                 this.inputEl().focus();
14778             }
14779         }
14780     },
14781     
14782     onTickableTriggerClick : function(e)
14783     {
14784         if(this.disabled){
14785             return;
14786         }
14787         
14788         this.page = 0;
14789         this.loadNext = false;
14790         this.hasFocus = true;
14791         
14792         if(this.triggerAction == 'all') {
14793             this.doQuery(this.allQuery, true);
14794         } else {
14795             this.doQuery(this.getRawValue());
14796         }
14797     },
14798     
14799     onSearchFieldClick : function(e)
14800     {
14801         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14802             this.onTickableFooterButtonClick(e, false, false);
14803             return;
14804         }
14805         
14806         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14807             return;
14808         }
14809         
14810         this.page = 0;
14811         this.loadNext = false;
14812         this.hasFocus = true;
14813         
14814         if(this.triggerAction == 'all') {
14815             this.doQuery(this.allQuery, true);
14816         } else {
14817             this.doQuery(this.getRawValue());
14818         }
14819     },
14820     
14821     listKeyPress : function(e)
14822     {
14823         //Roo.log('listkeypress');
14824         // scroll to first matching element based on key pres..
14825         if (e.isSpecialKey()) {
14826             return false;
14827         }
14828         var k = String.fromCharCode(e.getKey()).toUpperCase();
14829         //Roo.log(k);
14830         var match  = false;
14831         var csel = this.view.getSelectedNodes();
14832         var cselitem = false;
14833         if (csel.length) {
14834             var ix = this.view.indexOf(csel[0]);
14835             cselitem  = this.store.getAt(ix);
14836             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14837                 cselitem = false;
14838             }
14839             
14840         }
14841         
14842         this.store.each(function(v) { 
14843             if (cselitem) {
14844                 // start at existing selection.
14845                 if (cselitem.id == v.id) {
14846                     cselitem = false;
14847                 }
14848                 return true;
14849             }
14850                 
14851             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14852                 match = this.store.indexOf(v);
14853                 return false;
14854             }
14855             return true;
14856         }, this);
14857         
14858         if (match === false) {
14859             return true; // no more action?
14860         }
14861         // scroll to?
14862         this.view.select(match);
14863         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14864         sn.scrollIntoView(sn.dom.parentNode, false);
14865     },
14866     
14867     onViewScroll : function(e, t){
14868         
14869         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){
14870             return;
14871         }
14872         
14873         this.hasQuery = true;
14874         
14875         this.loading = this.list.select('.loading', true).first();
14876         
14877         if(this.loading === null){
14878             this.list.createChild({
14879                 tag: 'div',
14880                 cls: 'loading roo-select2-more-results roo-select2-active',
14881                 html: 'Loading more results...'
14882             });
14883             
14884             this.loading = this.list.select('.loading', true).first();
14885             
14886             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14887             
14888             this.loading.hide();
14889         }
14890         
14891         this.loading.show();
14892         
14893         var _combo = this;
14894         
14895         this.page++;
14896         this.loadNext = true;
14897         
14898         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14899         
14900         return;
14901     },
14902     
14903     addItem : function(o)
14904     {   
14905         var dv = ''; // display value
14906         
14907         if (this.displayField) {
14908             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14909         } else {
14910             // this is an error condition!!!
14911             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14912         }
14913         
14914         if(!dv.length){
14915             return;
14916         }
14917         
14918         var choice = this.choices.createChild({
14919             tag: 'li',
14920             cls: 'roo-select2-search-choice',
14921             cn: [
14922                 {
14923                     tag: 'div',
14924                     html: dv
14925                 },
14926                 {
14927                     tag: 'a',
14928                     href: '#',
14929                     cls: 'roo-select2-search-choice-close fa fa-times',
14930                     tabindex: '-1'
14931                 }
14932             ]
14933             
14934         }, this.searchField);
14935         
14936         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14937         
14938         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14939         
14940         this.item.push(o);
14941         
14942         this.lastData = o;
14943         
14944         this.syncValue();
14945         
14946         this.inputEl().dom.value = '';
14947         
14948         this.validate();
14949     },
14950     
14951     onRemoveItem : function(e, _self, o)
14952     {
14953         e.preventDefault();
14954         
14955         this.lastItem = Roo.apply([], this.item);
14956         
14957         var index = this.item.indexOf(o.data) * 1;
14958         
14959         if( index < 0){
14960             Roo.log('not this item?!');
14961             return;
14962         }
14963         
14964         this.item.splice(index, 1);
14965         o.item.remove();
14966         
14967         this.syncValue();
14968         
14969         this.fireEvent('remove', this, e);
14970         
14971         this.validate();
14972         
14973     },
14974     
14975     syncValue : function()
14976     {
14977         if(!this.item.length){
14978             this.clearValue();
14979             return;
14980         }
14981             
14982         var value = [];
14983         var _this = this;
14984         Roo.each(this.item, function(i){
14985             if(_this.valueField){
14986                 value.push(i[_this.valueField]);
14987                 return;
14988             }
14989
14990             value.push(i);
14991         });
14992
14993         this.value = value.join(',');
14994
14995         if(this.hiddenField){
14996             this.hiddenField.dom.value = this.value;
14997         }
14998         
14999         this.store.fireEvent("datachanged", this.store);
15000         
15001         this.validate();
15002     },
15003     
15004     clearItem : function()
15005     {
15006         if(!this.multiple){
15007             return;
15008         }
15009         
15010         this.item = [];
15011         
15012         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15013            c.remove();
15014         });
15015         
15016         this.syncValue();
15017         
15018         this.validate();
15019         
15020         if(this.tickable && !Roo.isTouch){
15021             this.view.refresh();
15022         }
15023     },
15024     
15025     inputEl: function ()
15026     {
15027         if(Roo.isIOS && this.useNativeIOS){
15028             return this.el.select('select.roo-ios-select', true).first();
15029         }
15030         
15031         if(Roo.isTouch && this.mobileTouchView){
15032             return this.el.select('input.form-control',true).first();
15033         }
15034         
15035         if(this.tickable){
15036             return this.searchField;
15037         }
15038         
15039         return this.el.select('input.form-control',true).first();
15040     },
15041     
15042     onTickableFooterButtonClick : function(e, btn, el)
15043     {
15044         e.preventDefault();
15045         
15046         this.lastItem = Roo.apply([], this.item);
15047         
15048         if(btn && btn.name == 'cancel'){
15049             this.tickItems = Roo.apply([], this.item);
15050             this.collapse();
15051             return;
15052         }
15053         
15054         this.clearItem();
15055         
15056         var _this = this;
15057         
15058         Roo.each(this.tickItems, function(o){
15059             _this.addItem(o);
15060         });
15061         
15062         this.collapse();
15063         
15064     },
15065     
15066     validate : function()
15067     {
15068         if(this.getVisibilityEl().hasClass('hidden')){
15069             return true;
15070         }
15071         
15072         var v = this.getRawValue();
15073         
15074         if(this.multiple){
15075             v = this.getValue();
15076         }
15077         
15078         if(this.disabled || this.allowBlank || v.length){
15079             this.markValid();
15080             return true;
15081         }
15082         
15083         this.markInvalid();
15084         return false;
15085     },
15086     
15087     tickableInputEl : function()
15088     {
15089         if(!this.tickable || !this.editable){
15090             return this.inputEl();
15091         }
15092         
15093         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15094     },
15095     
15096     
15097     getAutoCreateTouchView : function()
15098     {
15099         var id = Roo.id();
15100         
15101         var cfg = {
15102             cls: 'form-group' //input-group
15103         };
15104         
15105         var input =  {
15106             tag: 'input',
15107             id : id,
15108             type : this.inputType,
15109             cls : 'form-control x-combo-noedit',
15110             autocomplete: 'new-password',
15111             placeholder : this.placeholder || '',
15112             readonly : true
15113         };
15114         
15115         if (this.name) {
15116             input.name = this.name;
15117         }
15118         
15119         if (this.size) {
15120             input.cls += ' input-' + this.size;
15121         }
15122         
15123         if (this.disabled) {
15124             input.disabled = true;
15125         }
15126         
15127         var inputblock = {
15128             cls : '',
15129             cn : [
15130                 input
15131             ]
15132         };
15133         
15134         if(this.before){
15135             inputblock.cls += ' input-group';
15136             
15137             inputblock.cn.unshift({
15138                 tag :'span',
15139                 cls : 'input-group-addon input-group-prepend input-group-text',
15140                 html : this.before
15141             });
15142         }
15143         
15144         if(this.removable && !this.multiple){
15145             inputblock.cls += ' roo-removable';
15146             
15147             inputblock.cn.push({
15148                 tag: 'button',
15149                 html : 'x',
15150                 cls : 'roo-combo-removable-btn close'
15151             });
15152         }
15153
15154         if(this.hasFeedback && !this.allowBlank){
15155             
15156             inputblock.cls += ' has-feedback';
15157             
15158             inputblock.cn.push({
15159                 tag: 'span',
15160                 cls: 'glyphicon form-control-feedback'
15161             });
15162             
15163         }
15164         
15165         if (this.after) {
15166             
15167             inputblock.cls += (this.before) ? '' : ' input-group';
15168             
15169             inputblock.cn.push({
15170                 tag :'span',
15171                 cls : 'input-group-addon input-group-append input-group-text',
15172                 html : this.after
15173             });
15174         }
15175
15176         
15177         var ibwrap = inputblock;
15178         
15179         if(this.multiple){
15180             ibwrap = {
15181                 tag: 'ul',
15182                 cls: 'roo-select2-choices',
15183                 cn:[
15184                     {
15185                         tag: 'li',
15186                         cls: 'roo-select2-search-field',
15187                         cn: [
15188
15189                             inputblock
15190                         ]
15191                     }
15192                 ]
15193             };
15194         
15195             
15196         }
15197         
15198         var combobox = {
15199             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15200             cn: [
15201                 {
15202                     tag: 'input',
15203                     type : 'hidden',
15204                     cls: 'form-hidden-field'
15205                 },
15206                 ibwrap
15207             ]
15208         };
15209         
15210         if(!this.multiple && this.showToggleBtn){
15211             
15212             var caret = {
15213                         tag: 'span',
15214                         cls: 'caret'
15215             };
15216             
15217             if (this.caret != false) {
15218                 caret = {
15219                      tag: 'i',
15220                      cls: 'fa fa-' + this.caret
15221                 };
15222                 
15223             }
15224             
15225             combobox.cn.push({
15226                 tag :'span',
15227                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15228                 cn : [
15229                     caret,
15230                     {
15231                         tag: 'span',
15232                         cls: 'combobox-clear',
15233                         cn  : [
15234                             {
15235                                 tag : 'i',
15236                                 cls: 'icon-remove'
15237                             }
15238                         ]
15239                     }
15240                 ]
15241
15242             })
15243         }
15244         
15245         if(this.multiple){
15246             combobox.cls += ' roo-select2-container-multi';
15247         }
15248         
15249         var align = this.labelAlign || this.parentLabelAlign();
15250         
15251         if (align ==='left' && this.fieldLabel.length) {
15252
15253             cfg.cn = [
15254                 {
15255                    tag : 'i',
15256                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15257                    tooltip : 'This field is required'
15258                 },
15259                 {
15260                     tag: 'label',
15261                     cls : 'control-label col-form-label',
15262                     html : this.fieldLabel
15263
15264                 },
15265                 {
15266                     cls : '', 
15267                     cn: [
15268                         combobox
15269                     ]
15270                 }
15271             ];
15272             
15273             var labelCfg = cfg.cn[1];
15274             var contentCfg = cfg.cn[2];
15275             
15276
15277             if(this.indicatorpos == 'right'){
15278                 cfg.cn = [
15279                     {
15280                         tag: 'label',
15281                         'for' :  id,
15282                         cls : 'control-label col-form-label',
15283                         cn : [
15284                             {
15285                                 tag : 'span',
15286                                 html : this.fieldLabel
15287                             },
15288                             {
15289                                 tag : 'i',
15290                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15291                                 tooltip : 'This field is required'
15292                             }
15293                         ]
15294                     },
15295                     {
15296                         cls : "",
15297                         cn: [
15298                             combobox
15299                         ]
15300                     }
15301
15302                 ];
15303                 
15304                 labelCfg = cfg.cn[0];
15305                 contentCfg = cfg.cn[1];
15306             }
15307             
15308            
15309             
15310             if(this.labelWidth > 12){
15311                 labelCfg.style = "width: " + this.labelWidth + 'px';
15312             }
15313             
15314             if(this.labelWidth < 13 && this.labelmd == 0){
15315                 this.labelmd = this.labelWidth;
15316             }
15317             
15318             if(this.labellg > 0){
15319                 labelCfg.cls += ' col-lg-' + this.labellg;
15320                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15321             }
15322             
15323             if(this.labelmd > 0){
15324                 labelCfg.cls += ' col-md-' + this.labelmd;
15325                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15326             }
15327             
15328             if(this.labelsm > 0){
15329                 labelCfg.cls += ' col-sm-' + this.labelsm;
15330                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15331             }
15332             
15333             if(this.labelxs > 0){
15334                 labelCfg.cls += ' col-xs-' + this.labelxs;
15335                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15336             }
15337                 
15338                 
15339         } else if ( this.fieldLabel.length) {
15340             cfg.cn = [
15341                 {
15342                    tag : 'i',
15343                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15344                    tooltip : 'This field is required'
15345                 },
15346                 {
15347                     tag: 'label',
15348                     cls : 'control-label',
15349                     html : this.fieldLabel
15350
15351                 },
15352                 {
15353                     cls : '', 
15354                     cn: [
15355                         combobox
15356                     ]
15357                 }
15358             ];
15359             
15360             if(this.indicatorpos == 'right'){
15361                 cfg.cn = [
15362                     {
15363                         tag: 'label',
15364                         cls : 'control-label',
15365                         html : this.fieldLabel,
15366                         cn : [
15367                             {
15368                                tag : 'i',
15369                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15370                                tooltip : 'This field is required'
15371                             }
15372                         ]
15373                     },
15374                     {
15375                         cls : '', 
15376                         cn: [
15377                             combobox
15378                         ]
15379                     }
15380                 ];
15381             }
15382         } else {
15383             cfg.cn = combobox;    
15384         }
15385         
15386         
15387         var settings = this;
15388         
15389         ['xs','sm','md','lg'].map(function(size){
15390             if (settings[size]) {
15391                 cfg.cls += ' col-' + size + '-' + settings[size];
15392             }
15393         });
15394         
15395         return cfg;
15396     },
15397     
15398     initTouchView : function()
15399     {
15400         this.renderTouchView();
15401         
15402         this.touchViewEl.on('scroll', function(){
15403             this.el.dom.scrollTop = 0;
15404         }, this);
15405         
15406         this.originalValue = this.getValue();
15407         
15408         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15409         
15410         this.inputEl().on("click", this.showTouchView, this);
15411         if (this.triggerEl) {
15412             this.triggerEl.on("click", this.showTouchView, this);
15413         }
15414         
15415         
15416         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15417         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15418         
15419         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15420         
15421         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15422         this.store.on('load', this.onTouchViewLoad, this);
15423         this.store.on('loadexception', this.onTouchViewLoadException, this);
15424         
15425         if(this.hiddenName){
15426             
15427             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15428             
15429             this.hiddenField.dom.value =
15430                 this.hiddenValue !== undefined ? this.hiddenValue :
15431                 this.value !== undefined ? this.value : '';
15432         
15433             this.el.dom.removeAttribute('name');
15434             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15435         }
15436         
15437         if(this.multiple){
15438             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15439             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15440         }
15441         
15442         if(this.removable && !this.multiple){
15443             var close = this.closeTriggerEl();
15444             if(close){
15445                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15446                 close.on('click', this.removeBtnClick, this, close);
15447             }
15448         }
15449         /*
15450          * fix the bug in Safari iOS8
15451          */
15452         this.inputEl().on("focus", function(e){
15453             document.activeElement.blur();
15454         }, this);
15455         
15456         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15457         
15458         return;
15459         
15460         
15461     },
15462     
15463     renderTouchView : function()
15464     {
15465         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15466         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15467         
15468         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15469         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15470         
15471         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15472         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15473         this.touchViewBodyEl.setStyle('overflow', 'auto');
15474         
15475         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15476         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15477         
15478         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15479         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15480         
15481     },
15482     
15483     showTouchView : function()
15484     {
15485         if(this.disabled){
15486             return;
15487         }
15488         
15489         this.touchViewHeaderEl.hide();
15490
15491         if(this.modalTitle.length){
15492             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15493             this.touchViewHeaderEl.show();
15494         }
15495
15496         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15497         this.touchViewEl.show();
15498
15499         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15500         
15501         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15502         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15503
15504         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15505
15506         if(this.modalTitle.length){
15507             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15508         }
15509         
15510         this.touchViewBodyEl.setHeight(bodyHeight);
15511
15512         if(this.animate){
15513             var _this = this;
15514             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15515         }else{
15516             this.touchViewEl.addClass('in');
15517         }
15518         
15519         if(this._touchViewMask){
15520             Roo.get(document.body).addClass("x-body-masked");
15521             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15522             this._touchViewMask.setStyle('z-index', 10000);
15523             this._touchViewMask.addClass('show');
15524         }
15525         
15526         this.doTouchViewQuery();
15527         
15528     },
15529     
15530     hideTouchView : function()
15531     {
15532         this.touchViewEl.removeClass('in');
15533
15534         if(this.animate){
15535             var _this = this;
15536             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15537         }else{
15538             this.touchViewEl.setStyle('display', 'none');
15539         }
15540         
15541         if(this._touchViewMask){
15542             this._touchViewMask.removeClass('show');
15543             Roo.get(document.body).removeClass("x-body-masked");
15544         }
15545     },
15546     
15547     setTouchViewValue : function()
15548     {
15549         if(this.multiple){
15550             this.clearItem();
15551         
15552             var _this = this;
15553
15554             Roo.each(this.tickItems, function(o){
15555                 this.addItem(o);
15556             }, this);
15557         }
15558         
15559         this.hideTouchView();
15560     },
15561     
15562     doTouchViewQuery : function()
15563     {
15564         var qe = {
15565             query: '',
15566             forceAll: true,
15567             combo: this,
15568             cancel:false
15569         };
15570         
15571         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15572             return false;
15573         }
15574         
15575         if(!this.alwaysQuery || this.mode == 'local'){
15576             this.onTouchViewLoad();
15577             return;
15578         }
15579         
15580         this.store.load();
15581     },
15582     
15583     onTouchViewBeforeLoad : function(combo,opts)
15584     {
15585         return;
15586     },
15587
15588     // private
15589     onTouchViewLoad : function()
15590     {
15591         if(this.store.getCount() < 1){
15592             this.onTouchViewEmptyResults();
15593             return;
15594         }
15595         
15596         this.clearTouchView();
15597         
15598         var rawValue = this.getRawValue();
15599         
15600         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15601         
15602         this.tickItems = [];
15603         
15604         this.store.data.each(function(d, rowIndex){
15605             var row = this.touchViewListGroup.createChild(template);
15606             
15607             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15608                 row.addClass(d.data.cls);
15609             }
15610             
15611             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15612                 var cfg = {
15613                     data : d.data,
15614                     html : d.data[this.displayField]
15615                 };
15616                 
15617                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15618                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15619                 }
15620             }
15621             row.removeClass('selected');
15622             if(!this.multiple && this.valueField &&
15623                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15624             {
15625                 // radio buttons..
15626                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15627                 row.addClass('selected');
15628             }
15629             
15630             if(this.multiple && this.valueField &&
15631                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15632             {
15633                 
15634                 // checkboxes...
15635                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15636                 this.tickItems.push(d.data);
15637             }
15638             
15639             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15640             
15641         }, this);
15642         
15643         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15644         
15645         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15646
15647         if(this.modalTitle.length){
15648             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15649         }
15650
15651         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15652         
15653         if(this.mobile_restrict_height && listHeight < bodyHeight){
15654             this.touchViewBodyEl.setHeight(listHeight);
15655         }
15656         
15657         var _this = this;
15658         
15659         if(firstChecked && listHeight > bodyHeight){
15660             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15661         }
15662         
15663     },
15664     
15665     onTouchViewLoadException : function()
15666     {
15667         this.hideTouchView();
15668     },
15669     
15670     onTouchViewEmptyResults : function()
15671     {
15672         this.clearTouchView();
15673         
15674         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15675         
15676         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15677         
15678     },
15679     
15680     clearTouchView : function()
15681     {
15682         this.touchViewListGroup.dom.innerHTML = '';
15683     },
15684     
15685     onTouchViewClick : function(e, el, o)
15686     {
15687         e.preventDefault();
15688         
15689         var row = o.row;
15690         var rowIndex = o.rowIndex;
15691         
15692         var r = this.store.getAt(rowIndex);
15693         
15694         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15695             
15696             if(!this.multiple){
15697                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15698                     c.dom.removeAttribute('checked');
15699                 }, this);
15700
15701                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15702
15703                 this.setFromData(r.data);
15704
15705                 var close = this.closeTriggerEl();
15706
15707                 if(close){
15708                     close.show();
15709                 }
15710
15711                 this.hideTouchView();
15712
15713                 this.fireEvent('select', this, r, rowIndex);
15714
15715                 return;
15716             }
15717
15718             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15719                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15720                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15721                 return;
15722             }
15723
15724             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15725             this.addItem(r.data);
15726             this.tickItems.push(r.data);
15727         }
15728     },
15729     
15730     getAutoCreateNativeIOS : function()
15731     {
15732         var cfg = {
15733             cls: 'form-group' //input-group,
15734         };
15735         
15736         var combobox =  {
15737             tag: 'select',
15738             cls : 'roo-ios-select'
15739         };
15740         
15741         if (this.name) {
15742             combobox.name = this.name;
15743         }
15744         
15745         if (this.disabled) {
15746             combobox.disabled = true;
15747         }
15748         
15749         var settings = this;
15750         
15751         ['xs','sm','md','lg'].map(function(size){
15752             if (settings[size]) {
15753                 cfg.cls += ' col-' + size + '-' + settings[size];
15754             }
15755         });
15756         
15757         cfg.cn = combobox;
15758         
15759         return cfg;
15760         
15761     },
15762     
15763     initIOSView : function()
15764     {
15765         this.store.on('load', this.onIOSViewLoad, this);
15766         
15767         return;
15768     },
15769     
15770     onIOSViewLoad : function()
15771     {
15772         if(this.store.getCount() < 1){
15773             return;
15774         }
15775         
15776         this.clearIOSView();
15777         
15778         if(this.allowBlank) {
15779             
15780             var default_text = '-- SELECT --';
15781             
15782             if(this.placeholder.length){
15783                 default_text = this.placeholder;
15784             }
15785             
15786             if(this.emptyTitle.length){
15787                 default_text += ' - ' + this.emptyTitle + ' -';
15788             }
15789             
15790             var opt = this.inputEl().createChild({
15791                 tag: 'option',
15792                 value : 0,
15793                 html : default_text
15794             });
15795             
15796             var o = {};
15797             o[this.valueField] = 0;
15798             o[this.displayField] = default_text;
15799             
15800             this.ios_options.push({
15801                 data : o,
15802                 el : opt
15803             });
15804             
15805         }
15806         
15807         this.store.data.each(function(d, rowIndex){
15808             
15809             var html = '';
15810             
15811             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15812                 html = d.data[this.displayField];
15813             }
15814             
15815             var value = '';
15816             
15817             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15818                 value = d.data[this.valueField];
15819             }
15820             
15821             var option = {
15822                 tag: 'option',
15823                 value : value,
15824                 html : html
15825             };
15826             
15827             if(this.value == d.data[this.valueField]){
15828                 option['selected'] = true;
15829             }
15830             
15831             var opt = this.inputEl().createChild(option);
15832             
15833             this.ios_options.push({
15834                 data : d.data,
15835                 el : opt
15836             });
15837             
15838         }, this);
15839         
15840         this.inputEl().on('change', function(){
15841            this.fireEvent('select', this);
15842         }, this);
15843         
15844     },
15845     
15846     clearIOSView: function()
15847     {
15848         this.inputEl().dom.innerHTML = '';
15849         
15850         this.ios_options = [];
15851     },
15852     
15853     setIOSValue: function(v)
15854     {
15855         this.value = v;
15856         
15857         if(!this.ios_options){
15858             return;
15859         }
15860         
15861         Roo.each(this.ios_options, function(opts){
15862            
15863            opts.el.dom.removeAttribute('selected');
15864            
15865            if(opts.data[this.valueField] != v){
15866                return;
15867            }
15868            
15869            opts.el.dom.setAttribute('selected', true);
15870            
15871         }, this);
15872     }
15873
15874     /** 
15875     * @cfg {Boolean} grow 
15876     * @hide 
15877     */
15878     /** 
15879     * @cfg {Number} growMin 
15880     * @hide 
15881     */
15882     /** 
15883     * @cfg {Number} growMax 
15884     * @hide 
15885     */
15886     /**
15887      * @hide
15888      * @method autoSize
15889      */
15890 });
15891
15892 Roo.apply(Roo.bootstrap.ComboBox,  {
15893     
15894     header : {
15895         tag: 'div',
15896         cls: 'modal-header',
15897         cn: [
15898             {
15899                 tag: 'h4',
15900                 cls: 'modal-title'
15901             }
15902         ]
15903     },
15904     
15905     body : {
15906         tag: 'div',
15907         cls: 'modal-body',
15908         cn: [
15909             {
15910                 tag: 'ul',
15911                 cls: 'list-group'
15912             }
15913         ]
15914     },
15915     
15916     listItemRadio : {
15917         tag: 'li',
15918         cls: 'list-group-item',
15919         cn: [
15920             {
15921                 tag: 'span',
15922                 cls: 'roo-combobox-list-group-item-value'
15923             },
15924             {
15925                 tag: 'div',
15926                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15927                 cn: [
15928                     {
15929                         tag: 'input',
15930                         type: 'radio'
15931                     },
15932                     {
15933                         tag: 'label'
15934                     }
15935                 ]
15936             }
15937         ]
15938     },
15939     
15940     listItemCheckbox : {
15941         tag: 'li',
15942         cls: 'list-group-item',
15943         cn: [
15944             {
15945                 tag: 'span',
15946                 cls: 'roo-combobox-list-group-item-value'
15947             },
15948             {
15949                 tag: 'div',
15950                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15951                 cn: [
15952                     {
15953                         tag: 'input',
15954                         type: 'checkbox'
15955                     },
15956                     {
15957                         tag: 'label'
15958                     }
15959                 ]
15960             }
15961         ]
15962     },
15963     
15964     emptyResult : {
15965         tag: 'div',
15966         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15967     },
15968     
15969     footer : {
15970         tag: 'div',
15971         cls: 'modal-footer',
15972         cn: [
15973             {
15974                 tag: 'div',
15975                 cls: 'row',
15976                 cn: [
15977                     {
15978                         tag: 'div',
15979                         cls: 'col-xs-6 text-left',
15980                         cn: {
15981                             tag: 'button',
15982                             cls: 'btn btn-danger roo-touch-view-cancel',
15983                             html: 'Cancel'
15984                         }
15985                     },
15986                     {
15987                         tag: 'div',
15988                         cls: 'col-xs-6 text-right',
15989                         cn: {
15990                             tag: 'button',
15991                             cls: 'btn btn-success roo-touch-view-ok',
15992                             html: 'OK'
15993                         }
15994                     }
15995                 ]
15996             }
15997         ]
15998         
15999     }
16000 });
16001
16002 Roo.apply(Roo.bootstrap.ComboBox,  {
16003     
16004     touchViewTemplate : {
16005         tag: 'div',
16006         cls: 'modal fade roo-combobox-touch-view',
16007         cn: [
16008             {
16009                 tag: 'div',
16010                 cls: 'modal-dialog',
16011                 style : 'position:fixed', // we have to fix position....
16012                 cn: [
16013                     {
16014                         tag: 'div',
16015                         cls: 'modal-content',
16016                         cn: [
16017                             Roo.bootstrap.ComboBox.header,
16018                             Roo.bootstrap.ComboBox.body,
16019                             Roo.bootstrap.ComboBox.footer
16020                         ]
16021                     }
16022                 ]
16023             }
16024         ]
16025     }
16026 });/*
16027  * Based on:
16028  * Ext JS Library 1.1.1
16029  * Copyright(c) 2006-2007, Ext JS, LLC.
16030  *
16031  * Originally Released Under LGPL - original licence link has changed is not relivant.
16032  *
16033  * Fork - LGPL
16034  * <script type="text/javascript">
16035  */
16036
16037 /**
16038  * @class Roo.View
16039  * @extends Roo.util.Observable
16040  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16041  * This class also supports single and multi selection modes. <br>
16042  * Create a data model bound view:
16043  <pre><code>
16044  var store = new Roo.data.Store(...);
16045
16046  var view = new Roo.View({
16047     el : "my-element",
16048     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16049  
16050     singleSelect: true,
16051     selectedClass: "ydataview-selected",
16052     store: store
16053  });
16054
16055  // listen for node click?
16056  view.on("click", function(vw, index, node, e){
16057  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16058  });
16059
16060  // load XML data
16061  dataModel.load("foobar.xml");
16062  </code></pre>
16063  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16064  * <br><br>
16065  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16066  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16067  * 
16068  * Note: old style constructor is still suported (container, template, config)
16069  * 
16070  * @constructor
16071  * Create a new View
16072  * @param {Object} config The config object
16073  * 
16074  */
16075 Roo.View = function(config, depreciated_tpl, depreciated_config){
16076     
16077     this.parent = false;
16078     
16079     if (typeof(depreciated_tpl) == 'undefined') {
16080         // new way.. - universal constructor.
16081         Roo.apply(this, config);
16082         this.el  = Roo.get(this.el);
16083     } else {
16084         // old format..
16085         this.el  = Roo.get(config);
16086         this.tpl = depreciated_tpl;
16087         Roo.apply(this, depreciated_config);
16088     }
16089     this.wrapEl  = this.el.wrap().wrap();
16090     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16091     
16092     
16093     if(typeof(this.tpl) == "string"){
16094         this.tpl = new Roo.Template(this.tpl);
16095     } else {
16096         // support xtype ctors..
16097         this.tpl = new Roo.factory(this.tpl, Roo);
16098     }
16099     
16100     
16101     this.tpl.compile();
16102     
16103     /** @private */
16104     this.addEvents({
16105         /**
16106          * @event beforeclick
16107          * Fires before a click is processed. Returns false to cancel the default action.
16108          * @param {Roo.View} this
16109          * @param {Number} index The index of the target node
16110          * @param {HTMLElement} node The target node
16111          * @param {Roo.EventObject} e The raw event object
16112          */
16113             "beforeclick" : true,
16114         /**
16115          * @event click
16116          * Fires when a template node is clicked.
16117          * @param {Roo.View} this
16118          * @param {Number} index The index of the target node
16119          * @param {HTMLElement} node The target node
16120          * @param {Roo.EventObject} e The raw event object
16121          */
16122             "click" : true,
16123         /**
16124          * @event dblclick
16125          * Fires when a template node is double clicked.
16126          * @param {Roo.View} this
16127          * @param {Number} index The index of the target node
16128          * @param {HTMLElement} node The target node
16129          * @param {Roo.EventObject} e The raw event object
16130          */
16131             "dblclick" : true,
16132         /**
16133          * @event contextmenu
16134          * Fires when a template node is right clicked.
16135          * @param {Roo.View} this
16136          * @param {Number} index The index of the target node
16137          * @param {HTMLElement} node The target node
16138          * @param {Roo.EventObject} e The raw event object
16139          */
16140             "contextmenu" : true,
16141         /**
16142          * @event selectionchange
16143          * Fires when the selected nodes change.
16144          * @param {Roo.View} this
16145          * @param {Array} selections Array of the selected nodes
16146          */
16147             "selectionchange" : true,
16148     
16149         /**
16150          * @event beforeselect
16151          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16152          * @param {Roo.View} this
16153          * @param {HTMLElement} node The node to be selected
16154          * @param {Array} selections Array of currently selected nodes
16155          */
16156             "beforeselect" : true,
16157         /**
16158          * @event preparedata
16159          * Fires on every row to render, to allow you to change the data.
16160          * @param {Roo.View} this
16161          * @param {Object} data to be rendered (change this)
16162          */
16163           "preparedata" : true
16164           
16165           
16166         });
16167
16168
16169
16170     this.el.on({
16171         "click": this.onClick,
16172         "dblclick": this.onDblClick,
16173         "contextmenu": this.onContextMenu,
16174         scope:this
16175     });
16176
16177     this.selections = [];
16178     this.nodes = [];
16179     this.cmp = new Roo.CompositeElementLite([]);
16180     if(this.store){
16181         this.store = Roo.factory(this.store, Roo.data);
16182         this.setStore(this.store, true);
16183     }
16184     
16185     if ( this.footer && this.footer.xtype) {
16186            
16187          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16188         
16189         this.footer.dataSource = this.store;
16190         this.footer.container = fctr;
16191         this.footer = Roo.factory(this.footer, Roo);
16192         fctr.insertFirst(this.el);
16193         
16194         // this is a bit insane - as the paging toolbar seems to detach the el..
16195 //        dom.parentNode.parentNode.parentNode
16196          // they get detached?
16197     }
16198     
16199     
16200     Roo.View.superclass.constructor.call(this);
16201     
16202     
16203 };
16204
16205 Roo.extend(Roo.View, Roo.util.Observable, {
16206     
16207      /**
16208      * @cfg {Roo.data.Store} store Data store to load data from.
16209      */
16210     store : false,
16211     
16212     /**
16213      * @cfg {String|Roo.Element} el The container element.
16214      */
16215     el : '',
16216     
16217     /**
16218      * @cfg {String|Roo.Template} tpl The template used by this View 
16219      */
16220     tpl : false,
16221     /**
16222      * @cfg {String} dataName the named area of the template to use as the data area
16223      *                          Works with domtemplates roo-name="name"
16224      */
16225     dataName: false,
16226     /**
16227      * @cfg {String} selectedClass The css class to add to selected nodes
16228      */
16229     selectedClass : "x-view-selected",
16230      /**
16231      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16232      */
16233     emptyText : "",
16234     
16235     /**
16236      * @cfg {String} text to display on mask (default Loading)
16237      */
16238     mask : false,
16239     /**
16240      * @cfg {Boolean} multiSelect Allow multiple selection
16241      */
16242     multiSelect : false,
16243     /**
16244      * @cfg {Boolean} singleSelect Allow single selection
16245      */
16246     singleSelect:  false,
16247     
16248     /**
16249      * @cfg {Boolean} toggleSelect - selecting 
16250      */
16251     toggleSelect : false,
16252     
16253     /**
16254      * @cfg {Boolean} tickable - selecting 
16255      */
16256     tickable : false,
16257     
16258     /**
16259      * Returns the element this view is bound to.
16260      * @return {Roo.Element}
16261      */
16262     getEl : function(){
16263         return this.wrapEl;
16264     },
16265     
16266     
16267
16268     /**
16269      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16270      */
16271     refresh : function(){
16272         //Roo.log('refresh');
16273         var t = this.tpl;
16274         
16275         // if we are using something like 'domtemplate', then
16276         // the what gets used is:
16277         // t.applySubtemplate(NAME, data, wrapping data..)
16278         // the outer template then get' applied with
16279         //     the store 'extra data'
16280         // and the body get's added to the
16281         //      roo-name="data" node?
16282         //      <span class='roo-tpl-{name}'></span> ?????
16283         
16284         
16285         
16286         this.clearSelections();
16287         this.el.update("");
16288         var html = [];
16289         var records = this.store.getRange();
16290         if(records.length < 1) {
16291             
16292             // is this valid??  = should it render a template??
16293             
16294             this.el.update(this.emptyText);
16295             return;
16296         }
16297         var el = this.el;
16298         if (this.dataName) {
16299             this.el.update(t.apply(this.store.meta)); //????
16300             el = this.el.child('.roo-tpl-' + this.dataName);
16301         }
16302         
16303         for(var i = 0, len = records.length; i < len; i++){
16304             var data = this.prepareData(records[i].data, i, records[i]);
16305             this.fireEvent("preparedata", this, data, i, records[i]);
16306             
16307             var d = Roo.apply({}, data);
16308             
16309             if(this.tickable){
16310                 Roo.apply(d, {'roo-id' : Roo.id()});
16311                 
16312                 var _this = this;
16313             
16314                 Roo.each(this.parent.item, function(item){
16315                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16316                         return;
16317                     }
16318                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16319                 });
16320             }
16321             
16322             html[html.length] = Roo.util.Format.trim(
16323                 this.dataName ?
16324                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16325                     t.apply(d)
16326             );
16327         }
16328         
16329         
16330         
16331         el.update(html.join(""));
16332         this.nodes = el.dom.childNodes;
16333         this.updateIndexes(0);
16334     },
16335     
16336
16337     /**
16338      * Function to override to reformat the data that is sent to
16339      * the template for each node.
16340      * DEPRICATED - use the preparedata event handler.
16341      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16342      * a JSON object for an UpdateManager bound view).
16343      */
16344     prepareData : function(data, index, record)
16345     {
16346         this.fireEvent("preparedata", this, data, index, record);
16347         return data;
16348     },
16349
16350     onUpdate : function(ds, record){
16351         // Roo.log('on update');   
16352         this.clearSelections();
16353         var index = this.store.indexOf(record);
16354         var n = this.nodes[index];
16355         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16356         n.parentNode.removeChild(n);
16357         this.updateIndexes(index, index);
16358     },
16359
16360     
16361     
16362 // --------- FIXME     
16363     onAdd : function(ds, records, index)
16364     {
16365         //Roo.log(['on Add', ds, records, index] );        
16366         this.clearSelections();
16367         if(this.nodes.length == 0){
16368             this.refresh();
16369             return;
16370         }
16371         var n = this.nodes[index];
16372         for(var i = 0, len = records.length; i < len; i++){
16373             var d = this.prepareData(records[i].data, i, records[i]);
16374             if(n){
16375                 this.tpl.insertBefore(n, d);
16376             }else{
16377                 
16378                 this.tpl.append(this.el, d);
16379             }
16380         }
16381         this.updateIndexes(index);
16382     },
16383
16384     onRemove : function(ds, record, index){
16385        // Roo.log('onRemove');
16386         this.clearSelections();
16387         var el = this.dataName  ?
16388             this.el.child('.roo-tpl-' + this.dataName) :
16389             this.el; 
16390         
16391         el.dom.removeChild(this.nodes[index]);
16392         this.updateIndexes(index);
16393     },
16394
16395     /**
16396      * Refresh an individual node.
16397      * @param {Number} index
16398      */
16399     refreshNode : function(index){
16400         this.onUpdate(this.store, this.store.getAt(index));
16401     },
16402
16403     updateIndexes : function(startIndex, endIndex){
16404         var ns = this.nodes;
16405         startIndex = startIndex || 0;
16406         endIndex = endIndex || ns.length - 1;
16407         for(var i = startIndex; i <= endIndex; i++){
16408             ns[i].nodeIndex = i;
16409         }
16410     },
16411
16412     /**
16413      * Changes the data store this view uses and refresh the view.
16414      * @param {Store} store
16415      */
16416     setStore : function(store, initial){
16417         if(!initial && this.store){
16418             this.store.un("datachanged", this.refresh);
16419             this.store.un("add", this.onAdd);
16420             this.store.un("remove", this.onRemove);
16421             this.store.un("update", this.onUpdate);
16422             this.store.un("clear", this.refresh);
16423             this.store.un("beforeload", this.onBeforeLoad);
16424             this.store.un("load", this.onLoad);
16425             this.store.un("loadexception", this.onLoad);
16426         }
16427         if(store){
16428           
16429             store.on("datachanged", this.refresh, this);
16430             store.on("add", this.onAdd, this);
16431             store.on("remove", this.onRemove, this);
16432             store.on("update", this.onUpdate, this);
16433             store.on("clear", this.refresh, this);
16434             store.on("beforeload", this.onBeforeLoad, this);
16435             store.on("load", this.onLoad, this);
16436             store.on("loadexception", this.onLoad, this);
16437         }
16438         
16439         if(store){
16440             this.refresh();
16441         }
16442     },
16443     /**
16444      * onbeforeLoad - masks the loading area.
16445      *
16446      */
16447     onBeforeLoad : function(store,opts)
16448     {
16449          //Roo.log('onBeforeLoad');   
16450         if (!opts.add) {
16451             this.el.update("");
16452         }
16453         this.el.mask(this.mask ? this.mask : "Loading" ); 
16454     },
16455     onLoad : function ()
16456     {
16457         this.el.unmask();
16458     },
16459     
16460
16461     /**
16462      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16463      * @param {HTMLElement} node
16464      * @return {HTMLElement} The template node
16465      */
16466     findItemFromChild : function(node){
16467         var el = this.dataName  ?
16468             this.el.child('.roo-tpl-' + this.dataName,true) :
16469             this.el.dom; 
16470         
16471         if(!node || node.parentNode == el){
16472                     return node;
16473             }
16474             var p = node.parentNode;
16475             while(p && p != el){
16476             if(p.parentNode == el){
16477                 return p;
16478             }
16479             p = p.parentNode;
16480         }
16481             return null;
16482     },
16483
16484     /** @ignore */
16485     onClick : function(e){
16486         var item = this.findItemFromChild(e.getTarget());
16487         if(item){
16488             var index = this.indexOf(item);
16489             if(this.onItemClick(item, index, e) !== false){
16490                 this.fireEvent("click", this, index, item, e);
16491             }
16492         }else{
16493             this.clearSelections();
16494         }
16495     },
16496
16497     /** @ignore */
16498     onContextMenu : function(e){
16499         var item = this.findItemFromChild(e.getTarget());
16500         if(item){
16501             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16502         }
16503     },
16504
16505     /** @ignore */
16506     onDblClick : function(e){
16507         var item = this.findItemFromChild(e.getTarget());
16508         if(item){
16509             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16510         }
16511     },
16512
16513     onItemClick : function(item, index, e)
16514     {
16515         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16516             return false;
16517         }
16518         if (this.toggleSelect) {
16519             var m = this.isSelected(item) ? 'unselect' : 'select';
16520             //Roo.log(m);
16521             var _t = this;
16522             _t[m](item, true, false);
16523             return true;
16524         }
16525         if(this.multiSelect || this.singleSelect){
16526             if(this.multiSelect && e.shiftKey && this.lastSelection){
16527                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16528             }else{
16529                 this.select(item, this.multiSelect && e.ctrlKey);
16530                 this.lastSelection = item;
16531             }
16532             
16533             if(!this.tickable){
16534                 e.preventDefault();
16535             }
16536             
16537         }
16538         return true;
16539     },
16540
16541     /**
16542      * Get the number of selected nodes.
16543      * @return {Number}
16544      */
16545     getSelectionCount : function(){
16546         return this.selections.length;
16547     },
16548
16549     /**
16550      * Get the currently selected nodes.
16551      * @return {Array} An array of HTMLElements
16552      */
16553     getSelectedNodes : function(){
16554         return this.selections;
16555     },
16556
16557     /**
16558      * Get the indexes of the selected nodes.
16559      * @return {Array}
16560      */
16561     getSelectedIndexes : function(){
16562         var indexes = [], s = this.selections;
16563         for(var i = 0, len = s.length; i < len; i++){
16564             indexes.push(s[i].nodeIndex);
16565         }
16566         return indexes;
16567     },
16568
16569     /**
16570      * Clear all selections
16571      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16572      */
16573     clearSelections : function(suppressEvent){
16574         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16575             this.cmp.elements = this.selections;
16576             this.cmp.removeClass(this.selectedClass);
16577             this.selections = [];
16578             if(!suppressEvent){
16579                 this.fireEvent("selectionchange", this, this.selections);
16580             }
16581         }
16582     },
16583
16584     /**
16585      * Returns true if the passed node is selected
16586      * @param {HTMLElement/Number} node The node or node index
16587      * @return {Boolean}
16588      */
16589     isSelected : function(node){
16590         var s = this.selections;
16591         if(s.length < 1){
16592             return false;
16593         }
16594         node = this.getNode(node);
16595         return s.indexOf(node) !== -1;
16596     },
16597
16598     /**
16599      * Selects nodes.
16600      * @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
16601      * @param {Boolean} keepExisting (optional) true to keep existing selections
16602      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16603      */
16604     select : function(nodeInfo, keepExisting, suppressEvent){
16605         if(nodeInfo instanceof Array){
16606             if(!keepExisting){
16607                 this.clearSelections(true);
16608             }
16609             for(var i = 0, len = nodeInfo.length; i < len; i++){
16610                 this.select(nodeInfo[i], true, true);
16611             }
16612             return;
16613         } 
16614         var node = this.getNode(nodeInfo);
16615         if(!node || this.isSelected(node)){
16616             return; // already selected.
16617         }
16618         if(!keepExisting){
16619             this.clearSelections(true);
16620         }
16621         
16622         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16623             Roo.fly(node).addClass(this.selectedClass);
16624             this.selections.push(node);
16625             if(!suppressEvent){
16626                 this.fireEvent("selectionchange", this, this.selections);
16627             }
16628         }
16629         
16630         
16631     },
16632       /**
16633      * Unselects nodes.
16634      * @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
16635      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16636      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16637      */
16638     unselect : function(nodeInfo, keepExisting, suppressEvent)
16639     {
16640         if(nodeInfo instanceof Array){
16641             Roo.each(this.selections, function(s) {
16642                 this.unselect(s, nodeInfo);
16643             }, this);
16644             return;
16645         }
16646         var node = this.getNode(nodeInfo);
16647         if(!node || !this.isSelected(node)){
16648             //Roo.log("not selected");
16649             return; // not selected.
16650         }
16651         // fireevent???
16652         var ns = [];
16653         Roo.each(this.selections, function(s) {
16654             if (s == node ) {
16655                 Roo.fly(node).removeClass(this.selectedClass);
16656
16657                 return;
16658             }
16659             ns.push(s);
16660         },this);
16661         
16662         this.selections= ns;
16663         this.fireEvent("selectionchange", this, this.selections);
16664     },
16665
16666     /**
16667      * Gets a template node.
16668      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16669      * @return {HTMLElement} The node or null if it wasn't found
16670      */
16671     getNode : function(nodeInfo){
16672         if(typeof nodeInfo == "string"){
16673             return document.getElementById(nodeInfo);
16674         }else if(typeof nodeInfo == "number"){
16675             return this.nodes[nodeInfo];
16676         }
16677         return nodeInfo;
16678     },
16679
16680     /**
16681      * Gets a range template nodes.
16682      * @param {Number} startIndex
16683      * @param {Number} endIndex
16684      * @return {Array} An array of nodes
16685      */
16686     getNodes : function(start, end){
16687         var ns = this.nodes;
16688         start = start || 0;
16689         end = typeof end == "undefined" ? ns.length - 1 : end;
16690         var nodes = [];
16691         if(start <= end){
16692             for(var i = start; i <= end; i++){
16693                 nodes.push(ns[i]);
16694             }
16695         } else{
16696             for(var i = start; i >= end; i--){
16697                 nodes.push(ns[i]);
16698             }
16699         }
16700         return nodes;
16701     },
16702
16703     /**
16704      * Finds the index of the passed node
16705      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16706      * @return {Number} The index of the node or -1
16707      */
16708     indexOf : function(node){
16709         node = this.getNode(node);
16710         if(typeof node.nodeIndex == "number"){
16711             return node.nodeIndex;
16712         }
16713         var ns = this.nodes;
16714         for(var i = 0, len = ns.length; i < len; i++){
16715             if(ns[i] == node){
16716                 return i;
16717             }
16718         }
16719         return -1;
16720     }
16721 });
16722 /*
16723  * - LGPL
16724  *
16725  * based on jquery fullcalendar
16726  * 
16727  */
16728
16729 Roo.bootstrap = Roo.bootstrap || {};
16730 /**
16731  * @class Roo.bootstrap.Calendar
16732  * @extends Roo.bootstrap.Component
16733  * Bootstrap Calendar class
16734  * @cfg {Boolean} loadMask (true|false) default false
16735  * @cfg {Object} header generate the user specific header of the calendar, default false
16736
16737  * @constructor
16738  * Create a new Container
16739  * @param {Object} config The config object
16740  */
16741
16742
16743
16744 Roo.bootstrap.Calendar = function(config){
16745     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16746      this.addEvents({
16747         /**
16748              * @event select
16749              * Fires when a date is selected
16750              * @param {DatePicker} this
16751              * @param {Date} date The selected date
16752              */
16753         'select': true,
16754         /**
16755              * @event monthchange
16756              * Fires when the displayed month changes 
16757              * @param {DatePicker} this
16758              * @param {Date} date The selected month
16759              */
16760         'monthchange': true,
16761         /**
16762              * @event evententer
16763              * Fires when mouse over an event
16764              * @param {Calendar} this
16765              * @param {event} Event
16766              */
16767         'evententer': true,
16768         /**
16769              * @event eventleave
16770              * Fires when the mouse leaves an
16771              * @param {Calendar} this
16772              * @param {event}
16773              */
16774         'eventleave': true,
16775         /**
16776              * @event eventclick
16777              * Fires when the mouse click an
16778              * @param {Calendar} this
16779              * @param {event}
16780              */
16781         'eventclick': true
16782         
16783     });
16784
16785 };
16786
16787 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16788     
16789      /**
16790      * @cfg {Number} startDay
16791      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16792      */
16793     startDay : 0,
16794     
16795     loadMask : false,
16796     
16797     header : false,
16798       
16799     getAutoCreate : function(){
16800         
16801         
16802         var fc_button = function(name, corner, style, content ) {
16803             return Roo.apply({},{
16804                 tag : 'span',
16805                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16806                          (corner.length ?
16807                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16808                             ''
16809                         ),
16810                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16811                 unselectable: 'on'
16812             });
16813         };
16814         
16815         var header = {};
16816         
16817         if(!this.header){
16818             header = {
16819                 tag : 'table',
16820                 cls : 'fc-header',
16821                 style : 'width:100%',
16822                 cn : [
16823                     {
16824                         tag: 'tr',
16825                         cn : [
16826                             {
16827                                 tag : 'td',
16828                                 cls : 'fc-header-left',
16829                                 cn : [
16830                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16831                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16832                                     { tag: 'span', cls: 'fc-header-space' },
16833                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16834
16835
16836                                 ]
16837                             },
16838
16839                             {
16840                                 tag : 'td',
16841                                 cls : 'fc-header-center',
16842                                 cn : [
16843                                     {
16844                                         tag: 'span',
16845                                         cls: 'fc-header-title',
16846                                         cn : {
16847                                             tag: 'H2',
16848                                             html : 'month / year'
16849                                         }
16850                                     }
16851
16852                                 ]
16853                             },
16854                             {
16855                                 tag : 'td',
16856                                 cls : 'fc-header-right',
16857                                 cn : [
16858                               /*      fc_button('month', 'left', '', 'month' ),
16859                                     fc_button('week', '', '', 'week' ),
16860                                     fc_button('day', 'right', '', 'day' )
16861                                 */    
16862
16863                                 ]
16864                             }
16865
16866                         ]
16867                     }
16868                 ]
16869             };
16870         }
16871         
16872         header = this.header;
16873         
16874        
16875         var cal_heads = function() {
16876             var ret = [];
16877             // fixme - handle this.
16878             
16879             for (var i =0; i < Date.dayNames.length; i++) {
16880                 var d = Date.dayNames[i];
16881                 ret.push({
16882                     tag: 'th',
16883                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16884                     html : d.substring(0,3)
16885                 });
16886                 
16887             }
16888             ret[0].cls += ' fc-first';
16889             ret[6].cls += ' fc-last';
16890             return ret;
16891         };
16892         var cal_cell = function(n) {
16893             return  {
16894                 tag: 'td',
16895                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16896                 cn : [
16897                     {
16898                         cn : [
16899                             {
16900                                 cls: 'fc-day-number',
16901                                 html: 'D'
16902                             },
16903                             {
16904                                 cls: 'fc-day-content',
16905                              
16906                                 cn : [
16907                                      {
16908                                         style: 'position: relative;' // height: 17px;
16909                                     }
16910                                 ]
16911                             }
16912                             
16913                             
16914                         ]
16915                     }
16916                 ]
16917                 
16918             }
16919         };
16920         var cal_rows = function() {
16921             
16922             var ret = [];
16923             for (var r = 0; r < 6; r++) {
16924                 var row= {
16925                     tag : 'tr',
16926                     cls : 'fc-week',
16927                     cn : []
16928                 };
16929                 
16930                 for (var i =0; i < Date.dayNames.length; i++) {
16931                     var d = Date.dayNames[i];
16932                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16933
16934                 }
16935                 row.cn[0].cls+=' fc-first';
16936                 row.cn[0].cn[0].style = 'min-height:90px';
16937                 row.cn[6].cls+=' fc-last';
16938                 ret.push(row);
16939                 
16940             }
16941             ret[0].cls += ' fc-first';
16942             ret[4].cls += ' fc-prev-last';
16943             ret[5].cls += ' fc-last';
16944             return ret;
16945             
16946         };
16947         
16948         var cal_table = {
16949             tag: 'table',
16950             cls: 'fc-border-separate',
16951             style : 'width:100%',
16952             cellspacing  : 0,
16953             cn : [
16954                 { 
16955                     tag: 'thead',
16956                     cn : [
16957                         { 
16958                             tag: 'tr',
16959                             cls : 'fc-first fc-last',
16960                             cn : cal_heads()
16961                         }
16962                     ]
16963                 },
16964                 { 
16965                     tag: 'tbody',
16966                     cn : cal_rows()
16967                 }
16968                   
16969             ]
16970         };
16971          
16972          var cfg = {
16973             cls : 'fc fc-ltr',
16974             cn : [
16975                 header,
16976                 {
16977                     cls : 'fc-content',
16978                     style : "position: relative;",
16979                     cn : [
16980                         {
16981                             cls : 'fc-view fc-view-month fc-grid',
16982                             style : 'position: relative',
16983                             unselectable : 'on',
16984                             cn : [
16985                                 {
16986                                     cls : 'fc-event-container',
16987                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16988                                 },
16989                                 cal_table
16990                             ]
16991                         }
16992                     ]
16993     
16994                 }
16995            ] 
16996             
16997         };
16998         
16999          
17000         
17001         return cfg;
17002     },
17003     
17004     
17005     initEvents : function()
17006     {
17007         if(!this.store){
17008             throw "can not find store for calendar";
17009         }
17010         
17011         var mark = {
17012             tag: "div",
17013             cls:"x-dlg-mask",
17014             style: "text-align:center",
17015             cn: [
17016                 {
17017                     tag: "div",
17018                     style: "background-color:white;width:50%;margin:250 auto",
17019                     cn: [
17020                         {
17021                             tag: "img",
17022                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17023                         },
17024                         {
17025                             tag: "span",
17026                             html: "Loading"
17027                         }
17028                         
17029                     ]
17030                 }
17031             ]
17032         };
17033         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17034         
17035         var size = this.el.select('.fc-content', true).first().getSize();
17036         this.maskEl.setSize(size.width, size.height);
17037         this.maskEl.enableDisplayMode("block");
17038         if(!this.loadMask){
17039             this.maskEl.hide();
17040         }
17041         
17042         this.store = Roo.factory(this.store, Roo.data);
17043         this.store.on('load', this.onLoad, this);
17044         this.store.on('beforeload', this.onBeforeLoad, this);
17045         
17046         this.resize();
17047         
17048         this.cells = this.el.select('.fc-day',true);
17049         //Roo.log(this.cells);
17050         this.textNodes = this.el.query('.fc-day-number');
17051         this.cells.addClassOnOver('fc-state-hover');
17052         
17053         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17054         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17055         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17056         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17057         
17058         this.on('monthchange', this.onMonthChange, this);
17059         
17060         this.update(new Date().clearTime());
17061     },
17062     
17063     resize : function() {
17064         var sz  = this.el.getSize();
17065         
17066         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17067         this.el.select('.fc-day-content div',true).setHeight(34);
17068     },
17069     
17070     
17071     // private
17072     showPrevMonth : function(e){
17073         this.update(this.activeDate.add("mo", -1));
17074     },
17075     showToday : function(e){
17076         this.update(new Date().clearTime());
17077     },
17078     // private
17079     showNextMonth : function(e){
17080         this.update(this.activeDate.add("mo", 1));
17081     },
17082
17083     // private
17084     showPrevYear : function(){
17085         this.update(this.activeDate.add("y", -1));
17086     },
17087
17088     // private
17089     showNextYear : function(){
17090         this.update(this.activeDate.add("y", 1));
17091     },
17092
17093     
17094    // private
17095     update : function(date)
17096     {
17097         var vd = this.activeDate;
17098         this.activeDate = date;
17099 //        if(vd && this.el){
17100 //            var t = date.getTime();
17101 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17102 //                Roo.log('using add remove');
17103 //                
17104 //                this.fireEvent('monthchange', this, date);
17105 //                
17106 //                this.cells.removeClass("fc-state-highlight");
17107 //                this.cells.each(function(c){
17108 //                   if(c.dateValue == t){
17109 //                       c.addClass("fc-state-highlight");
17110 //                       setTimeout(function(){
17111 //                            try{c.dom.firstChild.focus();}catch(e){}
17112 //                       }, 50);
17113 //                       return false;
17114 //                   }
17115 //                   return true;
17116 //                });
17117 //                return;
17118 //            }
17119 //        }
17120         
17121         var days = date.getDaysInMonth();
17122         
17123         var firstOfMonth = date.getFirstDateOfMonth();
17124         var startingPos = firstOfMonth.getDay()-this.startDay;
17125         
17126         if(startingPos < this.startDay){
17127             startingPos += 7;
17128         }
17129         
17130         var pm = date.add(Date.MONTH, -1);
17131         var prevStart = pm.getDaysInMonth()-startingPos;
17132 //        
17133         this.cells = this.el.select('.fc-day',true);
17134         this.textNodes = this.el.query('.fc-day-number');
17135         this.cells.addClassOnOver('fc-state-hover');
17136         
17137         var cells = this.cells.elements;
17138         var textEls = this.textNodes;
17139         
17140         Roo.each(cells, function(cell){
17141             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17142         });
17143         
17144         days += startingPos;
17145
17146         // convert everything to numbers so it's fast
17147         var day = 86400000;
17148         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17149         //Roo.log(d);
17150         //Roo.log(pm);
17151         //Roo.log(prevStart);
17152         
17153         var today = new Date().clearTime().getTime();
17154         var sel = date.clearTime().getTime();
17155         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17156         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17157         var ddMatch = this.disabledDatesRE;
17158         var ddText = this.disabledDatesText;
17159         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17160         var ddaysText = this.disabledDaysText;
17161         var format = this.format;
17162         
17163         var setCellClass = function(cal, cell){
17164             cell.row = 0;
17165             cell.events = [];
17166             cell.more = [];
17167             //Roo.log('set Cell Class');
17168             cell.title = "";
17169             var t = d.getTime();
17170             
17171             //Roo.log(d);
17172             
17173             cell.dateValue = t;
17174             if(t == today){
17175                 cell.className += " fc-today";
17176                 cell.className += " fc-state-highlight";
17177                 cell.title = cal.todayText;
17178             }
17179             if(t == sel){
17180                 // disable highlight in other month..
17181                 //cell.className += " fc-state-highlight";
17182                 
17183             }
17184             // disabling
17185             if(t < min) {
17186                 cell.className = " fc-state-disabled";
17187                 cell.title = cal.minText;
17188                 return;
17189             }
17190             if(t > max) {
17191                 cell.className = " fc-state-disabled";
17192                 cell.title = cal.maxText;
17193                 return;
17194             }
17195             if(ddays){
17196                 if(ddays.indexOf(d.getDay()) != -1){
17197                     cell.title = ddaysText;
17198                     cell.className = " fc-state-disabled";
17199                 }
17200             }
17201             if(ddMatch && format){
17202                 var fvalue = d.dateFormat(format);
17203                 if(ddMatch.test(fvalue)){
17204                     cell.title = ddText.replace("%0", fvalue);
17205                     cell.className = " fc-state-disabled";
17206                 }
17207             }
17208             
17209             if (!cell.initialClassName) {
17210                 cell.initialClassName = cell.dom.className;
17211             }
17212             
17213             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17214         };
17215
17216         var i = 0;
17217         
17218         for(; i < startingPos; i++) {
17219             textEls[i].innerHTML = (++prevStart);
17220             d.setDate(d.getDate()+1);
17221             
17222             cells[i].className = "fc-past fc-other-month";
17223             setCellClass(this, cells[i]);
17224         }
17225         
17226         var intDay = 0;
17227         
17228         for(; i < days; i++){
17229             intDay = i - startingPos + 1;
17230             textEls[i].innerHTML = (intDay);
17231             d.setDate(d.getDate()+1);
17232             
17233             cells[i].className = ''; // "x-date-active";
17234             setCellClass(this, cells[i]);
17235         }
17236         var extraDays = 0;
17237         
17238         for(; i < 42; i++) {
17239             textEls[i].innerHTML = (++extraDays);
17240             d.setDate(d.getDate()+1);
17241             
17242             cells[i].className = "fc-future fc-other-month";
17243             setCellClass(this, cells[i]);
17244         }
17245         
17246         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17247         
17248         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17249         
17250         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17251         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17252         
17253         if(totalRows != 6){
17254             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17255             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17256         }
17257         
17258         this.fireEvent('monthchange', this, date);
17259         
17260         
17261         /*
17262         if(!this.internalRender){
17263             var main = this.el.dom.firstChild;
17264             var w = main.offsetWidth;
17265             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17266             Roo.fly(main).setWidth(w);
17267             this.internalRender = true;
17268             // opera does not respect the auto grow header center column
17269             // then, after it gets a width opera refuses to recalculate
17270             // without a second pass
17271             if(Roo.isOpera && !this.secondPass){
17272                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17273                 this.secondPass = true;
17274                 this.update.defer(10, this, [date]);
17275             }
17276         }
17277         */
17278         
17279     },
17280     
17281     findCell : function(dt) {
17282         dt = dt.clearTime().getTime();
17283         var ret = false;
17284         this.cells.each(function(c){
17285             //Roo.log("check " +c.dateValue + '?=' + dt);
17286             if(c.dateValue == dt){
17287                 ret = c;
17288                 return false;
17289             }
17290             return true;
17291         });
17292         
17293         return ret;
17294     },
17295     
17296     findCells : function(ev) {
17297         var s = ev.start.clone().clearTime().getTime();
17298        // Roo.log(s);
17299         var e= ev.end.clone().clearTime().getTime();
17300        // Roo.log(e);
17301         var ret = [];
17302         this.cells.each(function(c){
17303              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17304             
17305             if(c.dateValue > e){
17306                 return ;
17307             }
17308             if(c.dateValue < s){
17309                 return ;
17310             }
17311             ret.push(c);
17312         });
17313         
17314         return ret;    
17315     },
17316     
17317 //    findBestRow: function(cells)
17318 //    {
17319 //        var ret = 0;
17320 //        
17321 //        for (var i =0 ; i < cells.length;i++) {
17322 //            ret  = Math.max(cells[i].rows || 0,ret);
17323 //        }
17324 //        return ret;
17325 //        
17326 //    },
17327     
17328     
17329     addItem : function(ev)
17330     {
17331         // look for vertical location slot in
17332         var cells = this.findCells(ev);
17333         
17334 //        ev.row = this.findBestRow(cells);
17335         
17336         // work out the location.
17337         
17338         var crow = false;
17339         var rows = [];
17340         for(var i =0; i < cells.length; i++) {
17341             
17342             cells[i].row = cells[0].row;
17343             
17344             if(i == 0){
17345                 cells[i].row = cells[i].row + 1;
17346             }
17347             
17348             if (!crow) {
17349                 crow = {
17350                     start : cells[i],
17351                     end :  cells[i]
17352                 };
17353                 continue;
17354             }
17355             if (crow.start.getY() == cells[i].getY()) {
17356                 // on same row.
17357                 crow.end = cells[i];
17358                 continue;
17359             }
17360             // different row.
17361             rows.push(crow);
17362             crow = {
17363                 start: cells[i],
17364                 end : cells[i]
17365             };
17366             
17367         }
17368         
17369         rows.push(crow);
17370         ev.els = [];
17371         ev.rows = rows;
17372         ev.cells = cells;
17373         
17374         cells[0].events.push(ev);
17375         
17376         this.calevents.push(ev);
17377     },
17378     
17379     clearEvents: function() {
17380         
17381         if(!this.calevents){
17382             return;
17383         }
17384         
17385         Roo.each(this.cells.elements, function(c){
17386             c.row = 0;
17387             c.events = [];
17388             c.more = [];
17389         });
17390         
17391         Roo.each(this.calevents, function(e) {
17392             Roo.each(e.els, function(el) {
17393                 el.un('mouseenter' ,this.onEventEnter, this);
17394                 el.un('mouseleave' ,this.onEventLeave, this);
17395                 el.remove();
17396             },this);
17397         },this);
17398         
17399         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17400             e.remove();
17401         });
17402         
17403     },
17404     
17405     renderEvents: function()
17406     {   
17407         var _this = this;
17408         
17409         this.cells.each(function(c) {
17410             
17411             if(c.row < 5){
17412                 return;
17413             }
17414             
17415             var ev = c.events;
17416             
17417             var r = 4;
17418             if(c.row != c.events.length){
17419                 r = 4 - (4 - (c.row - c.events.length));
17420             }
17421             
17422             c.events = ev.slice(0, r);
17423             c.more = ev.slice(r);
17424             
17425             if(c.more.length && c.more.length == 1){
17426                 c.events.push(c.more.pop());
17427             }
17428             
17429             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17430             
17431         });
17432             
17433         this.cells.each(function(c) {
17434             
17435             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17436             
17437             
17438             for (var e = 0; e < c.events.length; e++){
17439                 var ev = c.events[e];
17440                 var rows = ev.rows;
17441                 
17442                 for(var i = 0; i < rows.length; i++) {
17443                 
17444                     // how many rows should it span..
17445
17446                     var  cfg = {
17447                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17448                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17449
17450                         unselectable : "on",
17451                         cn : [
17452                             {
17453                                 cls: 'fc-event-inner',
17454                                 cn : [
17455     //                                {
17456     //                                  tag:'span',
17457     //                                  cls: 'fc-event-time',
17458     //                                  html : cells.length > 1 ? '' : ev.time
17459     //                                },
17460                                     {
17461                                       tag:'span',
17462                                       cls: 'fc-event-title',
17463                                       html : String.format('{0}', ev.title)
17464                                     }
17465
17466
17467                                 ]
17468                             },
17469                             {
17470                                 cls: 'ui-resizable-handle ui-resizable-e',
17471                                 html : '&nbsp;&nbsp;&nbsp'
17472                             }
17473
17474                         ]
17475                     };
17476
17477                     if (i == 0) {
17478                         cfg.cls += ' fc-event-start';
17479                     }
17480                     if ((i+1) == rows.length) {
17481                         cfg.cls += ' fc-event-end';
17482                     }
17483
17484                     var ctr = _this.el.select('.fc-event-container',true).first();
17485                     var cg = ctr.createChild(cfg);
17486
17487                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17488                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17489
17490                     var r = (c.more.length) ? 1 : 0;
17491                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17492                     cg.setWidth(ebox.right - sbox.x -2);
17493
17494                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17495                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17496                     cg.on('click', _this.onEventClick, _this, ev);
17497
17498                     ev.els.push(cg);
17499                     
17500                 }
17501                 
17502             }
17503             
17504             
17505             if(c.more.length){
17506                 var  cfg = {
17507                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17508                     style : 'position: absolute',
17509                     unselectable : "on",
17510                     cn : [
17511                         {
17512                             cls: 'fc-event-inner',
17513                             cn : [
17514                                 {
17515                                   tag:'span',
17516                                   cls: 'fc-event-title',
17517                                   html : 'More'
17518                                 }
17519
17520
17521                             ]
17522                         },
17523                         {
17524                             cls: 'ui-resizable-handle ui-resizable-e',
17525                             html : '&nbsp;&nbsp;&nbsp'
17526                         }
17527
17528                     ]
17529                 };
17530
17531                 var ctr = _this.el.select('.fc-event-container',true).first();
17532                 var cg = ctr.createChild(cfg);
17533
17534                 var sbox = c.select('.fc-day-content',true).first().getBox();
17535                 var ebox = c.select('.fc-day-content',true).first().getBox();
17536                 //Roo.log(cg);
17537                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17538                 cg.setWidth(ebox.right - sbox.x -2);
17539
17540                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17541                 
17542             }
17543             
17544         });
17545         
17546         
17547         
17548     },
17549     
17550     onEventEnter: function (e, el,event,d) {
17551         this.fireEvent('evententer', this, el, event);
17552     },
17553     
17554     onEventLeave: function (e, el,event,d) {
17555         this.fireEvent('eventleave', this, el, event);
17556     },
17557     
17558     onEventClick: function (e, el,event,d) {
17559         this.fireEvent('eventclick', this, el, event);
17560     },
17561     
17562     onMonthChange: function () {
17563         this.store.load();
17564     },
17565     
17566     onMoreEventClick: function(e, el, more)
17567     {
17568         var _this = this;
17569         
17570         this.calpopover.placement = 'right';
17571         this.calpopover.setTitle('More');
17572         
17573         this.calpopover.setContent('');
17574         
17575         var ctr = this.calpopover.el.select('.popover-content', true).first();
17576         
17577         Roo.each(more, function(m){
17578             var cfg = {
17579                 cls : 'fc-event-hori fc-event-draggable',
17580                 html : m.title
17581             };
17582             var cg = ctr.createChild(cfg);
17583             
17584             cg.on('click', _this.onEventClick, _this, m);
17585         });
17586         
17587         this.calpopover.show(el);
17588         
17589         
17590     },
17591     
17592     onLoad: function () 
17593     {   
17594         this.calevents = [];
17595         var cal = this;
17596         
17597         if(this.store.getCount() > 0){
17598             this.store.data.each(function(d){
17599                cal.addItem({
17600                     id : d.data.id,
17601                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17602                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17603                     time : d.data.start_time,
17604                     title : d.data.title,
17605                     description : d.data.description,
17606                     venue : d.data.venue
17607                 });
17608             });
17609         }
17610         
17611         this.renderEvents();
17612         
17613         if(this.calevents.length && this.loadMask){
17614             this.maskEl.hide();
17615         }
17616     },
17617     
17618     onBeforeLoad: function()
17619     {
17620         this.clearEvents();
17621         if(this.loadMask){
17622             this.maskEl.show();
17623         }
17624     }
17625 });
17626
17627  
17628  /*
17629  * - LGPL
17630  *
17631  * element
17632  * 
17633  */
17634
17635 /**
17636  * @class Roo.bootstrap.Popover
17637  * @extends Roo.bootstrap.Component
17638  * Bootstrap Popover class
17639  * @cfg {String} html contents of the popover   (or false to use children..)
17640  * @cfg {String} title of popover (or false to hide)
17641  * @cfg {String} placement how it is placed
17642  * @cfg {String} trigger click || hover (or false to trigger manually)
17643  * @cfg {String} over what (parent or false to trigger manually.)
17644  * @cfg {Number} delay - delay before showing
17645  
17646  * @constructor
17647  * Create a new Popover
17648  * @param {Object} config The config object
17649  */
17650
17651 Roo.bootstrap.Popover = function(config){
17652     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17653     
17654     this.addEvents({
17655         // raw events
17656          /**
17657          * @event show
17658          * After the popover show
17659          * 
17660          * @param {Roo.bootstrap.Popover} this
17661          */
17662         "show" : true,
17663         /**
17664          * @event hide
17665          * After the popover hide
17666          * 
17667          * @param {Roo.bootstrap.Popover} this
17668          */
17669         "hide" : true
17670     });
17671 };
17672
17673 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17674     
17675     title: 'Fill in a title',
17676     html: false,
17677     
17678     placement : 'right',
17679     trigger : 'hover', // hover
17680     
17681     delay : 0,
17682     
17683     over: 'parent',
17684     
17685     can_build_overlaid : false,
17686     
17687     getChildContainer : function()
17688     {
17689         return this.el.select('.popover-content',true).first();
17690     },
17691     
17692     getAutoCreate : function(){
17693          
17694         var cfg = {
17695            cls : 'popover roo-dynamic',
17696            style: 'display:block',
17697            cn : [
17698                 {
17699                     cls : 'arrow'
17700                 },
17701                 {
17702                     cls : 'popover-inner',
17703                     cn : [
17704                         {
17705                             tag: 'h3',
17706                             cls: 'popover-title popover-header',
17707                             html : this.title
17708                         },
17709                         {
17710                             cls : 'popover-content popover-body',
17711                             html : this.html
17712                         }
17713                     ]
17714                     
17715                 }
17716            ]
17717         };
17718         
17719         return cfg;
17720     },
17721     setTitle: function(str)
17722     {
17723         this.title = str;
17724         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17725     },
17726     setContent: function(str)
17727     {
17728         this.html = str;
17729         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17730     },
17731     // as it get's added to the bottom of the page.
17732     onRender : function(ct, position)
17733     {
17734         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17735         if(!this.el){
17736             var cfg = Roo.apply({},  this.getAutoCreate());
17737             cfg.id = Roo.id();
17738             
17739             if (this.cls) {
17740                 cfg.cls += ' ' + this.cls;
17741             }
17742             if (this.style) {
17743                 cfg.style = this.style;
17744             }
17745             //Roo.log("adding to ");
17746             this.el = Roo.get(document.body).createChild(cfg, position);
17747 //            Roo.log(this.el);
17748         }
17749         this.initEvents();
17750     },
17751     
17752     initEvents : function()
17753     {
17754         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17755         this.el.enableDisplayMode('block');
17756         this.el.hide();
17757         if (this.over === false) {
17758             return; 
17759         }
17760         if (this.triggers === false) {
17761             return;
17762         }
17763         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17764         var triggers = this.trigger ? this.trigger.split(' ') : [];
17765         Roo.each(triggers, function(trigger) {
17766         
17767             if (trigger == 'click') {
17768                 on_el.on('click', this.toggle, this);
17769             } else if (trigger != 'manual') {
17770                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17771                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17772       
17773                 on_el.on(eventIn  ,this.enter, this);
17774                 on_el.on(eventOut, this.leave, this);
17775             }
17776         }, this);
17777         
17778     },
17779     
17780     
17781     // private
17782     timeout : null,
17783     hoverState : null,
17784     
17785     toggle : function () {
17786         this.hoverState == 'in' ? this.leave() : this.enter();
17787     },
17788     
17789     enter : function () {
17790         
17791         clearTimeout(this.timeout);
17792     
17793         this.hoverState = 'in';
17794     
17795         if (!this.delay || !this.delay.show) {
17796             this.show();
17797             return;
17798         }
17799         var _t = this;
17800         this.timeout = setTimeout(function () {
17801             if (_t.hoverState == 'in') {
17802                 _t.show();
17803             }
17804         }, this.delay.show)
17805     },
17806     
17807     leave : function() {
17808         clearTimeout(this.timeout);
17809     
17810         this.hoverState = 'out';
17811     
17812         if (!this.delay || !this.delay.hide) {
17813             this.hide();
17814             return;
17815         }
17816         var _t = this;
17817         this.timeout = setTimeout(function () {
17818             if (_t.hoverState == 'out') {
17819                 _t.hide();
17820             }
17821         }, this.delay.hide)
17822     },
17823     
17824     show : function (on_el)
17825     {
17826         if (!on_el) {
17827             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17828         }
17829         
17830         // set content.
17831         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17832         if (this.html !== false) {
17833             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17834         }
17835         this.el.removeClass([
17836             'fade','top','bottom', 'left', 'right','in',
17837             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17838         ]);
17839         if (!this.title.length) {
17840             this.el.select('.popover-title',true).hide();
17841         }
17842         
17843         var placement = typeof this.placement == 'function' ?
17844             this.placement.call(this, this.el, on_el) :
17845             this.placement;
17846             
17847         var autoToken = /\s?auto?\s?/i;
17848         var autoPlace = autoToken.test(placement);
17849         if (autoPlace) {
17850             placement = placement.replace(autoToken, '') || 'top';
17851         }
17852         
17853         //this.el.detach()
17854         //this.el.setXY([0,0]);
17855         this.el.show();
17856         this.el.dom.style.display='block';
17857         this.el.addClass(placement);
17858         
17859         //this.el.appendTo(on_el);
17860         
17861         var p = this.getPosition();
17862         var box = this.el.getBox();
17863         
17864         if (autoPlace) {
17865             // fixme..
17866         }
17867         var align = Roo.bootstrap.Popover.alignment[placement];
17868         
17869 //        Roo.log(align);
17870         this.el.alignTo(on_el, align[0],align[1]);
17871         //var arrow = this.el.select('.arrow',true).first();
17872         //arrow.set(align[2], 
17873         
17874         this.el.addClass('in');
17875         
17876         
17877         if (this.el.hasClass('fade')) {
17878             // fade it?
17879         }
17880         
17881         this.hoverState = 'in';
17882         
17883         this.fireEvent('show', this);
17884         
17885     },
17886     hide : function()
17887     {
17888         this.el.setXY([0,0]);
17889         this.el.removeClass('in');
17890         this.el.hide();
17891         this.hoverState = null;
17892         
17893         this.fireEvent('hide', this);
17894     }
17895     
17896 });
17897
17898 Roo.bootstrap.Popover.alignment = {
17899     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17900     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17901     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17902     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17903 };
17904
17905  /*
17906  * - LGPL
17907  *
17908  * Progress
17909  * 
17910  */
17911
17912 /**
17913  * @class Roo.bootstrap.Progress
17914  * @extends Roo.bootstrap.Component
17915  * Bootstrap Progress class
17916  * @cfg {Boolean} striped striped of the progress bar
17917  * @cfg {Boolean} active animated of the progress bar
17918  * 
17919  * 
17920  * @constructor
17921  * Create a new Progress
17922  * @param {Object} config The config object
17923  */
17924
17925 Roo.bootstrap.Progress = function(config){
17926     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17927 };
17928
17929 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17930     
17931     striped : false,
17932     active: false,
17933     
17934     getAutoCreate : function(){
17935         var cfg = {
17936             tag: 'div',
17937             cls: 'progress'
17938         };
17939         
17940         
17941         if(this.striped){
17942             cfg.cls += ' progress-striped';
17943         }
17944       
17945         if(this.active){
17946             cfg.cls += ' active';
17947         }
17948         
17949         
17950         return cfg;
17951     }
17952    
17953 });
17954
17955  
17956
17957  /*
17958  * - LGPL
17959  *
17960  * ProgressBar
17961  * 
17962  */
17963
17964 /**
17965  * @class Roo.bootstrap.ProgressBar
17966  * @extends Roo.bootstrap.Component
17967  * Bootstrap ProgressBar class
17968  * @cfg {Number} aria_valuenow aria-value now
17969  * @cfg {Number} aria_valuemin aria-value min
17970  * @cfg {Number} aria_valuemax aria-value max
17971  * @cfg {String} label label for the progress bar
17972  * @cfg {String} panel (success | info | warning | danger )
17973  * @cfg {String} role role of the progress bar
17974  * @cfg {String} sr_only text
17975  * 
17976  * 
17977  * @constructor
17978  * Create a new ProgressBar
17979  * @param {Object} config The config object
17980  */
17981
17982 Roo.bootstrap.ProgressBar = function(config){
17983     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17984 };
17985
17986 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17987     
17988     aria_valuenow : 0,
17989     aria_valuemin : 0,
17990     aria_valuemax : 100,
17991     label : false,
17992     panel : false,
17993     role : false,
17994     sr_only: false,
17995     
17996     getAutoCreate : function()
17997     {
17998         
17999         var cfg = {
18000             tag: 'div',
18001             cls: 'progress-bar',
18002             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18003         };
18004         
18005         if(this.sr_only){
18006             cfg.cn = {
18007                 tag: 'span',
18008                 cls: 'sr-only',
18009                 html: this.sr_only
18010             }
18011         }
18012         
18013         if(this.role){
18014             cfg.role = this.role;
18015         }
18016         
18017         if(this.aria_valuenow){
18018             cfg['aria-valuenow'] = this.aria_valuenow;
18019         }
18020         
18021         if(this.aria_valuemin){
18022             cfg['aria-valuemin'] = this.aria_valuemin;
18023         }
18024         
18025         if(this.aria_valuemax){
18026             cfg['aria-valuemax'] = this.aria_valuemax;
18027         }
18028         
18029         if(this.label && !this.sr_only){
18030             cfg.html = this.label;
18031         }
18032         
18033         if(this.panel){
18034             cfg.cls += ' progress-bar-' + this.panel;
18035         }
18036         
18037         return cfg;
18038     },
18039     
18040     update : function(aria_valuenow)
18041     {
18042         this.aria_valuenow = aria_valuenow;
18043         
18044         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18045     }
18046    
18047 });
18048
18049  
18050
18051  /*
18052  * - LGPL
18053  *
18054  * column
18055  * 
18056  */
18057
18058 /**
18059  * @class Roo.bootstrap.TabGroup
18060  * @extends Roo.bootstrap.Column
18061  * Bootstrap Column class
18062  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18063  * @cfg {Boolean} carousel true to make the group behave like a carousel
18064  * @cfg {Boolean} bullets show bullets for the panels
18065  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18066  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18067  * @cfg {Boolean} showarrow (true|false) show arrow default true
18068  * 
18069  * @constructor
18070  * Create a new TabGroup
18071  * @param {Object} config The config object
18072  */
18073
18074 Roo.bootstrap.TabGroup = function(config){
18075     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18076     if (!this.navId) {
18077         this.navId = Roo.id();
18078     }
18079     this.tabs = [];
18080     Roo.bootstrap.TabGroup.register(this);
18081     
18082 };
18083
18084 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18085     
18086     carousel : false,
18087     transition : false,
18088     bullets : 0,
18089     timer : 0,
18090     autoslide : false,
18091     slideFn : false,
18092     slideOnTouch : false,
18093     showarrow : true,
18094     
18095     getAutoCreate : function()
18096     {
18097         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18098         
18099         cfg.cls += ' tab-content';
18100         
18101         if (this.carousel) {
18102             cfg.cls += ' carousel slide';
18103             
18104             cfg.cn = [{
18105                cls : 'carousel-inner',
18106                cn : []
18107             }];
18108         
18109             if(this.bullets  && !Roo.isTouch){
18110                 
18111                 var bullets = {
18112                     cls : 'carousel-bullets',
18113                     cn : []
18114                 };
18115                
18116                 if(this.bullets_cls){
18117                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18118                 }
18119                 
18120                 bullets.cn.push({
18121                     cls : 'clear'
18122                 });
18123                 
18124                 cfg.cn[0].cn.push(bullets);
18125             }
18126             
18127             if(this.showarrow){
18128                 cfg.cn[0].cn.push({
18129                     tag : 'div',
18130                     class : 'carousel-arrow',
18131                     cn : [
18132                         {
18133                             tag : 'div',
18134                             class : 'carousel-prev',
18135                             cn : [
18136                                 {
18137                                     tag : 'i',
18138                                     class : 'fa fa-chevron-left'
18139                                 }
18140                             ]
18141                         },
18142                         {
18143                             tag : 'div',
18144                             class : 'carousel-next',
18145                             cn : [
18146                                 {
18147                                     tag : 'i',
18148                                     class : 'fa fa-chevron-right'
18149                                 }
18150                             ]
18151                         }
18152                     ]
18153                 });
18154             }
18155             
18156         }
18157         
18158         return cfg;
18159     },
18160     
18161     initEvents:  function()
18162     {
18163 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18164 //            this.el.on("touchstart", this.onTouchStart, this);
18165 //        }
18166         
18167         if(this.autoslide){
18168             var _this = this;
18169             
18170             this.slideFn = window.setInterval(function() {
18171                 _this.showPanelNext();
18172             }, this.timer);
18173         }
18174         
18175         if(this.showarrow){
18176             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18177             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18178         }
18179         
18180         
18181     },
18182     
18183 //    onTouchStart : function(e, el, o)
18184 //    {
18185 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18186 //            return;
18187 //        }
18188 //        
18189 //        this.showPanelNext();
18190 //    },
18191     
18192     
18193     getChildContainer : function()
18194     {
18195         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18196     },
18197     
18198     /**
18199     * register a Navigation item
18200     * @param {Roo.bootstrap.NavItem} the navitem to add
18201     */
18202     register : function(item)
18203     {
18204         this.tabs.push( item);
18205         item.navId = this.navId; // not really needed..
18206         this.addBullet();
18207     
18208     },
18209     
18210     getActivePanel : function()
18211     {
18212         var r = false;
18213         Roo.each(this.tabs, function(t) {
18214             if (t.active) {
18215                 r = t;
18216                 return false;
18217             }
18218             return null;
18219         });
18220         return r;
18221         
18222     },
18223     getPanelByName : function(n)
18224     {
18225         var r = false;
18226         Roo.each(this.tabs, function(t) {
18227             if (t.tabId == n) {
18228                 r = t;
18229                 return false;
18230             }
18231             return null;
18232         });
18233         return r;
18234     },
18235     indexOfPanel : function(p)
18236     {
18237         var r = false;
18238         Roo.each(this.tabs, function(t,i) {
18239             if (t.tabId == p.tabId) {
18240                 r = i;
18241                 return false;
18242             }
18243             return null;
18244         });
18245         return r;
18246     },
18247     /**
18248      * show a specific panel
18249      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18250      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18251      */
18252     showPanel : function (pan)
18253     {
18254         if(this.transition || typeof(pan) == 'undefined'){
18255             Roo.log("waiting for the transitionend");
18256             return;
18257         }
18258         
18259         if (typeof(pan) == 'number') {
18260             pan = this.tabs[pan];
18261         }
18262         
18263         if (typeof(pan) == 'string') {
18264             pan = this.getPanelByName(pan);
18265         }
18266         
18267         var cur = this.getActivePanel();
18268         
18269         if(!pan || !cur){
18270             Roo.log('pan or acitve pan is undefined');
18271             return false;
18272         }
18273         
18274         if (pan.tabId == this.getActivePanel().tabId) {
18275             return true;
18276         }
18277         
18278         if (false === cur.fireEvent('beforedeactivate')) {
18279             return false;
18280         }
18281         
18282         if(this.bullets > 0 && !Roo.isTouch){
18283             this.setActiveBullet(this.indexOfPanel(pan));
18284         }
18285         
18286         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18287             
18288             this.transition = true;
18289             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18290             var lr = dir == 'next' ? 'left' : 'right';
18291             pan.el.addClass(dir); // or prev
18292             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18293             cur.el.addClass(lr); // or right
18294             pan.el.addClass(lr);
18295             
18296             var _this = this;
18297             cur.el.on('transitionend', function() {
18298                 Roo.log("trans end?");
18299                 
18300                 pan.el.removeClass([lr,dir]);
18301                 pan.setActive(true);
18302                 
18303                 cur.el.removeClass([lr]);
18304                 cur.setActive(false);
18305                 
18306                 _this.transition = false;
18307                 
18308             }, this, { single:  true } );
18309             
18310             return true;
18311         }
18312         
18313         cur.setActive(false);
18314         pan.setActive(true);
18315         
18316         return true;
18317         
18318     },
18319     showPanelNext : function()
18320     {
18321         var i = this.indexOfPanel(this.getActivePanel());
18322         
18323         if (i >= this.tabs.length - 1 && !this.autoslide) {
18324             return;
18325         }
18326         
18327         if (i >= this.tabs.length - 1 && this.autoslide) {
18328             i = -1;
18329         }
18330         
18331         this.showPanel(this.tabs[i+1]);
18332     },
18333     
18334     showPanelPrev : function()
18335     {
18336         var i = this.indexOfPanel(this.getActivePanel());
18337         
18338         if (i  < 1 && !this.autoslide) {
18339             return;
18340         }
18341         
18342         if (i < 1 && this.autoslide) {
18343             i = this.tabs.length;
18344         }
18345         
18346         this.showPanel(this.tabs[i-1]);
18347     },
18348     
18349     
18350     addBullet: function()
18351     {
18352         if(!this.bullets || Roo.isTouch){
18353             return;
18354         }
18355         var ctr = this.el.select('.carousel-bullets',true).first();
18356         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18357         var bullet = ctr.createChild({
18358             cls : 'bullet bullet-' + i
18359         },ctr.dom.lastChild);
18360         
18361         
18362         var _this = this;
18363         
18364         bullet.on('click', (function(e, el, o, ii, t){
18365
18366             e.preventDefault();
18367
18368             this.showPanel(ii);
18369
18370             if(this.autoslide && this.slideFn){
18371                 clearInterval(this.slideFn);
18372                 this.slideFn = window.setInterval(function() {
18373                     _this.showPanelNext();
18374                 }, this.timer);
18375             }
18376
18377         }).createDelegate(this, [i, bullet], true));
18378                 
18379         
18380     },
18381      
18382     setActiveBullet : function(i)
18383     {
18384         if(Roo.isTouch){
18385             return;
18386         }
18387         
18388         Roo.each(this.el.select('.bullet', true).elements, function(el){
18389             el.removeClass('selected');
18390         });
18391
18392         var bullet = this.el.select('.bullet-' + i, true).first();
18393         
18394         if(!bullet){
18395             return;
18396         }
18397         
18398         bullet.addClass('selected');
18399     }
18400     
18401     
18402   
18403 });
18404
18405  
18406
18407  
18408  
18409 Roo.apply(Roo.bootstrap.TabGroup, {
18410     
18411     groups: {},
18412      /**
18413     * register a Navigation Group
18414     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18415     */
18416     register : function(navgrp)
18417     {
18418         this.groups[navgrp.navId] = navgrp;
18419         
18420     },
18421     /**
18422     * fetch a Navigation Group based on the navigation ID
18423     * if one does not exist , it will get created.
18424     * @param {string} the navgroup to add
18425     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18426     */
18427     get: function(navId) {
18428         if (typeof(this.groups[navId]) == 'undefined') {
18429             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18430         }
18431         return this.groups[navId] ;
18432     }
18433     
18434     
18435     
18436 });
18437
18438  /*
18439  * - LGPL
18440  *
18441  * TabPanel
18442  * 
18443  */
18444
18445 /**
18446  * @class Roo.bootstrap.TabPanel
18447  * @extends Roo.bootstrap.Component
18448  * Bootstrap TabPanel class
18449  * @cfg {Boolean} active panel active
18450  * @cfg {String} html panel content
18451  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18452  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18453  * @cfg {String} href click to link..
18454  * 
18455  * 
18456  * @constructor
18457  * Create a new TabPanel
18458  * @param {Object} config The config object
18459  */
18460
18461 Roo.bootstrap.TabPanel = function(config){
18462     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18463     this.addEvents({
18464         /**
18465              * @event changed
18466              * Fires when the active status changes
18467              * @param {Roo.bootstrap.TabPanel} this
18468              * @param {Boolean} state the new state
18469             
18470          */
18471         'changed': true,
18472         /**
18473              * @event beforedeactivate
18474              * Fires before a tab is de-activated - can be used to do validation on a form.
18475              * @param {Roo.bootstrap.TabPanel} this
18476              * @return {Boolean} false if there is an error
18477             
18478          */
18479         'beforedeactivate': true
18480      });
18481     
18482     this.tabId = this.tabId || Roo.id();
18483   
18484 };
18485
18486 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18487     
18488     active: false,
18489     html: false,
18490     tabId: false,
18491     navId : false,
18492     href : '',
18493     
18494     getAutoCreate : function(){
18495         var cfg = {
18496             tag: 'div',
18497             // item is needed for carousel - not sure if it has any effect otherwise
18498             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18499             html: this.html || ''
18500         };
18501         
18502         if(this.active){
18503             cfg.cls += ' active';
18504         }
18505         
18506         if(this.tabId){
18507             cfg.tabId = this.tabId;
18508         }
18509         
18510         
18511         return cfg;
18512     },
18513     
18514     initEvents:  function()
18515     {
18516         var p = this.parent();
18517         
18518         this.navId = this.navId || p.navId;
18519         
18520         if (typeof(this.navId) != 'undefined') {
18521             // not really needed.. but just in case.. parent should be a NavGroup.
18522             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18523             
18524             tg.register(this);
18525             
18526             var i = tg.tabs.length - 1;
18527             
18528             if(this.active && tg.bullets > 0 && i < tg.bullets){
18529                 tg.setActiveBullet(i);
18530             }
18531         }
18532         
18533         this.el.on('click', this.onClick, this);
18534         
18535         if(Roo.isTouch){
18536             this.el.on("touchstart", this.onTouchStart, this);
18537             this.el.on("touchmove", this.onTouchMove, this);
18538             this.el.on("touchend", this.onTouchEnd, this);
18539         }
18540         
18541     },
18542     
18543     onRender : function(ct, position)
18544     {
18545         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18546     },
18547     
18548     setActive : function(state)
18549     {
18550         Roo.log("panel - set active " + this.tabId + "=" + state);
18551         
18552         this.active = state;
18553         if (!state) {
18554             this.el.removeClass('active');
18555             
18556         } else  if (!this.el.hasClass('active')) {
18557             this.el.addClass('active');
18558         }
18559         
18560         this.fireEvent('changed', this, state);
18561     },
18562     
18563     onClick : function(e)
18564     {
18565         e.preventDefault();
18566         
18567         if(!this.href.length){
18568             return;
18569         }
18570         
18571         window.location.href = this.href;
18572     },
18573     
18574     startX : 0,
18575     startY : 0,
18576     endX : 0,
18577     endY : 0,
18578     swiping : false,
18579     
18580     onTouchStart : function(e)
18581     {
18582         this.swiping = false;
18583         
18584         this.startX = e.browserEvent.touches[0].clientX;
18585         this.startY = e.browserEvent.touches[0].clientY;
18586     },
18587     
18588     onTouchMove : function(e)
18589     {
18590         this.swiping = true;
18591         
18592         this.endX = e.browserEvent.touches[0].clientX;
18593         this.endY = e.browserEvent.touches[0].clientY;
18594     },
18595     
18596     onTouchEnd : function(e)
18597     {
18598         if(!this.swiping){
18599             this.onClick(e);
18600             return;
18601         }
18602         
18603         var tabGroup = this.parent();
18604         
18605         if(this.endX > this.startX){ // swiping right
18606             tabGroup.showPanelPrev();
18607             return;
18608         }
18609         
18610         if(this.startX > this.endX){ // swiping left
18611             tabGroup.showPanelNext();
18612             return;
18613         }
18614     }
18615     
18616     
18617 });
18618  
18619
18620  
18621
18622  /*
18623  * - LGPL
18624  *
18625  * DateField
18626  * 
18627  */
18628
18629 /**
18630  * @class Roo.bootstrap.DateField
18631  * @extends Roo.bootstrap.Input
18632  * Bootstrap DateField class
18633  * @cfg {Number} weekStart default 0
18634  * @cfg {String} viewMode default empty, (months|years)
18635  * @cfg {String} minViewMode default empty, (months|years)
18636  * @cfg {Number} startDate default -Infinity
18637  * @cfg {Number} endDate default Infinity
18638  * @cfg {Boolean} todayHighlight default false
18639  * @cfg {Boolean} todayBtn default false
18640  * @cfg {Boolean} calendarWeeks default false
18641  * @cfg {Object} daysOfWeekDisabled default empty
18642  * @cfg {Boolean} singleMode default false (true | false)
18643  * 
18644  * @cfg {Boolean} keyboardNavigation default true
18645  * @cfg {String} language default en
18646  * 
18647  * @constructor
18648  * Create a new DateField
18649  * @param {Object} config The config object
18650  */
18651
18652 Roo.bootstrap.DateField = function(config){
18653     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18654      this.addEvents({
18655             /**
18656              * @event show
18657              * Fires when this field show.
18658              * @param {Roo.bootstrap.DateField} this
18659              * @param {Mixed} date The date value
18660              */
18661             show : true,
18662             /**
18663              * @event show
18664              * Fires when this field hide.
18665              * @param {Roo.bootstrap.DateField} this
18666              * @param {Mixed} date The date value
18667              */
18668             hide : true,
18669             /**
18670              * @event select
18671              * Fires when select a date.
18672              * @param {Roo.bootstrap.DateField} this
18673              * @param {Mixed} date The date value
18674              */
18675             select : true,
18676             /**
18677              * @event beforeselect
18678              * Fires when before select a date.
18679              * @param {Roo.bootstrap.DateField} this
18680              * @param {Mixed} date The date value
18681              */
18682             beforeselect : true
18683         });
18684 };
18685
18686 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18687     
18688     /**
18689      * @cfg {String} format
18690      * The default date format string which can be overriden for localization support.  The format must be
18691      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18692      */
18693     format : "m/d/y",
18694     /**
18695      * @cfg {String} altFormats
18696      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18697      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18698      */
18699     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18700     
18701     weekStart : 0,
18702     
18703     viewMode : '',
18704     
18705     minViewMode : '',
18706     
18707     todayHighlight : false,
18708     
18709     todayBtn: false,
18710     
18711     language: 'en',
18712     
18713     keyboardNavigation: true,
18714     
18715     calendarWeeks: false,
18716     
18717     startDate: -Infinity,
18718     
18719     endDate: Infinity,
18720     
18721     daysOfWeekDisabled: [],
18722     
18723     _events: [],
18724     
18725     singleMode : false,
18726     
18727     UTCDate: function()
18728     {
18729         return new Date(Date.UTC.apply(Date, arguments));
18730     },
18731     
18732     UTCToday: function()
18733     {
18734         var today = new Date();
18735         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18736     },
18737     
18738     getDate: function() {
18739             var d = this.getUTCDate();
18740             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18741     },
18742     
18743     getUTCDate: function() {
18744             return this.date;
18745     },
18746     
18747     setDate: function(d) {
18748             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18749     },
18750     
18751     setUTCDate: function(d) {
18752             this.date = d;
18753             this.setValue(this.formatDate(this.date));
18754     },
18755         
18756     onRender: function(ct, position)
18757     {
18758         
18759         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18760         
18761         this.language = this.language || 'en';
18762         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18763         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18764         
18765         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18766         this.format = this.format || 'm/d/y';
18767         this.isInline = false;
18768         this.isInput = true;
18769         this.component = this.el.select('.add-on', true).first() || false;
18770         this.component = (this.component && this.component.length === 0) ? false : this.component;
18771         this.hasInput = this.component && this.inputEl().length;
18772         
18773         if (typeof(this.minViewMode === 'string')) {
18774             switch (this.minViewMode) {
18775                 case 'months':
18776                     this.minViewMode = 1;
18777                     break;
18778                 case 'years':
18779                     this.minViewMode = 2;
18780                     break;
18781                 default:
18782                     this.minViewMode = 0;
18783                     break;
18784             }
18785         }
18786         
18787         if (typeof(this.viewMode === 'string')) {
18788             switch (this.viewMode) {
18789                 case 'months':
18790                     this.viewMode = 1;
18791                     break;
18792                 case 'years':
18793                     this.viewMode = 2;
18794                     break;
18795                 default:
18796                     this.viewMode = 0;
18797                     break;
18798             }
18799         }
18800                 
18801         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18802         
18803 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18804         
18805         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18806         
18807         this.picker().on('mousedown', this.onMousedown, this);
18808         this.picker().on('click', this.onClick, this);
18809         
18810         this.picker().addClass('datepicker-dropdown');
18811         
18812         this.startViewMode = this.viewMode;
18813         
18814         if(this.singleMode){
18815             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18816                 v.setVisibilityMode(Roo.Element.DISPLAY);
18817                 v.hide();
18818             });
18819             
18820             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18821                 v.setStyle('width', '189px');
18822             });
18823         }
18824         
18825         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18826             if(!this.calendarWeeks){
18827                 v.remove();
18828                 return;
18829             }
18830             
18831             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18832             v.attr('colspan', function(i, val){
18833                 return parseInt(val) + 1;
18834             });
18835         });
18836                         
18837         
18838         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18839         
18840         this.setStartDate(this.startDate);
18841         this.setEndDate(this.endDate);
18842         
18843         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18844         
18845         this.fillDow();
18846         this.fillMonths();
18847         this.update();
18848         this.showMode();
18849         
18850         if(this.isInline) {
18851             this.showPopup();
18852         }
18853     },
18854     
18855     picker : function()
18856     {
18857         return this.pickerEl;
18858 //        return this.el.select('.datepicker', true).first();
18859     },
18860     
18861     fillDow: function()
18862     {
18863         var dowCnt = this.weekStart;
18864         
18865         var dow = {
18866             tag: 'tr',
18867             cn: [
18868                 
18869             ]
18870         };
18871         
18872         if(this.calendarWeeks){
18873             dow.cn.push({
18874                 tag: 'th',
18875                 cls: 'cw',
18876                 html: '&nbsp;'
18877             })
18878         }
18879         
18880         while (dowCnt < this.weekStart + 7) {
18881             dow.cn.push({
18882                 tag: 'th',
18883                 cls: 'dow',
18884                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18885             });
18886         }
18887         
18888         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18889     },
18890     
18891     fillMonths: function()
18892     {    
18893         var i = 0;
18894         var months = this.picker().select('>.datepicker-months td', true).first();
18895         
18896         months.dom.innerHTML = '';
18897         
18898         while (i < 12) {
18899             var month = {
18900                 tag: 'span',
18901                 cls: 'month',
18902                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18903             };
18904             
18905             months.createChild(month);
18906         }
18907         
18908     },
18909     
18910     update: function()
18911     {
18912         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;
18913         
18914         if (this.date < this.startDate) {
18915             this.viewDate = new Date(this.startDate);
18916         } else if (this.date > this.endDate) {
18917             this.viewDate = new Date(this.endDate);
18918         } else {
18919             this.viewDate = new Date(this.date);
18920         }
18921         
18922         this.fill();
18923     },
18924     
18925     fill: function() 
18926     {
18927         var d = new Date(this.viewDate),
18928                 year = d.getUTCFullYear(),
18929                 month = d.getUTCMonth(),
18930                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18931                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18932                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18933                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18934                 currentDate = this.date && this.date.valueOf(),
18935                 today = this.UTCToday();
18936         
18937         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18938         
18939 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18940         
18941 //        this.picker.select('>tfoot th.today').
18942 //                                              .text(dates[this.language].today)
18943 //                                              .toggle(this.todayBtn !== false);
18944     
18945         this.updateNavArrows();
18946         this.fillMonths();
18947                                                 
18948         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18949         
18950         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18951          
18952         prevMonth.setUTCDate(day);
18953         
18954         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18955         
18956         var nextMonth = new Date(prevMonth);
18957         
18958         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18959         
18960         nextMonth = nextMonth.valueOf();
18961         
18962         var fillMonths = false;
18963         
18964         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18965         
18966         while(prevMonth.valueOf() <= nextMonth) {
18967             var clsName = '';
18968             
18969             if (prevMonth.getUTCDay() === this.weekStart) {
18970                 if(fillMonths){
18971                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18972                 }
18973                     
18974                 fillMonths = {
18975                     tag: 'tr',
18976                     cn: []
18977                 };
18978                 
18979                 if(this.calendarWeeks){
18980                     // ISO 8601: First week contains first thursday.
18981                     // ISO also states week starts on Monday, but we can be more abstract here.
18982                     var
18983                     // Start of current week: based on weekstart/current date
18984                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18985                     // Thursday of this week
18986                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18987                     // First Thursday of year, year from thursday
18988                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18989                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18990                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18991                     
18992                     fillMonths.cn.push({
18993                         tag: 'td',
18994                         cls: 'cw',
18995                         html: calWeek
18996                     });
18997                 }
18998             }
18999             
19000             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19001                 clsName += ' old';
19002             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19003                 clsName += ' new';
19004             }
19005             if (this.todayHighlight &&
19006                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19007                 prevMonth.getUTCMonth() == today.getMonth() &&
19008                 prevMonth.getUTCDate() == today.getDate()) {
19009                 clsName += ' today';
19010             }
19011             
19012             if (currentDate && prevMonth.valueOf() === currentDate) {
19013                 clsName += ' active';
19014             }
19015             
19016             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19017                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19018                     clsName += ' disabled';
19019             }
19020             
19021             fillMonths.cn.push({
19022                 tag: 'td',
19023                 cls: 'day ' + clsName,
19024                 html: prevMonth.getDate()
19025             });
19026             
19027             prevMonth.setDate(prevMonth.getDate()+1);
19028         }
19029           
19030         var currentYear = this.date && this.date.getUTCFullYear();
19031         var currentMonth = this.date && this.date.getUTCMonth();
19032         
19033         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19034         
19035         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19036             v.removeClass('active');
19037             
19038             if(currentYear === year && k === currentMonth){
19039                 v.addClass('active');
19040             }
19041             
19042             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19043                 v.addClass('disabled');
19044             }
19045             
19046         });
19047         
19048         
19049         year = parseInt(year/10, 10) * 10;
19050         
19051         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19052         
19053         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19054         
19055         year -= 1;
19056         for (var i = -1; i < 11; i++) {
19057             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19058                 tag: 'span',
19059                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19060                 html: year
19061             });
19062             
19063             year += 1;
19064         }
19065     },
19066     
19067     showMode: function(dir) 
19068     {
19069         if (dir) {
19070             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19071         }
19072         
19073         Roo.each(this.picker().select('>div',true).elements, function(v){
19074             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19075             v.hide();
19076         });
19077         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19078     },
19079     
19080     place: function()
19081     {
19082         if(this.isInline) {
19083             return;
19084         }
19085         
19086         this.picker().removeClass(['bottom', 'top']);
19087         
19088         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19089             /*
19090              * place to the top of element!
19091              *
19092              */
19093             
19094             this.picker().addClass('top');
19095             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19096             
19097             return;
19098         }
19099         
19100         this.picker().addClass('bottom');
19101         
19102         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19103     },
19104     
19105     parseDate : function(value)
19106     {
19107         if(!value || value instanceof Date){
19108             return value;
19109         }
19110         var v = Date.parseDate(value, this.format);
19111         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19112             v = Date.parseDate(value, 'Y-m-d');
19113         }
19114         if(!v && this.altFormats){
19115             if(!this.altFormatsArray){
19116                 this.altFormatsArray = this.altFormats.split("|");
19117             }
19118             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19119                 v = Date.parseDate(value, this.altFormatsArray[i]);
19120             }
19121         }
19122         return v;
19123     },
19124     
19125     formatDate : function(date, fmt)
19126     {   
19127         return (!date || !(date instanceof Date)) ?
19128         date : date.dateFormat(fmt || this.format);
19129     },
19130     
19131     onFocus : function()
19132     {
19133         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19134         this.showPopup();
19135     },
19136     
19137     onBlur : function()
19138     {
19139         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19140         
19141         var d = this.inputEl().getValue();
19142         
19143         this.setValue(d);
19144                 
19145         this.hidePopup();
19146     },
19147     
19148     showPopup : function()
19149     {
19150         this.picker().show();
19151         this.update();
19152         this.place();
19153         
19154         this.fireEvent('showpopup', this, this.date);
19155     },
19156     
19157     hidePopup : function()
19158     {
19159         if(this.isInline) {
19160             return;
19161         }
19162         this.picker().hide();
19163         this.viewMode = this.startViewMode;
19164         this.showMode();
19165         
19166         this.fireEvent('hidepopup', this, this.date);
19167         
19168     },
19169     
19170     onMousedown: function(e)
19171     {
19172         e.stopPropagation();
19173         e.preventDefault();
19174     },
19175     
19176     keyup: function(e)
19177     {
19178         Roo.bootstrap.DateField.superclass.keyup.call(this);
19179         this.update();
19180     },
19181
19182     setValue: function(v)
19183     {
19184         if(this.fireEvent('beforeselect', this, v) !== false){
19185             var d = new Date(this.parseDate(v) ).clearTime();
19186         
19187             if(isNaN(d.getTime())){
19188                 this.date = this.viewDate = '';
19189                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19190                 return;
19191             }
19192
19193             v = this.formatDate(d);
19194
19195             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19196
19197             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19198
19199             this.update();
19200
19201             this.fireEvent('select', this, this.date);
19202         }
19203     },
19204     
19205     getValue: function()
19206     {
19207         return this.formatDate(this.date);
19208     },
19209     
19210     fireKey: function(e)
19211     {
19212         if (!this.picker().isVisible()){
19213             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19214                 this.showPopup();
19215             }
19216             return;
19217         }
19218         
19219         var dateChanged = false,
19220         dir, day, month,
19221         newDate, newViewDate;
19222         
19223         switch(e.keyCode){
19224             case 27: // escape
19225                 this.hidePopup();
19226                 e.preventDefault();
19227                 break;
19228             case 37: // left
19229             case 39: // right
19230                 if (!this.keyboardNavigation) {
19231                     break;
19232                 }
19233                 dir = e.keyCode == 37 ? -1 : 1;
19234                 
19235                 if (e.ctrlKey){
19236                     newDate = this.moveYear(this.date, dir);
19237                     newViewDate = this.moveYear(this.viewDate, dir);
19238                 } else if (e.shiftKey){
19239                     newDate = this.moveMonth(this.date, dir);
19240                     newViewDate = this.moveMonth(this.viewDate, dir);
19241                 } else {
19242                     newDate = new Date(this.date);
19243                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19244                     newViewDate = new Date(this.viewDate);
19245                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19246                 }
19247                 if (this.dateWithinRange(newDate)){
19248                     this.date = newDate;
19249                     this.viewDate = newViewDate;
19250                     this.setValue(this.formatDate(this.date));
19251 //                    this.update();
19252                     e.preventDefault();
19253                     dateChanged = true;
19254                 }
19255                 break;
19256             case 38: // up
19257             case 40: // down
19258                 if (!this.keyboardNavigation) {
19259                     break;
19260                 }
19261                 dir = e.keyCode == 38 ? -1 : 1;
19262                 if (e.ctrlKey){
19263                     newDate = this.moveYear(this.date, dir);
19264                     newViewDate = this.moveYear(this.viewDate, dir);
19265                 } else if (e.shiftKey){
19266                     newDate = this.moveMonth(this.date, dir);
19267                     newViewDate = this.moveMonth(this.viewDate, dir);
19268                 } else {
19269                     newDate = new Date(this.date);
19270                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19271                     newViewDate = new Date(this.viewDate);
19272                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19273                 }
19274                 if (this.dateWithinRange(newDate)){
19275                     this.date = newDate;
19276                     this.viewDate = newViewDate;
19277                     this.setValue(this.formatDate(this.date));
19278 //                    this.update();
19279                     e.preventDefault();
19280                     dateChanged = true;
19281                 }
19282                 break;
19283             case 13: // enter
19284                 this.setValue(this.formatDate(this.date));
19285                 this.hidePopup();
19286                 e.preventDefault();
19287                 break;
19288             case 9: // tab
19289                 this.setValue(this.formatDate(this.date));
19290                 this.hidePopup();
19291                 break;
19292             case 16: // shift
19293             case 17: // ctrl
19294             case 18: // alt
19295                 break;
19296             default :
19297                 this.hidePopup();
19298                 
19299         }
19300     },
19301     
19302     
19303     onClick: function(e) 
19304     {
19305         e.stopPropagation();
19306         e.preventDefault();
19307         
19308         var target = e.getTarget();
19309         
19310         if(target.nodeName.toLowerCase() === 'i'){
19311             target = Roo.get(target).dom.parentNode;
19312         }
19313         
19314         var nodeName = target.nodeName;
19315         var className = target.className;
19316         var html = target.innerHTML;
19317         //Roo.log(nodeName);
19318         
19319         switch(nodeName.toLowerCase()) {
19320             case 'th':
19321                 switch(className) {
19322                     case 'switch':
19323                         this.showMode(1);
19324                         break;
19325                     case 'prev':
19326                     case 'next':
19327                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19328                         switch(this.viewMode){
19329                                 case 0:
19330                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19331                                         break;
19332                                 case 1:
19333                                 case 2:
19334                                         this.viewDate = this.moveYear(this.viewDate, dir);
19335                                         break;
19336                         }
19337                         this.fill();
19338                         break;
19339                     case 'today':
19340                         var date = new Date();
19341                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19342 //                        this.fill()
19343                         this.setValue(this.formatDate(this.date));
19344                         
19345                         this.hidePopup();
19346                         break;
19347                 }
19348                 break;
19349             case 'span':
19350                 if (className.indexOf('disabled') < 0) {
19351                     this.viewDate.setUTCDate(1);
19352                     if (className.indexOf('month') > -1) {
19353                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19354                     } else {
19355                         var year = parseInt(html, 10) || 0;
19356                         this.viewDate.setUTCFullYear(year);
19357                         
19358                     }
19359                     
19360                     if(this.singleMode){
19361                         this.setValue(this.formatDate(this.viewDate));
19362                         this.hidePopup();
19363                         return;
19364                     }
19365                     
19366                     this.showMode(-1);
19367                     this.fill();
19368                 }
19369                 break;
19370                 
19371             case 'td':
19372                 //Roo.log(className);
19373                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19374                     var day = parseInt(html, 10) || 1;
19375                     var year = this.viewDate.getUTCFullYear(),
19376                         month = this.viewDate.getUTCMonth();
19377
19378                     if (className.indexOf('old') > -1) {
19379                         if(month === 0 ){
19380                             month = 11;
19381                             year -= 1;
19382                         }else{
19383                             month -= 1;
19384                         }
19385                     } else if (className.indexOf('new') > -1) {
19386                         if (month == 11) {
19387                             month = 0;
19388                             year += 1;
19389                         } else {
19390                             month += 1;
19391                         }
19392                     }
19393                     //Roo.log([year,month,day]);
19394                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19395                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19396 //                    this.fill();
19397                     //Roo.log(this.formatDate(this.date));
19398                     this.setValue(this.formatDate(this.date));
19399                     this.hidePopup();
19400                 }
19401                 break;
19402         }
19403     },
19404     
19405     setStartDate: function(startDate)
19406     {
19407         this.startDate = startDate || -Infinity;
19408         if (this.startDate !== -Infinity) {
19409             this.startDate = this.parseDate(this.startDate);
19410         }
19411         this.update();
19412         this.updateNavArrows();
19413     },
19414
19415     setEndDate: function(endDate)
19416     {
19417         this.endDate = endDate || Infinity;
19418         if (this.endDate !== Infinity) {
19419             this.endDate = this.parseDate(this.endDate);
19420         }
19421         this.update();
19422         this.updateNavArrows();
19423     },
19424     
19425     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19426     {
19427         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19428         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19429             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19430         }
19431         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19432             return parseInt(d, 10);
19433         });
19434         this.update();
19435         this.updateNavArrows();
19436     },
19437     
19438     updateNavArrows: function() 
19439     {
19440         if(this.singleMode){
19441             return;
19442         }
19443         
19444         var d = new Date(this.viewDate),
19445         year = d.getUTCFullYear(),
19446         month = d.getUTCMonth();
19447         
19448         Roo.each(this.picker().select('.prev', true).elements, function(v){
19449             v.show();
19450             switch (this.viewMode) {
19451                 case 0:
19452
19453                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19454                         v.hide();
19455                     }
19456                     break;
19457                 case 1:
19458                 case 2:
19459                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19460                         v.hide();
19461                     }
19462                     break;
19463             }
19464         });
19465         
19466         Roo.each(this.picker().select('.next', true).elements, function(v){
19467             v.show();
19468             switch (this.viewMode) {
19469                 case 0:
19470
19471                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19472                         v.hide();
19473                     }
19474                     break;
19475                 case 1:
19476                 case 2:
19477                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19478                         v.hide();
19479                     }
19480                     break;
19481             }
19482         })
19483     },
19484     
19485     moveMonth: function(date, dir)
19486     {
19487         if (!dir) {
19488             return date;
19489         }
19490         var new_date = new Date(date.valueOf()),
19491         day = new_date.getUTCDate(),
19492         month = new_date.getUTCMonth(),
19493         mag = Math.abs(dir),
19494         new_month, test;
19495         dir = dir > 0 ? 1 : -1;
19496         if (mag == 1){
19497             test = dir == -1
19498             // If going back one month, make sure month is not current month
19499             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19500             ? function(){
19501                 return new_date.getUTCMonth() == month;
19502             }
19503             // If going forward one month, make sure month is as expected
19504             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19505             : function(){
19506                 return new_date.getUTCMonth() != new_month;
19507             };
19508             new_month = month + dir;
19509             new_date.setUTCMonth(new_month);
19510             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19511             if (new_month < 0 || new_month > 11) {
19512                 new_month = (new_month + 12) % 12;
19513             }
19514         } else {
19515             // For magnitudes >1, move one month at a time...
19516             for (var i=0; i<mag; i++) {
19517                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19518                 new_date = this.moveMonth(new_date, dir);
19519             }
19520             // ...then reset the day, keeping it in the new month
19521             new_month = new_date.getUTCMonth();
19522             new_date.setUTCDate(day);
19523             test = function(){
19524                 return new_month != new_date.getUTCMonth();
19525             };
19526         }
19527         // Common date-resetting loop -- if date is beyond end of month, make it
19528         // end of month
19529         while (test()){
19530             new_date.setUTCDate(--day);
19531             new_date.setUTCMonth(new_month);
19532         }
19533         return new_date;
19534     },
19535
19536     moveYear: function(date, dir)
19537     {
19538         return this.moveMonth(date, dir*12);
19539     },
19540
19541     dateWithinRange: function(date)
19542     {
19543         return date >= this.startDate && date <= this.endDate;
19544     },
19545
19546     
19547     remove: function() 
19548     {
19549         this.picker().remove();
19550     },
19551     
19552     validateValue : function(value)
19553     {
19554         if(this.getVisibilityEl().hasClass('hidden')){
19555             return true;
19556         }
19557         
19558         if(value.length < 1)  {
19559             if(this.allowBlank){
19560                 return true;
19561             }
19562             return false;
19563         }
19564         
19565         if(value.length < this.minLength){
19566             return false;
19567         }
19568         if(value.length > this.maxLength){
19569             return false;
19570         }
19571         if(this.vtype){
19572             var vt = Roo.form.VTypes;
19573             if(!vt[this.vtype](value, this)){
19574                 return false;
19575             }
19576         }
19577         if(typeof this.validator == "function"){
19578             var msg = this.validator(value);
19579             if(msg !== true){
19580                 return false;
19581             }
19582         }
19583         
19584         if(this.regex && !this.regex.test(value)){
19585             return false;
19586         }
19587         
19588         if(typeof(this.parseDate(value)) == 'undefined'){
19589             return false;
19590         }
19591         
19592         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19593             return false;
19594         }      
19595         
19596         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19597             return false;
19598         } 
19599         
19600         
19601         return true;
19602     },
19603     
19604     reset : function()
19605     {
19606         this.date = this.viewDate = '';
19607         
19608         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19609     }
19610    
19611 });
19612
19613 Roo.apply(Roo.bootstrap.DateField,  {
19614     
19615     head : {
19616         tag: 'thead',
19617         cn: [
19618         {
19619             tag: 'tr',
19620             cn: [
19621             {
19622                 tag: 'th',
19623                 cls: 'prev',
19624                 html: '<i class="fa fa-arrow-left"/>'
19625             },
19626             {
19627                 tag: 'th',
19628                 cls: 'switch',
19629                 colspan: '5'
19630             },
19631             {
19632                 tag: 'th',
19633                 cls: 'next',
19634                 html: '<i class="fa fa-arrow-right"/>'
19635             }
19636
19637             ]
19638         }
19639         ]
19640     },
19641     
19642     content : {
19643         tag: 'tbody',
19644         cn: [
19645         {
19646             tag: 'tr',
19647             cn: [
19648             {
19649                 tag: 'td',
19650                 colspan: '7'
19651             }
19652             ]
19653         }
19654         ]
19655     },
19656     
19657     footer : {
19658         tag: 'tfoot',
19659         cn: [
19660         {
19661             tag: 'tr',
19662             cn: [
19663             {
19664                 tag: 'th',
19665                 colspan: '7',
19666                 cls: 'today'
19667             }
19668                     
19669             ]
19670         }
19671         ]
19672     },
19673     
19674     dates:{
19675         en: {
19676             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19677             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19678             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19679             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19680             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19681             today: "Today"
19682         }
19683     },
19684     
19685     modes: [
19686     {
19687         clsName: 'days',
19688         navFnc: 'Month',
19689         navStep: 1
19690     },
19691     {
19692         clsName: 'months',
19693         navFnc: 'FullYear',
19694         navStep: 1
19695     },
19696     {
19697         clsName: 'years',
19698         navFnc: 'FullYear',
19699         navStep: 10
19700     }]
19701 });
19702
19703 Roo.apply(Roo.bootstrap.DateField,  {
19704   
19705     template : {
19706         tag: 'div',
19707         cls: 'datepicker dropdown-menu roo-dynamic',
19708         cn: [
19709         {
19710             tag: 'div',
19711             cls: 'datepicker-days',
19712             cn: [
19713             {
19714                 tag: 'table',
19715                 cls: 'table-condensed',
19716                 cn:[
19717                 Roo.bootstrap.DateField.head,
19718                 {
19719                     tag: 'tbody'
19720                 },
19721                 Roo.bootstrap.DateField.footer
19722                 ]
19723             }
19724             ]
19725         },
19726         {
19727             tag: 'div',
19728             cls: 'datepicker-months',
19729             cn: [
19730             {
19731                 tag: 'table',
19732                 cls: 'table-condensed',
19733                 cn:[
19734                 Roo.bootstrap.DateField.head,
19735                 Roo.bootstrap.DateField.content,
19736                 Roo.bootstrap.DateField.footer
19737                 ]
19738             }
19739             ]
19740         },
19741         {
19742             tag: 'div',
19743             cls: 'datepicker-years',
19744             cn: [
19745             {
19746                 tag: 'table',
19747                 cls: 'table-condensed',
19748                 cn:[
19749                 Roo.bootstrap.DateField.head,
19750                 Roo.bootstrap.DateField.content,
19751                 Roo.bootstrap.DateField.footer
19752                 ]
19753             }
19754             ]
19755         }
19756         ]
19757     }
19758 });
19759
19760  
19761
19762  /*
19763  * - LGPL
19764  *
19765  * TimeField
19766  * 
19767  */
19768
19769 /**
19770  * @class Roo.bootstrap.TimeField
19771  * @extends Roo.bootstrap.Input
19772  * Bootstrap DateField class
19773  * 
19774  * 
19775  * @constructor
19776  * Create a new TimeField
19777  * @param {Object} config The config object
19778  */
19779
19780 Roo.bootstrap.TimeField = function(config){
19781     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19782     this.addEvents({
19783             /**
19784              * @event show
19785              * Fires when this field show.
19786              * @param {Roo.bootstrap.DateField} thisthis
19787              * @param {Mixed} date The date value
19788              */
19789             show : true,
19790             /**
19791              * @event show
19792              * Fires when this field hide.
19793              * @param {Roo.bootstrap.DateField} this
19794              * @param {Mixed} date The date value
19795              */
19796             hide : true,
19797             /**
19798              * @event select
19799              * Fires when select a date.
19800              * @param {Roo.bootstrap.DateField} this
19801              * @param {Mixed} date The date value
19802              */
19803             select : true
19804         });
19805 };
19806
19807 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19808     
19809     /**
19810      * @cfg {String} format
19811      * The default time format string which can be overriden for localization support.  The format must be
19812      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19813      */
19814     format : "H:i",
19815        
19816     onRender: function(ct, position)
19817     {
19818         
19819         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19820                 
19821         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19822         
19823         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19824         
19825         this.pop = this.picker().select('>.datepicker-time',true).first();
19826         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19827         
19828         this.picker().on('mousedown', this.onMousedown, this);
19829         this.picker().on('click', this.onClick, this);
19830         
19831         this.picker().addClass('datepicker-dropdown');
19832     
19833         this.fillTime();
19834         this.update();
19835             
19836         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19837         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19838         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19839         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19840         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19841         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19842
19843     },
19844     
19845     fireKey: function(e){
19846         if (!this.picker().isVisible()){
19847             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19848                 this.show();
19849             }
19850             return;
19851         }
19852
19853         e.preventDefault();
19854         
19855         switch(e.keyCode){
19856             case 27: // escape
19857                 this.hide();
19858                 break;
19859             case 37: // left
19860             case 39: // right
19861                 this.onTogglePeriod();
19862                 break;
19863             case 38: // up
19864                 this.onIncrementMinutes();
19865                 break;
19866             case 40: // down
19867                 this.onDecrementMinutes();
19868                 break;
19869             case 13: // enter
19870             case 9: // tab
19871                 this.setTime();
19872                 break;
19873         }
19874     },
19875     
19876     onClick: function(e) {
19877         e.stopPropagation();
19878         e.preventDefault();
19879     },
19880     
19881     picker : function()
19882     {
19883         return this.el.select('.datepicker', true).first();
19884     },
19885     
19886     fillTime: function()
19887     {    
19888         var time = this.pop.select('tbody', true).first();
19889         
19890         time.dom.innerHTML = '';
19891         
19892         time.createChild({
19893             tag: 'tr',
19894             cn: [
19895                 {
19896                     tag: 'td',
19897                     cn: [
19898                         {
19899                             tag: 'a',
19900                             href: '#',
19901                             cls: 'btn',
19902                             cn: [
19903                                 {
19904                                     tag: 'span',
19905                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19906                                 }
19907                             ]
19908                         } 
19909                     ]
19910                 },
19911                 {
19912                     tag: 'td',
19913                     cls: 'separator'
19914                 },
19915                 {
19916                     tag: 'td',
19917                     cn: [
19918                         {
19919                             tag: 'a',
19920                             href: '#',
19921                             cls: 'btn',
19922                             cn: [
19923                                 {
19924                                     tag: 'span',
19925                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19926                                 }
19927                             ]
19928                         }
19929                     ]
19930                 },
19931                 {
19932                     tag: 'td',
19933                     cls: 'separator'
19934                 }
19935             ]
19936         });
19937         
19938         time.createChild({
19939             tag: 'tr',
19940             cn: [
19941                 {
19942                     tag: 'td',
19943                     cn: [
19944                         {
19945                             tag: 'span',
19946                             cls: 'timepicker-hour',
19947                             html: '00'
19948                         }  
19949                     ]
19950                 },
19951                 {
19952                     tag: 'td',
19953                     cls: 'separator',
19954                     html: ':'
19955                 },
19956                 {
19957                     tag: 'td',
19958                     cn: [
19959                         {
19960                             tag: 'span',
19961                             cls: 'timepicker-minute',
19962                             html: '00'
19963                         }  
19964                     ]
19965                 },
19966                 {
19967                     tag: 'td',
19968                     cls: 'separator'
19969                 },
19970                 {
19971                     tag: 'td',
19972                     cn: [
19973                         {
19974                             tag: 'button',
19975                             type: 'button',
19976                             cls: 'btn btn-primary period',
19977                             html: 'AM'
19978                             
19979                         }
19980                     ]
19981                 }
19982             ]
19983         });
19984         
19985         time.createChild({
19986             tag: 'tr',
19987             cn: [
19988                 {
19989                     tag: 'td',
19990                     cn: [
19991                         {
19992                             tag: 'a',
19993                             href: '#',
19994                             cls: 'btn',
19995                             cn: [
19996                                 {
19997                                     tag: 'span',
19998                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19999                                 }
20000                             ]
20001                         }
20002                     ]
20003                 },
20004                 {
20005                     tag: 'td',
20006                     cls: 'separator'
20007                 },
20008                 {
20009                     tag: 'td',
20010                     cn: [
20011                         {
20012                             tag: 'a',
20013                             href: '#',
20014                             cls: 'btn',
20015                             cn: [
20016                                 {
20017                                     tag: 'span',
20018                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20019                                 }
20020                             ]
20021                         }
20022                     ]
20023                 },
20024                 {
20025                     tag: 'td',
20026                     cls: 'separator'
20027                 }
20028             ]
20029         });
20030         
20031     },
20032     
20033     update: function()
20034     {
20035         
20036         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20037         
20038         this.fill();
20039     },
20040     
20041     fill: function() 
20042     {
20043         var hours = this.time.getHours();
20044         var minutes = this.time.getMinutes();
20045         var period = 'AM';
20046         
20047         if(hours > 11){
20048             period = 'PM';
20049         }
20050         
20051         if(hours == 0){
20052             hours = 12;
20053         }
20054         
20055         
20056         if(hours > 12){
20057             hours = hours - 12;
20058         }
20059         
20060         if(hours < 10){
20061             hours = '0' + hours;
20062         }
20063         
20064         if(minutes < 10){
20065             minutes = '0' + minutes;
20066         }
20067         
20068         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20069         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20070         this.pop.select('button', true).first().dom.innerHTML = period;
20071         
20072     },
20073     
20074     place: function()
20075     {   
20076         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20077         
20078         var cls = ['bottom'];
20079         
20080         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20081             cls.pop();
20082             cls.push('top');
20083         }
20084         
20085         cls.push('right');
20086         
20087         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20088             cls.pop();
20089             cls.push('left');
20090         }
20091         
20092         this.picker().addClass(cls.join('-'));
20093         
20094         var _this = this;
20095         
20096         Roo.each(cls, function(c){
20097             if(c == 'bottom'){
20098                 _this.picker().setTop(_this.inputEl().getHeight());
20099                 return;
20100             }
20101             if(c == 'top'){
20102                 _this.picker().setTop(0 - _this.picker().getHeight());
20103                 return;
20104             }
20105             
20106             if(c == 'left'){
20107                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20108                 return;
20109             }
20110             if(c == 'right'){
20111                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20112                 return;
20113             }
20114         });
20115         
20116     },
20117   
20118     onFocus : function()
20119     {
20120         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20121         this.show();
20122     },
20123     
20124     onBlur : function()
20125     {
20126         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20127         this.hide();
20128     },
20129     
20130     show : function()
20131     {
20132         this.picker().show();
20133         this.pop.show();
20134         this.update();
20135         this.place();
20136         
20137         this.fireEvent('show', this, this.date);
20138     },
20139     
20140     hide : function()
20141     {
20142         this.picker().hide();
20143         this.pop.hide();
20144         
20145         this.fireEvent('hide', this, this.date);
20146     },
20147     
20148     setTime : function()
20149     {
20150         this.hide();
20151         this.setValue(this.time.format(this.format));
20152         
20153         this.fireEvent('select', this, this.date);
20154         
20155         
20156     },
20157     
20158     onMousedown: function(e){
20159         e.stopPropagation();
20160         e.preventDefault();
20161     },
20162     
20163     onIncrementHours: function()
20164     {
20165         Roo.log('onIncrementHours');
20166         this.time = this.time.add(Date.HOUR, 1);
20167         this.update();
20168         
20169     },
20170     
20171     onDecrementHours: function()
20172     {
20173         Roo.log('onDecrementHours');
20174         this.time = this.time.add(Date.HOUR, -1);
20175         this.update();
20176     },
20177     
20178     onIncrementMinutes: function()
20179     {
20180         Roo.log('onIncrementMinutes');
20181         this.time = this.time.add(Date.MINUTE, 1);
20182         this.update();
20183     },
20184     
20185     onDecrementMinutes: function()
20186     {
20187         Roo.log('onDecrementMinutes');
20188         this.time = this.time.add(Date.MINUTE, -1);
20189         this.update();
20190     },
20191     
20192     onTogglePeriod: function()
20193     {
20194         Roo.log('onTogglePeriod');
20195         this.time = this.time.add(Date.HOUR, 12);
20196         this.update();
20197     }
20198     
20199    
20200 });
20201
20202 Roo.apply(Roo.bootstrap.TimeField,  {
20203     
20204     content : {
20205         tag: 'tbody',
20206         cn: [
20207             {
20208                 tag: 'tr',
20209                 cn: [
20210                 {
20211                     tag: 'td',
20212                     colspan: '7'
20213                 }
20214                 ]
20215             }
20216         ]
20217     },
20218     
20219     footer : {
20220         tag: 'tfoot',
20221         cn: [
20222             {
20223                 tag: 'tr',
20224                 cn: [
20225                 {
20226                     tag: 'th',
20227                     colspan: '7',
20228                     cls: '',
20229                     cn: [
20230                         {
20231                             tag: 'button',
20232                             cls: 'btn btn-info ok',
20233                             html: 'OK'
20234                         }
20235                     ]
20236                 }
20237
20238                 ]
20239             }
20240         ]
20241     }
20242 });
20243
20244 Roo.apply(Roo.bootstrap.TimeField,  {
20245   
20246     template : {
20247         tag: 'div',
20248         cls: 'datepicker dropdown-menu',
20249         cn: [
20250             {
20251                 tag: 'div',
20252                 cls: 'datepicker-time',
20253                 cn: [
20254                 {
20255                     tag: 'table',
20256                     cls: 'table-condensed',
20257                     cn:[
20258                     Roo.bootstrap.TimeField.content,
20259                     Roo.bootstrap.TimeField.footer
20260                     ]
20261                 }
20262                 ]
20263             }
20264         ]
20265     }
20266 });
20267
20268  
20269
20270  /*
20271  * - LGPL
20272  *
20273  * MonthField
20274  * 
20275  */
20276
20277 /**
20278  * @class Roo.bootstrap.MonthField
20279  * @extends Roo.bootstrap.Input
20280  * Bootstrap MonthField class
20281  * 
20282  * @cfg {String} language default en
20283  * 
20284  * @constructor
20285  * Create a new MonthField
20286  * @param {Object} config The config object
20287  */
20288
20289 Roo.bootstrap.MonthField = function(config){
20290     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20291     
20292     this.addEvents({
20293         /**
20294          * @event show
20295          * Fires when this field show.
20296          * @param {Roo.bootstrap.MonthField} this
20297          * @param {Mixed} date The date value
20298          */
20299         show : true,
20300         /**
20301          * @event show
20302          * Fires when this field hide.
20303          * @param {Roo.bootstrap.MonthField} this
20304          * @param {Mixed} date The date value
20305          */
20306         hide : true,
20307         /**
20308          * @event select
20309          * Fires when select a date.
20310          * @param {Roo.bootstrap.MonthField} this
20311          * @param {String} oldvalue The old value
20312          * @param {String} newvalue The new value
20313          */
20314         select : true
20315     });
20316 };
20317
20318 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20319     
20320     onRender: function(ct, position)
20321     {
20322         
20323         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20324         
20325         this.language = this.language || 'en';
20326         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20327         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20328         
20329         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20330         this.isInline = false;
20331         this.isInput = true;
20332         this.component = this.el.select('.add-on', true).first() || false;
20333         this.component = (this.component && this.component.length === 0) ? false : this.component;
20334         this.hasInput = this.component && this.inputEL().length;
20335         
20336         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20337         
20338         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20339         
20340         this.picker().on('mousedown', this.onMousedown, this);
20341         this.picker().on('click', this.onClick, this);
20342         
20343         this.picker().addClass('datepicker-dropdown');
20344         
20345         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20346             v.setStyle('width', '189px');
20347         });
20348         
20349         this.fillMonths();
20350         
20351         this.update();
20352         
20353         if(this.isInline) {
20354             this.show();
20355         }
20356         
20357     },
20358     
20359     setValue: function(v, suppressEvent)
20360     {   
20361         var o = this.getValue();
20362         
20363         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20364         
20365         this.update();
20366
20367         if(suppressEvent !== true){
20368             this.fireEvent('select', this, o, v);
20369         }
20370         
20371     },
20372     
20373     getValue: function()
20374     {
20375         return this.value;
20376     },
20377     
20378     onClick: function(e) 
20379     {
20380         e.stopPropagation();
20381         e.preventDefault();
20382         
20383         var target = e.getTarget();
20384         
20385         if(target.nodeName.toLowerCase() === 'i'){
20386             target = Roo.get(target).dom.parentNode;
20387         }
20388         
20389         var nodeName = target.nodeName;
20390         var className = target.className;
20391         var html = target.innerHTML;
20392         
20393         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20394             return;
20395         }
20396         
20397         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20398         
20399         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20400         
20401         this.hide();
20402                         
20403     },
20404     
20405     picker : function()
20406     {
20407         return this.pickerEl;
20408     },
20409     
20410     fillMonths: function()
20411     {    
20412         var i = 0;
20413         var months = this.picker().select('>.datepicker-months td', true).first();
20414         
20415         months.dom.innerHTML = '';
20416         
20417         while (i < 12) {
20418             var month = {
20419                 tag: 'span',
20420                 cls: 'month',
20421                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20422             };
20423             
20424             months.createChild(month);
20425         }
20426         
20427     },
20428     
20429     update: function()
20430     {
20431         var _this = this;
20432         
20433         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20434             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20435         }
20436         
20437         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20438             e.removeClass('active');
20439             
20440             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20441                 e.addClass('active');
20442             }
20443         })
20444     },
20445     
20446     place: function()
20447     {
20448         if(this.isInline) {
20449             return;
20450         }
20451         
20452         this.picker().removeClass(['bottom', 'top']);
20453         
20454         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20455             /*
20456              * place to the top of element!
20457              *
20458              */
20459             
20460             this.picker().addClass('top');
20461             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20462             
20463             return;
20464         }
20465         
20466         this.picker().addClass('bottom');
20467         
20468         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20469     },
20470     
20471     onFocus : function()
20472     {
20473         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20474         this.show();
20475     },
20476     
20477     onBlur : function()
20478     {
20479         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20480         
20481         var d = this.inputEl().getValue();
20482         
20483         this.setValue(d);
20484                 
20485         this.hide();
20486     },
20487     
20488     show : function()
20489     {
20490         this.picker().show();
20491         this.picker().select('>.datepicker-months', true).first().show();
20492         this.update();
20493         this.place();
20494         
20495         this.fireEvent('show', this, this.date);
20496     },
20497     
20498     hide : function()
20499     {
20500         if(this.isInline) {
20501             return;
20502         }
20503         this.picker().hide();
20504         this.fireEvent('hide', this, this.date);
20505         
20506     },
20507     
20508     onMousedown: function(e)
20509     {
20510         e.stopPropagation();
20511         e.preventDefault();
20512     },
20513     
20514     keyup: function(e)
20515     {
20516         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20517         this.update();
20518     },
20519
20520     fireKey: function(e)
20521     {
20522         if (!this.picker().isVisible()){
20523             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20524                 this.show();
20525             }
20526             return;
20527         }
20528         
20529         var dir;
20530         
20531         switch(e.keyCode){
20532             case 27: // escape
20533                 this.hide();
20534                 e.preventDefault();
20535                 break;
20536             case 37: // left
20537             case 39: // right
20538                 dir = e.keyCode == 37 ? -1 : 1;
20539                 
20540                 this.vIndex = this.vIndex + dir;
20541                 
20542                 if(this.vIndex < 0){
20543                     this.vIndex = 0;
20544                 }
20545                 
20546                 if(this.vIndex > 11){
20547                     this.vIndex = 11;
20548                 }
20549                 
20550                 if(isNaN(this.vIndex)){
20551                     this.vIndex = 0;
20552                 }
20553                 
20554                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20555                 
20556                 break;
20557             case 38: // up
20558             case 40: // down
20559                 
20560                 dir = e.keyCode == 38 ? -1 : 1;
20561                 
20562                 this.vIndex = this.vIndex + dir * 4;
20563                 
20564                 if(this.vIndex < 0){
20565                     this.vIndex = 0;
20566                 }
20567                 
20568                 if(this.vIndex > 11){
20569                     this.vIndex = 11;
20570                 }
20571                 
20572                 if(isNaN(this.vIndex)){
20573                     this.vIndex = 0;
20574                 }
20575                 
20576                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20577                 break;
20578                 
20579             case 13: // enter
20580                 
20581                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20582                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20583                 }
20584                 
20585                 this.hide();
20586                 e.preventDefault();
20587                 break;
20588             case 9: // tab
20589                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20590                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20591                 }
20592                 this.hide();
20593                 break;
20594             case 16: // shift
20595             case 17: // ctrl
20596             case 18: // alt
20597                 break;
20598             default :
20599                 this.hide();
20600                 
20601         }
20602     },
20603     
20604     remove: function() 
20605     {
20606         this.picker().remove();
20607     }
20608    
20609 });
20610
20611 Roo.apply(Roo.bootstrap.MonthField,  {
20612     
20613     content : {
20614         tag: 'tbody',
20615         cn: [
20616         {
20617             tag: 'tr',
20618             cn: [
20619             {
20620                 tag: 'td',
20621                 colspan: '7'
20622             }
20623             ]
20624         }
20625         ]
20626     },
20627     
20628     dates:{
20629         en: {
20630             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20631             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20632         }
20633     }
20634 });
20635
20636 Roo.apply(Roo.bootstrap.MonthField,  {
20637   
20638     template : {
20639         tag: 'div',
20640         cls: 'datepicker dropdown-menu roo-dynamic',
20641         cn: [
20642             {
20643                 tag: 'div',
20644                 cls: 'datepicker-months',
20645                 cn: [
20646                 {
20647                     tag: 'table',
20648                     cls: 'table-condensed',
20649                     cn:[
20650                         Roo.bootstrap.DateField.content
20651                     ]
20652                 }
20653                 ]
20654             }
20655         ]
20656     }
20657 });
20658
20659  
20660
20661  
20662  /*
20663  * - LGPL
20664  *
20665  * CheckBox
20666  * 
20667  */
20668
20669 /**
20670  * @class Roo.bootstrap.CheckBox
20671  * @extends Roo.bootstrap.Input
20672  * Bootstrap CheckBox class
20673  * 
20674  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20675  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20676  * @cfg {String} boxLabel The text that appears beside the checkbox
20677  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20678  * @cfg {Boolean} checked initnal the element
20679  * @cfg {Boolean} inline inline the element (default false)
20680  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20681  * @cfg {String} tooltip label tooltip
20682  * 
20683  * @constructor
20684  * Create a new CheckBox
20685  * @param {Object} config The config object
20686  */
20687
20688 Roo.bootstrap.CheckBox = function(config){
20689     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20690    
20691     this.addEvents({
20692         /**
20693         * @event check
20694         * Fires when the element is checked or unchecked.
20695         * @param {Roo.bootstrap.CheckBox} this This input
20696         * @param {Boolean} checked The new checked value
20697         */
20698        check : true,
20699        /**
20700         * @event click
20701         * Fires when the element is click.
20702         * @param {Roo.bootstrap.CheckBox} this This input
20703         */
20704        click : true
20705     });
20706     
20707 };
20708
20709 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20710   
20711     inputType: 'checkbox',
20712     inputValue: 1,
20713     valueOff: 0,
20714     boxLabel: false,
20715     checked: false,
20716     weight : false,
20717     inline: false,
20718     tooltip : '',
20719     
20720     getAutoCreate : function()
20721     {
20722         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20723         
20724         var id = Roo.id();
20725         
20726         var cfg = {};
20727         
20728         cfg.cls = 'form-group ' + this.inputType; //input-group
20729         
20730         if(this.inline){
20731             cfg.cls += ' ' + this.inputType + '-inline';
20732         }
20733         
20734         var input =  {
20735             tag: 'input',
20736             id : id,
20737             type : this.inputType,
20738             value : this.inputValue,
20739             cls : 'roo-' + this.inputType, //'form-box',
20740             placeholder : this.placeholder || ''
20741             
20742         };
20743         
20744         if(this.inputType != 'radio'){
20745             var hidden =  {
20746                 tag: 'input',
20747                 type : 'hidden',
20748                 cls : 'roo-hidden-value',
20749                 value : this.checked ? this.inputValue : this.valueOff
20750             };
20751         }
20752         
20753             
20754         if (this.weight) { // Validity check?
20755             cfg.cls += " " + this.inputType + "-" + this.weight;
20756         }
20757         
20758         if (this.disabled) {
20759             input.disabled=true;
20760         }
20761         
20762         if(this.checked){
20763             input.checked = this.checked;
20764         }
20765         
20766         if (this.name) {
20767             
20768             input.name = this.name;
20769             
20770             if(this.inputType != 'radio'){
20771                 hidden.name = this.name;
20772                 input.name = '_hidden_' + this.name;
20773             }
20774         }
20775         
20776         if (this.size) {
20777             input.cls += ' input-' + this.size;
20778         }
20779         
20780         var settings=this;
20781         
20782         ['xs','sm','md','lg'].map(function(size){
20783             if (settings[size]) {
20784                 cfg.cls += ' col-' + size + '-' + settings[size];
20785             }
20786         });
20787         
20788         var inputblock = input;
20789          
20790         if (this.before || this.after) {
20791             
20792             inputblock = {
20793                 cls : 'input-group',
20794                 cn :  [] 
20795             };
20796             
20797             if (this.before) {
20798                 inputblock.cn.push({
20799                     tag :'span',
20800                     cls : 'input-group-addon',
20801                     html : this.before
20802                 });
20803             }
20804             
20805             inputblock.cn.push(input);
20806             
20807             if(this.inputType != 'radio'){
20808                 inputblock.cn.push(hidden);
20809             }
20810             
20811             if (this.after) {
20812                 inputblock.cn.push({
20813                     tag :'span',
20814                     cls : 'input-group-addon',
20815                     html : this.after
20816                 });
20817             }
20818             
20819         }
20820         
20821         if (align ==='left' && this.fieldLabel.length) {
20822 //                Roo.log("left and has label");
20823             cfg.cn = [
20824                 {
20825                     tag: 'label',
20826                     'for' :  id,
20827                     cls : 'control-label',
20828                     html : this.fieldLabel
20829                 },
20830                 {
20831                     cls : "", 
20832                     cn: [
20833                         inputblock
20834                     ]
20835                 }
20836             ];
20837             
20838             if(this.labelWidth > 12){
20839                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20840             }
20841             
20842             if(this.labelWidth < 13 && this.labelmd == 0){
20843                 this.labelmd = this.labelWidth;
20844             }
20845             
20846             if(this.labellg > 0){
20847                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20848                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20849             }
20850             
20851             if(this.labelmd > 0){
20852                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20853                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20854             }
20855             
20856             if(this.labelsm > 0){
20857                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20858                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20859             }
20860             
20861             if(this.labelxs > 0){
20862                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20863                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20864             }
20865             
20866         } else if ( this.fieldLabel.length) {
20867 //                Roo.log(" label");
20868                 cfg.cn = [
20869                    
20870                     {
20871                         tag: this.boxLabel ? 'span' : 'label',
20872                         'for': id,
20873                         cls: 'control-label box-input-label',
20874                         //cls : 'input-group-addon',
20875                         html : this.fieldLabel
20876                     },
20877                     
20878                     inputblock
20879                     
20880                 ];
20881
20882         } else {
20883             
20884 //                Roo.log(" no label && no align");
20885                 cfg.cn = [  inputblock ] ;
20886                 
20887                 
20888         }
20889         
20890         if(this.boxLabel){
20891              var boxLabelCfg = {
20892                 tag: 'label',
20893                 //'for': id, // box label is handled by onclick - so no for...
20894                 cls: 'box-label',
20895                 html: this.boxLabel
20896             };
20897             
20898             if(this.tooltip){
20899                 boxLabelCfg.tooltip = this.tooltip;
20900             }
20901              
20902             cfg.cn.push(boxLabelCfg);
20903         }
20904         
20905         if(this.inputType != 'radio'){
20906             cfg.cn.push(hidden);
20907         }
20908         
20909         return cfg;
20910         
20911     },
20912     
20913     /**
20914      * return the real input element.
20915      */
20916     inputEl: function ()
20917     {
20918         return this.el.select('input.roo-' + this.inputType,true).first();
20919     },
20920     hiddenEl: function ()
20921     {
20922         return this.el.select('input.roo-hidden-value',true).first();
20923     },
20924     
20925     labelEl: function()
20926     {
20927         return this.el.select('label.control-label',true).first();
20928     },
20929     /* depricated... */
20930     
20931     label: function()
20932     {
20933         return this.labelEl();
20934     },
20935     
20936     boxLabelEl: function()
20937     {
20938         return this.el.select('label.box-label',true).first();
20939     },
20940     
20941     initEvents : function()
20942     {
20943 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20944         
20945         this.inputEl().on('click', this.onClick,  this);
20946         
20947         if (this.boxLabel) { 
20948             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20949         }
20950         
20951         this.startValue = this.getValue();
20952         
20953         if(this.groupId){
20954             Roo.bootstrap.CheckBox.register(this);
20955         }
20956     },
20957     
20958     onClick : function(e)
20959     {   
20960         if(this.fireEvent('click', this, e) !== false){
20961             this.setChecked(!this.checked);
20962         }
20963         
20964     },
20965     
20966     setChecked : function(state,suppressEvent)
20967     {
20968         this.startValue = this.getValue();
20969
20970         if(this.inputType == 'radio'){
20971             
20972             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20973                 e.dom.checked = false;
20974             });
20975             
20976             this.inputEl().dom.checked = true;
20977             
20978             this.inputEl().dom.value = this.inputValue;
20979             
20980             if(suppressEvent !== true){
20981                 this.fireEvent('check', this, true);
20982             }
20983             
20984             this.validate();
20985             
20986             return;
20987         }
20988         
20989         this.checked = state;
20990         
20991         this.inputEl().dom.checked = state;
20992         
20993         
20994         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20995         
20996         if(suppressEvent !== true){
20997             this.fireEvent('check', this, state);
20998         }
20999         
21000         this.validate();
21001     },
21002     
21003     getValue : function()
21004     {
21005         if(this.inputType == 'radio'){
21006             return this.getGroupValue();
21007         }
21008         
21009         return this.hiddenEl().dom.value;
21010         
21011     },
21012     
21013     getGroupValue : function()
21014     {
21015         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21016             return '';
21017         }
21018         
21019         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21020     },
21021     
21022     setValue : function(v,suppressEvent)
21023     {
21024         if(this.inputType == 'radio'){
21025             this.setGroupValue(v, suppressEvent);
21026             return;
21027         }
21028         
21029         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21030         
21031         this.validate();
21032     },
21033     
21034     setGroupValue : function(v, suppressEvent)
21035     {
21036         this.startValue = this.getValue();
21037         
21038         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21039             e.dom.checked = false;
21040             
21041             if(e.dom.value == v){
21042                 e.dom.checked = true;
21043             }
21044         });
21045         
21046         if(suppressEvent !== true){
21047             this.fireEvent('check', this, true);
21048         }
21049
21050         this.validate();
21051         
21052         return;
21053     },
21054     
21055     validate : function()
21056     {
21057         if(this.getVisibilityEl().hasClass('hidden')){
21058             return true;
21059         }
21060         
21061         if(
21062                 this.disabled || 
21063                 (this.inputType == 'radio' && this.validateRadio()) ||
21064                 (this.inputType == 'checkbox' && this.validateCheckbox())
21065         ){
21066             this.markValid();
21067             return true;
21068         }
21069         
21070         this.markInvalid();
21071         return false;
21072     },
21073     
21074     validateRadio : function()
21075     {
21076         if(this.getVisibilityEl().hasClass('hidden')){
21077             return true;
21078         }
21079         
21080         if(this.allowBlank){
21081             return true;
21082         }
21083         
21084         var valid = false;
21085         
21086         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21087             if(!e.dom.checked){
21088                 return;
21089             }
21090             
21091             valid = true;
21092             
21093             return false;
21094         });
21095         
21096         return valid;
21097     },
21098     
21099     validateCheckbox : function()
21100     {
21101         if(!this.groupId){
21102             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21103             //return (this.getValue() == this.inputValue) ? true : false;
21104         }
21105         
21106         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21107         
21108         if(!group){
21109             return false;
21110         }
21111         
21112         var r = false;
21113         
21114         for(var i in group){
21115             if(group[i].el.isVisible(true)){
21116                 r = false;
21117                 break;
21118             }
21119             
21120             r = true;
21121         }
21122         
21123         for(var i in group){
21124             if(r){
21125                 break;
21126             }
21127             
21128             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21129         }
21130         
21131         return r;
21132     },
21133     
21134     /**
21135      * Mark this field as valid
21136      */
21137     markValid : function()
21138     {
21139         var _this = this;
21140         
21141         this.fireEvent('valid', this);
21142         
21143         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21144         
21145         if(this.groupId){
21146             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21147         }
21148         
21149         if(label){
21150             label.markValid();
21151         }
21152
21153         if(this.inputType == 'radio'){
21154             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21155                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21156                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21157             });
21158             
21159             return;
21160         }
21161
21162         if(!this.groupId){
21163             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21164             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21165             return;
21166         }
21167         
21168         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21169         
21170         if(!group){
21171             return;
21172         }
21173         
21174         for(var i in group){
21175             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21176             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21177         }
21178     },
21179     
21180      /**
21181      * Mark this field as invalid
21182      * @param {String} msg The validation message
21183      */
21184     markInvalid : function(msg)
21185     {
21186         if(this.allowBlank){
21187             return;
21188         }
21189         
21190         var _this = this;
21191         
21192         this.fireEvent('invalid', this, msg);
21193         
21194         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21195         
21196         if(this.groupId){
21197             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21198         }
21199         
21200         if(label){
21201             label.markInvalid();
21202         }
21203             
21204         if(this.inputType == 'radio'){
21205             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21206                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21207                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21208             });
21209             
21210             return;
21211         }
21212         
21213         if(!this.groupId){
21214             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21215             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21216             return;
21217         }
21218         
21219         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21220         
21221         if(!group){
21222             return;
21223         }
21224         
21225         for(var i in group){
21226             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21227             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21228         }
21229         
21230     },
21231     
21232     clearInvalid : function()
21233     {
21234         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21235         
21236         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21237         
21238         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21239         
21240         if (label && label.iconEl) {
21241             label.iconEl.removeClass(label.validClass);
21242             label.iconEl.removeClass(label.invalidClass);
21243         }
21244     },
21245     
21246     disable : function()
21247     {
21248         if(this.inputType != 'radio'){
21249             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21250             return;
21251         }
21252         
21253         var _this = this;
21254         
21255         if(this.rendered){
21256             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21257                 _this.getActionEl().addClass(this.disabledClass);
21258                 e.dom.disabled = true;
21259             });
21260         }
21261         
21262         this.disabled = true;
21263         this.fireEvent("disable", this);
21264         return this;
21265     },
21266
21267     enable : function()
21268     {
21269         if(this.inputType != 'radio'){
21270             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21271             return;
21272         }
21273         
21274         var _this = this;
21275         
21276         if(this.rendered){
21277             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21278                 _this.getActionEl().removeClass(this.disabledClass);
21279                 e.dom.disabled = false;
21280             });
21281         }
21282         
21283         this.disabled = false;
21284         this.fireEvent("enable", this);
21285         return this;
21286     },
21287     
21288     setBoxLabel : function(v)
21289     {
21290         this.boxLabel = v;
21291         
21292         if(this.rendered){
21293             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21294         }
21295     }
21296
21297 });
21298
21299 Roo.apply(Roo.bootstrap.CheckBox, {
21300     
21301     groups: {},
21302     
21303      /**
21304     * register a CheckBox Group
21305     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21306     */
21307     register : function(checkbox)
21308     {
21309         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21310             this.groups[checkbox.groupId] = {};
21311         }
21312         
21313         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21314             return;
21315         }
21316         
21317         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21318         
21319     },
21320     /**
21321     * fetch a CheckBox Group based on the group ID
21322     * @param {string} the group ID
21323     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21324     */
21325     get: function(groupId) {
21326         if (typeof(this.groups[groupId]) == 'undefined') {
21327             return false;
21328         }
21329         
21330         return this.groups[groupId] ;
21331     }
21332     
21333     
21334 });
21335 /*
21336  * - LGPL
21337  *
21338  * RadioItem
21339  * 
21340  */
21341
21342 /**
21343  * @class Roo.bootstrap.Radio
21344  * @extends Roo.bootstrap.Component
21345  * Bootstrap Radio class
21346  * @cfg {String} boxLabel - the label associated
21347  * @cfg {String} value - the value of radio
21348  * 
21349  * @constructor
21350  * Create a new Radio
21351  * @param {Object} config The config object
21352  */
21353 Roo.bootstrap.Radio = function(config){
21354     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21355     
21356 };
21357
21358 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21359     
21360     boxLabel : '',
21361     
21362     value : '',
21363     
21364     getAutoCreate : function()
21365     {
21366         var cfg = {
21367             tag : 'div',
21368             cls : 'form-group radio',
21369             cn : [
21370                 {
21371                     tag : 'label',
21372                     cls : 'box-label',
21373                     html : this.boxLabel
21374                 }
21375             ]
21376         };
21377         
21378         return cfg;
21379     },
21380     
21381     initEvents : function() 
21382     {
21383         this.parent().register(this);
21384         
21385         this.el.on('click', this.onClick, this);
21386         
21387     },
21388     
21389     onClick : function(e)
21390     {
21391         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21392             this.setChecked(true);
21393         }
21394     },
21395     
21396     setChecked : function(state, suppressEvent)
21397     {
21398         this.parent().setValue(this.value, suppressEvent);
21399         
21400     },
21401     
21402     setBoxLabel : function(v)
21403     {
21404         this.boxLabel = v;
21405         
21406         if(this.rendered){
21407             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21408         }
21409     }
21410     
21411 });
21412  
21413
21414  /*
21415  * - LGPL
21416  *
21417  * Input
21418  * 
21419  */
21420
21421 /**
21422  * @class Roo.bootstrap.SecurePass
21423  * @extends Roo.bootstrap.Input
21424  * Bootstrap SecurePass class
21425  *
21426  * 
21427  * @constructor
21428  * Create a new SecurePass
21429  * @param {Object} config The config object
21430  */
21431  
21432 Roo.bootstrap.SecurePass = function (config) {
21433     // these go here, so the translation tool can replace them..
21434     this.errors = {
21435         PwdEmpty: "Please type a password, and then retype it to confirm.",
21436         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21437         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21438         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21439         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21440         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21441         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21442         TooWeak: "Your password is Too Weak."
21443     },
21444     this.meterLabel = "Password strength:";
21445     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21446     this.meterClass = [
21447         "roo-password-meter-tooweak", 
21448         "roo-password-meter-weak", 
21449         "roo-password-meter-medium", 
21450         "roo-password-meter-strong", 
21451         "roo-password-meter-grey"
21452     ];
21453     
21454     this.errors = {};
21455     
21456     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21457 }
21458
21459 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21460     /**
21461      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21462      * {
21463      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21464      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21465      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21466      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21467      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21468      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21469      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21470      * })
21471      */
21472     // private
21473     
21474     meterWidth: 300,
21475     errorMsg :'',    
21476     errors: false,
21477     imageRoot: '/',
21478     /**
21479      * @cfg {String/Object} Label for the strength meter (defaults to
21480      * 'Password strength:')
21481      */
21482     // private
21483     meterLabel: '',
21484     /**
21485      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21486      * ['Weak', 'Medium', 'Strong'])
21487      */
21488     // private    
21489     pwdStrengths: false,    
21490     // private
21491     strength: 0,
21492     // private
21493     _lastPwd: null,
21494     // private
21495     kCapitalLetter: 0,
21496     kSmallLetter: 1,
21497     kDigit: 2,
21498     kPunctuation: 3,
21499     
21500     insecure: false,
21501     // private
21502     initEvents: function ()
21503     {
21504         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21505
21506         if (this.el.is('input[type=password]') && Roo.isSafari) {
21507             this.el.on('keydown', this.SafariOnKeyDown, this);
21508         }
21509
21510         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21511     },
21512     // private
21513     onRender: function (ct, position)
21514     {
21515         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21516         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21517         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21518
21519         this.trigger.createChild({
21520                    cn: [
21521                     {
21522                     //id: 'PwdMeter',
21523                     tag: 'div',
21524                     cls: 'roo-password-meter-grey col-xs-12',
21525                     style: {
21526                         //width: 0,
21527                         //width: this.meterWidth + 'px'                                                
21528                         }
21529                     },
21530                     {                            
21531                          cls: 'roo-password-meter-text'                          
21532                     }
21533                 ]            
21534         });
21535
21536          
21537         if (this.hideTrigger) {
21538             this.trigger.setDisplayed(false);
21539         }
21540         this.setSize(this.width || '', this.height || '');
21541     },
21542     // private
21543     onDestroy: function ()
21544     {
21545         if (this.trigger) {
21546             this.trigger.removeAllListeners();
21547             this.trigger.remove();
21548         }
21549         if (this.wrap) {
21550             this.wrap.remove();
21551         }
21552         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21553     },
21554     // private
21555     checkStrength: function ()
21556     {
21557         var pwd = this.inputEl().getValue();
21558         if (pwd == this._lastPwd) {
21559             return;
21560         }
21561
21562         var strength;
21563         if (this.ClientSideStrongPassword(pwd)) {
21564             strength = 3;
21565         } else if (this.ClientSideMediumPassword(pwd)) {
21566             strength = 2;
21567         } else if (this.ClientSideWeakPassword(pwd)) {
21568             strength = 1;
21569         } else {
21570             strength = 0;
21571         }
21572         
21573         Roo.log('strength1: ' + strength);
21574         
21575         //var pm = this.trigger.child('div/div/div').dom;
21576         var pm = this.trigger.child('div/div');
21577         pm.removeClass(this.meterClass);
21578         pm.addClass(this.meterClass[strength]);
21579                 
21580         
21581         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21582                 
21583         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21584         
21585         this._lastPwd = pwd;
21586     },
21587     reset: function ()
21588     {
21589         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21590         
21591         this._lastPwd = '';
21592         
21593         var pm = this.trigger.child('div/div');
21594         pm.removeClass(this.meterClass);
21595         pm.addClass('roo-password-meter-grey');        
21596         
21597         
21598         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21599         
21600         pt.innerHTML = '';
21601         this.inputEl().dom.type='password';
21602     },
21603     // private
21604     validateValue: function (value)
21605     {
21606         
21607         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21608             return false;
21609         }
21610         if (value.length == 0) {
21611             if (this.allowBlank) {
21612                 this.clearInvalid();
21613                 return true;
21614             }
21615
21616             this.markInvalid(this.errors.PwdEmpty);
21617             this.errorMsg = this.errors.PwdEmpty;
21618             return false;
21619         }
21620         
21621         if(this.insecure){
21622             return true;
21623         }
21624         
21625         if ('[\x21-\x7e]*'.match(value)) {
21626             this.markInvalid(this.errors.PwdBadChar);
21627             this.errorMsg = this.errors.PwdBadChar;
21628             return false;
21629         }
21630         if (value.length < 6) {
21631             this.markInvalid(this.errors.PwdShort);
21632             this.errorMsg = this.errors.PwdShort;
21633             return false;
21634         }
21635         if (value.length > 16) {
21636             this.markInvalid(this.errors.PwdLong);
21637             this.errorMsg = this.errors.PwdLong;
21638             return false;
21639         }
21640         var strength;
21641         if (this.ClientSideStrongPassword(value)) {
21642             strength = 3;
21643         } else if (this.ClientSideMediumPassword(value)) {
21644             strength = 2;
21645         } else if (this.ClientSideWeakPassword(value)) {
21646             strength = 1;
21647         } else {
21648             strength = 0;
21649         }
21650
21651         
21652         if (strength < 2) {
21653             //this.markInvalid(this.errors.TooWeak);
21654             this.errorMsg = this.errors.TooWeak;
21655             //return false;
21656         }
21657         
21658         
21659         console.log('strength2: ' + strength);
21660         
21661         //var pm = this.trigger.child('div/div/div').dom;
21662         
21663         var pm = this.trigger.child('div/div');
21664         pm.removeClass(this.meterClass);
21665         pm.addClass(this.meterClass[strength]);
21666                 
21667         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21668                 
21669         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21670         
21671         this.errorMsg = ''; 
21672         return true;
21673     },
21674     // private
21675     CharacterSetChecks: function (type)
21676     {
21677         this.type = type;
21678         this.fResult = false;
21679     },
21680     // private
21681     isctype: function (character, type)
21682     {
21683         switch (type) {  
21684             case this.kCapitalLetter:
21685                 if (character >= 'A' && character <= 'Z') {
21686                     return true;
21687                 }
21688                 break;
21689             
21690             case this.kSmallLetter:
21691                 if (character >= 'a' && character <= 'z') {
21692                     return true;
21693                 }
21694                 break;
21695             
21696             case this.kDigit:
21697                 if (character >= '0' && character <= '9') {
21698                     return true;
21699                 }
21700                 break;
21701             
21702             case this.kPunctuation:
21703                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21704                     return true;
21705                 }
21706                 break;
21707             
21708             default:
21709                 return false;
21710         }
21711
21712     },
21713     // private
21714     IsLongEnough: function (pwd, size)
21715     {
21716         return !(pwd == null || isNaN(size) || pwd.length < size);
21717     },
21718     // private
21719     SpansEnoughCharacterSets: function (word, nb)
21720     {
21721         if (!this.IsLongEnough(word, nb))
21722         {
21723             return false;
21724         }
21725
21726         var characterSetChecks = new Array(
21727             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21728             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21729         );
21730         
21731         for (var index = 0; index < word.length; ++index) {
21732             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21733                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21734                     characterSetChecks[nCharSet].fResult = true;
21735                     break;
21736                 }
21737             }
21738         }
21739
21740         var nCharSets = 0;
21741         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21742             if (characterSetChecks[nCharSet].fResult) {
21743                 ++nCharSets;
21744             }
21745         }
21746
21747         if (nCharSets < nb) {
21748             return false;
21749         }
21750         return true;
21751     },
21752     // private
21753     ClientSideStrongPassword: function (pwd)
21754     {
21755         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21756     },
21757     // private
21758     ClientSideMediumPassword: function (pwd)
21759     {
21760         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21761     },
21762     // private
21763     ClientSideWeakPassword: function (pwd)
21764     {
21765         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21766     }
21767           
21768 })//<script type="text/javascript">
21769
21770 /*
21771  * Based  Ext JS Library 1.1.1
21772  * Copyright(c) 2006-2007, Ext JS, LLC.
21773  * LGPL
21774  *
21775  */
21776  
21777 /**
21778  * @class Roo.HtmlEditorCore
21779  * @extends Roo.Component
21780  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21781  *
21782  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21783  */
21784
21785 Roo.HtmlEditorCore = function(config){
21786     
21787     
21788     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21789     
21790     
21791     this.addEvents({
21792         /**
21793          * @event initialize
21794          * Fires when the editor is fully initialized (including the iframe)
21795          * @param {Roo.HtmlEditorCore} this
21796          */
21797         initialize: true,
21798         /**
21799          * @event activate
21800          * Fires when the editor is first receives the focus. Any insertion must wait
21801          * until after this event.
21802          * @param {Roo.HtmlEditorCore} this
21803          */
21804         activate: true,
21805          /**
21806          * @event beforesync
21807          * Fires before the textarea is updated with content from the editor iframe. Return false
21808          * to cancel the sync.
21809          * @param {Roo.HtmlEditorCore} this
21810          * @param {String} html
21811          */
21812         beforesync: true,
21813          /**
21814          * @event beforepush
21815          * Fires before the iframe editor is updated with content from the textarea. Return false
21816          * to cancel the push.
21817          * @param {Roo.HtmlEditorCore} this
21818          * @param {String} html
21819          */
21820         beforepush: true,
21821          /**
21822          * @event sync
21823          * Fires when the textarea is updated with content from the editor iframe.
21824          * @param {Roo.HtmlEditorCore} this
21825          * @param {String} html
21826          */
21827         sync: true,
21828          /**
21829          * @event push
21830          * Fires when the iframe editor is updated with content from the textarea.
21831          * @param {Roo.HtmlEditorCore} this
21832          * @param {String} html
21833          */
21834         push: true,
21835         
21836         /**
21837          * @event editorevent
21838          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21839          * @param {Roo.HtmlEditorCore} this
21840          */
21841         editorevent: true
21842         
21843     });
21844     
21845     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21846     
21847     // defaults : white / black...
21848     this.applyBlacklists();
21849     
21850     
21851     
21852 };
21853
21854
21855 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21856
21857
21858      /**
21859      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21860      */
21861     
21862     owner : false,
21863     
21864      /**
21865      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21866      *                        Roo.resizable.
21867      */
21868     resizable : false,
21869      /**
21870      * @cfg {Number} height (in pixels)
21871      */   
21872     height: 300,
21873    /**
21874      * @cfg {Number} width (in pixels)
21875      */   
21876     width: 500,
21877     
21878     /**
21879      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21880      * 
21881      */
21882     stylesheets: false,
21883     
21884     // id of frame..
21885     frameId: false,
21886     
21887     // private properties
21888     validationEvent : false,
21889     deferHeight: true,
21890     initialized : false,
21891     activated : false,
21892     sourceEditMode : false,
21893     onFocus : Roo.emptyFn,
21894     iframePad:3,
21895     hideMode:'offsets',
21896     
21897     clearUp: true,
21898     
21899     // blacklist + whitelisted elements..
21900     black: false,
21901     white: false,
21902      
21903     bodyCls : '',
21904
21905     /**
21906      * Protected method that will not generally be called directly. It
21907      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21908      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21909      */
21910     getDocMarkup : function(){
21911         // body styles..
21912         var st = '';
21913         
21914         // inherit styels from page...?? 
21915         if (this.stylesheets === false) {
21916             
21917             Roo.get(document.head).select('style').each(function(node) {
21918                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21919             });
21920             
21921             Roo.get(document.head).select('link').each(function(node) { 
21922                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21923             });
21924             
21925         } else if (!this.stylesheets.length) {
21926                 // simple..
21927                 st = '<style type="text/css">' +
21928                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21929                    '</style>';
21930         } else { 
21931             st = '<style type="text/css">' +
21932                     this.stylesheets +
21933                 '</style>';
21934         }
21935         
21936         st +=  '<style type="text/css">' +
21937             'IMG { cursor: pointer } ' +
21938         '</style>';
21939
21940         var cls = 'roo-htmleditor-body';
21941         
21942         if(this.bodyCls.length){
21943             cls += ' ' + this.bodyCls;
21944         }
21945         
21946         return '<html><head>' + st  +
21947             //<style type="text/css">' +
21948             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21949             //'</style>' +
21950             ' </head><body class="' +  cls + '"></body></html>';
21951     },
21952
21953     // private
21954     onRender : function(ct, position)
21955     {
21956         var _t = this;
21957         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21958         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21959         
21960         
21961         this.el.dom.style.border = '0 none';
21962         this.el.dom.setAttribute('tabIndex', -1);
21963         this.el.addClass('x-hidden hide');
21964         
21965         
21966         
21967         if(Roo.isIE){ // fix IE 1px bogus margin
21968             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21969         }
21970        
21971         
21972         this.frameId = Roo.id();
21973         
21974          
21975         
21976         var iframe = this.owner.wrap.createChild({
21977             tag: 'iframe',
21978             cls: 'form-control', // bootstrap..
21979             id: this.frameId,
21980             name: this.frameId,
21981             frameBorder : 'no',
21982             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21983         }, this.el
21984         );
21985         
21986         
21987         this.iframe = iframe.dom;
21988
21989          this.assignDocWin();
21990         
21991         this.doc.designMode = 'on';
21992        
21993         this.doc.open();
21994         this.doc.write(this.getDocMarkup());
21995         this.doc.close();
21996
21997         
21998         var task = { // must defer to wait for browser to be ready
21999             run : function(){
22000                 //console.log("run task?" + this.doc.readyState);
22001                 this.assignDocWin();
22002                 if(this.doc.body || this.doc.readyState == 'complete'){
22003                     try {
22004                         this.doc.designMode="on";
22005                     } catch (e) {
22006                         return;
22007                     }
22008                     Roo.TaskMgr.stop(task);
22009                     this.initEditor.defer(10, this);
22010                 }
22011             },
22012             interval : 10,
22013             duration: 10000,
22014             scope: this
22015         };
22016         Roo.TaskMgr.start(task);
22017
22018     },
22019
22020     // private
22021     onResize : function(w, h)
22022     {
22023          Roo.log('resize: ' +w + ',' + h );
22024         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22025         if(!this.iframe){
22026             return;
22027         }
22028         if(typeof w == 'number'){
22029             
22030             this.iframe.style.width = w + 'px';
22031         }
22032         if(typeof h == 'number'){
22033             
22034             this.iframe.style.height = h + 'px';
22035             if(this.doc){
22036                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22037             }
22038         }
22039         
22040     },
22041
22042     /**
22043      * Toggles the editor between standard and source edit mode.
22044      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22045      */
22046     toggleSourceEdit : function(sourceEditMode){
22047         
22048         this.sourceEditMode = sourceEditMode === true;
22049         
22050         if(this.sourceEditMode){
22051  
22052             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22053             
22054         }else{
22055             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22056             //this.iframe.className = '';
22057             this.deferFocus();
22058         }
22059         //this.setSize(this.owner.wrap.getSize());
22060         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22061     },
22062
22063     
22064   
22065
22066     /**
22067      * Protected method that will not generally be called directly. If you need/want
22068      * custom HTML cleanup, this is the method you should override.
22069      * @param {String} html The HTML to be cleaned
22070      * return {String} The cleaned HTML
22071      */
22072     cleanHtml : function(html){
22073         html = String(html);
22074         if(html.length > 5){
22075             if(Roo.isSafari){ // strip safari nonsense
22076                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22077             }
22078         }
22079         if(html == '&nbsp;'){
22080             html = '';
22081         }
22082         return html;
22083     },
22084
22085     /**
22086      * HTML Editor -> Textarea
22087      * Protected method that will not generally be called directly. Syncs the contents
22088      * of the editor iframe with the textarea.
22089      */
22090     syncValue : function(){
22091         if(this.initialized){
22092             var bd = (this.doc.body || this.doc.documentElement);
22093             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22094             var html = bd.innerHTML;
22095             if(Roo.isSafari){
22096                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22097                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22098                 if(m && m[1]){
22099                     html = '<div style="'+m[0]+'">' + html + '</div>';
22100                 }
22101             }
22102             html = this.cleanHtml(html);
22103             // fix up the special chars.. normaly like back quotes in word...
22104             // however we do not want to do this with chinese..
22105             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22106                 var cc = b.charCodeAt();
22107                 if (
22108                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22109                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22110                     (cc >= 0xf900 && cc < 0xfb00 )
22111                 ) {
22112                         return b;
22113                 }
22114                 return "&#"+cc+";" 
22115             });
22116             if(this.owner.fireEvent('beforesync', this, html) !== false){
22117                 this.el.dom.value = html;
22118                 this.owner.fireEvent('sync', this, html);
22119             }
22120         }
22121     },
22122
22123     /**
22124      * Protected method that will not generally be called directly. Pushes the value of the textarea
22125      * into the iframe editor.
22126      */
22127     pushValue : function(){
22128         if(this.initialized){
22129             var v = this.el.dom.value.trim();
22130             
22131 //            if(v.length < 1){
22132 //                v = '&#160;';
22133 //            }
22134             
22135             if(this.owner.fireEvent('beforepush', this, v) !== false){
22136                 var d = (this.doc.body || this.doc.documentElement);
22137                 d.innerHTML = v;
22138                 this.cleanUpPaste();
22139                 this.el.dom.value = d.innerHTML;
22140                 this.owner.fireEvent('push', this, v);
22141             }
22142         }
22143     },
22144
22145     // private
22146     deferFocus : function(){
22147         this.focus.defer(10, this);
22148     },
22149
22150     // doc'ed in Field
22151     focus : function(){
22152         if(this.win && !this.sourceEditMode){
22153             this.win.focus();
22154         }else{
22155             this.el.focus();
22156         }
22157     },
22158     
22159     assignDocWin: function()
22160     {
22161         var iframe = this.iframe;
22162         
22163          if(Roo.isIE){
22164             this.doc = iframe.contentWindow.document;
22165             this.win = iframe.contentWindow;
22166         } else {
22167 //            if (!Roo.get(this.frameId)) {
22168 //                return;
22169 //            }
22170 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22171 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22172             
22173             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22174                 return;
22175             }
22176             
22177             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22178             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22179         }
22180     },
22181     
22182     // private
22183     initEditor : function(){
22184         //console.log("INIT EDITOR");
22185         this.assignDocWin();
22186         
22187         
22188         
22189         this.doc.designMode="on";
22190         this.doc.open();
22191         this.doc.write(this.getDocMarkup());
22192         this.doc.close();
22193         
22194         var dbody = (this.doc.body || this.doc.documentElement);
22195         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22196         // this copies styles from the containing element into thsi one..
22197         // not sure why we need all of this..
22198         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22199         
22200         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22201         //ss['background-attachment'] = 'fixed'; // w3c
22202         dbody.bgProperties = 'fixed'; // ie
22203         //Roo.DomHelper.applyStyles(dbody, ss);
22204         Roo.EventManager.on(this.doc, {
22205             //'mousedown': this.onEditorEvent,
22206             'mouseup': this.onEditorEvent,
22207             'dblclick': this.onEditorEvent,
22208             'click': this.onEditorEvent,
22209             'keyup': this.onEditorEvent,
22210             buffer:100,
22211             scope: this
22212         });
22213         if(Roo.isGecko){
22214             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22215         }
22216         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22217             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22218         }
22219         this.initialized = true;
22220
22221         this.owner.fireEvent('initialize', this);
22222         this.pushValue();
22223     },
22224
22225     // private
22226     onDestroy : function(){
22227         
22228         
22229         
22230         if(this.rendered){
22231             
22232             //for (var i =0; i < this.toolbars.length;i++) {
22233             //    // fixme - ask toolbars for heights?
22234             //    this.toolbars[i].onDestroy();
22235            // }
22236             
22237             //this.wrap.dom.innerHTML = '';
22238             //this.wrap.remove();
22239         }
22240     },
22241
22242     // private
22243     onFirstFocus : function(){
22244         
22245         this.assignDocWin();
22246         
22247         
22248         this.activated = true;
22249          
22250     
22251         if(Roo.isGecko){ // prevent silly gecko errors
22252             this.win.focus();
22253             var s = this.win.getSelection();
22254             if(!s.focusNode || s.focusNode.nodeType != 3){
22255                 var r = s.getRangeAt(0);
22256                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22257                 r.collapse(true);
22258                 this.deferFocus();
22259             }
22260             try{
22261                 this.execCmd('useCSS', true);
22262                 this.execCmd('styleWithCSS', false);
22263             }catch(e){}
22264         }
22265         this.owner.fireEvent('activate', this);
22266     },
22267
22268     // private
22269     adjustFont: function(btn){
22270         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22271         //if(Roo.isSafari){ // safari
22272         //    adjust *= 2;
22273        // }
22274         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22275         if(Roo.isSafari){ // safari
22276             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22277             v =  (v < 10) ? 10 : v;
22278             v =  (v > 48) ? 48 : v;
22279             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22280             
22281         }
22282         
22283         
22284         v = Math.max(1, v+adjust);
22285         
22286         this.execCmd('FontSize', v  );
22287     },
22288
22289     onEditorEvent : function(e)
22290     {
22291         this.owner.fireEvent('editorevent', this, e);
22292       //  this.updateToolbar();
22293         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22294     },
22295
22296     insertTag : function(tg)
22297     {
22298         // could be a bit smarter... -> wrap the current selected tRoo..
22299         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22300             
22301             range = this.createRange(this.getSelection());
22302             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22303             wrappingNode.appendChild(range.extractContents());
22304             range.insertNode(wrappingNode);
22305
22306             return;
22307             
22308             
22309             
22310         }
22311         this.execCmd("formatblock",   tg);
22312         
22313     },
22314     
22315     insertText : function(txt)
22316     {
22317         
22318         
22319         var range = this.createRange();
22320         range.deleteContents();
22321                //alert(Sender.getAttribute('label'));
22322                
22323         range.insertNode(this.doc.createTextNode(txt));
22324     } ,
22325     
22326      
22327
22328     /**
22329      * Executes a Midas editor command on the editor document and performs necessary focus and
22330      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22331      * @param {String} cmd The Midas command
22332      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22333      */
22334     relayCmd : function(cmd, value){
22335         this.win.focus();
22336         this.execCmd(cmd, value);
22337         this.owner.fireEvent('editorevent', this);
22338         //this.updateToolbar();
22339         this.owner.deferFocus();
22340     },
22341
22342     /**
22343      * Executes a Midas editor command directly on the editor document.
22344      * For visual commands, you should use {@link #relayCmd} instead.
22345      * <b>This should only be called after the editor is initialized.</b>
22346      * @param {String} cmd The Midas command
22347      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22348      */
22349     execCmd : function(cmd, value){
22350         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22351         this.syncValue();
22352     },
22353  
22354  
22355    
22356     /**
22357      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22358      * to insert tRoo.
22359      * @param {String} text | dom node.. 
22360      */
22361     insertAtCursor : function(text)
22362     {
22363         
22364         if(!this.activated){
22365             return;
22366         }
22367         /*
22368         if(Roo.isIE){
22369             this.win.focus();
22370             var r = this.doc.selection.createRange();
22371             if(r){
22372                 r.collapse(true);
22373                 r.pasteHTML(text);
22374                 this.syncValue();
22375                 this.deferFocus();
22376             
22377             }
22378             return;
22379         }
22380         */
22381         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22382             this.win.focus();
22383             
22384             
22385             // from jquery ui (MIT licenced)
22386             var range, node;
22387             var win = this.win;
22388             
22389             if (win.getSelection && win.getSelection().getRangeAt) {
22390                 range = win.getSelection().getRangeAt(0);
22391                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22392                 range.insertNode(node);
22393             } else if (win.document.selection && win.document.selection.createRange) {
22394                 // no firefox support
22395                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22396                 win.document.selection.createRange().pasteHTML(txt);
22397             } else {
22398                 // no firefox support
22399                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22400                 this.execCmd('InsertHTML', txt);
22401             } 
22402             
22403             this.syncValue();
22404             
22405             this.deferFocus();
22406         }
22407     },
22408  // private
22409     mozKeyPress : function(e){
22410         if(e.ctrlKey){
22411             var c = e.getCharCode(), cmd;
22412           
22413             if(c > 0){
22414                 c = String.fromCharCode(c).toLowerCase();
22415                 switch(c){
22416                     case 'b':
22417                         cmd = 'bold';
22418                         break;
22419                     case 'i':
22420                         cmd = 'italic';
22421                         break;
22422                     
22423                     case 'u':
22424                         cmd = 'underline';
22425                         break;
22426                     
22427                     case 'v':
22428                         this.cleanUpPaste.defer(100, this);
22429                         return;
22430                         
22431                 }
22432                 if(cmd){
22433                     this.win.focus();
22434                     this.execCmd(cmd);
22435                     this.deferFocus();
22436                     e.preventDefault();
22437                 }
22438                 
22439             }
22440         }
22441     },
22442
22443     // private
22444     fixKeys : function(){ // load time branching for fastest keydown performance
22445         if(Roo.isIE){
22446             return function(e){
22447                 var k = e.getKey(), r;
22448                 if(k == e.TAB){
22449                     e.stopEvent();
22450                     r = this.doc.selection.createRange();
22451                     if(r){
22452                         r.collapse(true);
22453                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22454                         this.deferFocus();
22455                     }
22456                     return;
22457                 }
22458                 
22459                 if(k == e.ENTER){
22460                     r = this.doc.selection.createRange();
22461                     if(r){
22462                         var target = r.parentElement();
22463                         if(!target || target.tagName.toLowerCase() != 'li'){
22464                             e.stopEvent();
22465                             r.pasteHTML('<br />');
22466                             r.collapse(false);
22467                             r.select();
22468                         }
22469                     }
22470                 }
22471                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22472                     this.cleanUpPaste.defer(100, this);
22473                     return;
22474                 }
22475                 
22476                 
22477             };
22478         }else if(Roo.isOpera){
22479             return function(e){
22480                 var k = e.getKey();
22481                 if(k == e.TAB){
22482                     e.stopEvent();
22483                     this.win.focus();
22484                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22485                     this.deferFocus();
22486                 }
22487                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22488                     this.cleanUpPaste.defer(100, this);
22489                     return;
22490                 }
22491                 
22492             };
22493         }else if(Roo.isSafari){
22494             return function(e){
22495                 var k = e.getKey();
22496                 
22497                 if(k == e.TAB){
22498                     e.stopEvent();
22499                     this.execCmd('InsertText','\t');
22500                     this.deferFocus();
22501                     return;
22502                 }
22503                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22504                     this.cleanUpPaste.defer(100, this);
22505                     return;
22506                 }
22507                 
22508              };
22509         }
22510     }(),
22511     
22512     getAllAncestors: function()
22513     {
22514         var p = this.getSelectedNode();
22515         var a = [];
22516         if (!p) {
22517             a.push(p); // push blank onto stack..
22518             p = this.getParentElement();
22519         }
22520         
22521         
22522         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22523             a.push(p);
22524             p = p.parentNode;
22525         }
22526         a.push(this.doc.body);
22527         return a;
22528     },
22529     lastSel : false,
22530     lastSelNode : false,
22531     
22532     
22533     getSelection : function() 
22534     {
22535         this.assignDocWin();
22536         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22537     },
22538     
22539     getSelectedNode: function() 
22540     {
22541         // this may only work on Gecko!!!
22542         
22543         // should we cache this!!!!
22544         
22545         
22546         
22547          
22548         var range = this.createRange(this.getSelection()).cloneRange();
22549         
22550         if (Roo.isIE) {
22551             var parent = range.parentElement();
22552             while (true) {
22553                 var testRange = range.duplicate();
22554                 testRange.moveToElementText(parent);
22555                 if (testRange.inRange(range)) {
22556                     break;
22557                 }
22558                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22559                     break;
22560                 }
22561                 parent = parent.parentElement;
22562             }
22563             return parent;
22564         }
22565         
22566         // is ancestor a text element.
22567         var ac =  range.commonAncestorContainer;
22568         if (ac.nodeType == 3) {
22569             ac = ac.parentNode;
22570         }
22571         
22572         var ar = ac.childNodes;
22573          
22574         var nodes = [];
22575         var other_nodes = [];
22576         var has_other_nodes = false;
22577         for (var i=0;i<ar.length;i++) {
22578             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22579                 continue;
22580             }
22581             // fullly contained node.
22582             
22583             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22584                 nodes.push(ar[i]);
22585                 continue;
22586             }
22587             
22588             // probably selected..
22589             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22590                 other_nodes.push(ar[i]);
22591                 continue;
22592             }
22593             // outer..
22594             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22595                 continue;
22596             }
22597             
22598             
22599             has_other_nodes = true;
22600         }
22601         if (!nodes.length && other_nodes.length) {
22602             nodes= other_nodes;
22603         }
22604         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22605             return false;
22606         }
22607         
22608         return nodes[0];
22609     },
22610     createRange: function(sel)
22611     {
22612         // this has strange effects when using with 
22613         // top toolbar - not sure if it's a great idea.
22614         //this.editor.contentWindow.focus();
22615         if (typeof sel != "undefined") {
22616             try {
22617                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22618             } catch(e) {
22619                 return this.doc.createRange();
22620             }
22621         } else {
22622             return this.doc.createRange();
22623         }
22624     },
22625     getParentElement: function()
22626     {
22627         
22628         this.assignDocWin();
22629         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22630         
22631         var range = this.createRange(sel);
22632          
22633         try {
22634             var p = range.commonAncestorContainer;
22635             while (p.nodeType == 3) { // text node
22636                 p = p.parentNode;
22637             }
22638             return p;
22639         } catch (e) {
22640             return null;
22641         }
22642     
22643     },
22644     /***
22645      *
22646      * Range intersection.. the hard stuff...
22647      *  '-1' = before
22648      *  '0' = hits..
22649      *  '1' = after.
22650      *         [ -- selected range --- ]
22651      *   [fail]                        [fail]
22652      *
22653      *    basically..
22654      *      if end is before start or  hits it. fail.
22655      *      if start is after end or hits it fail.
22656      *
22657      *   if either hits (but other is outside. - then it's not 
22658      *   
22659      *    
22660      **/
22661     
22662     
22663     // @see http://www.thismuchiknow.co.uk/?p=64.
22664     rangeIntersectsNode : function(range, node)
22665     {
22666         var nodeRange = node.ownerDocument.createRange();
22667         try {
22668             nodeRange.selectNode(node);
22669         } catch (e) {
22670             nodeRange.selectNodeContents(node);
22671         }
22672     
22673         var rangeStartRange = range.cloneRange();
22674         rangeStartRange.collapse(true);
22675     
22676         var rangeEndRange = range.cloneRange();
22677         rangeEndRange.collapse(false);
22678     
22679         var nodeStartRange = nodeRange.cloneRange();
22680         nodeStartRange.collapse(true);
22681     
22682         var nodeEndRange = nodeRange.cloneRange();
22683         nodeEndRange.collapse(false);
22684     
22685         return rangeStartRange.compareBoundaryPoints(
22686                  Range.START_TO_START, nodeEndRange) == -1 &&
22687                rangeEndRange.compareBoundaryPoints(
22688                  Range.START_TO_START, nodeStartRange) == 1;
22689         
22690          
22691     },
22692     rangeCompareNode : function(range, node)
22693     {
22694         var nodeRange = node.ownerDocument.createRange();
22695         try {
22696             nodeRange.selectNode(node);
22697         } catch (e) {
22698             nodeRange.selectNodeContents(node);
22699         }
22700         
22701         
22702         range.collapse(true);
22703     
22704         nodeRange.collapse(true);
22705      
22706         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22707         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22708          
22709         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22710         
22711         var nodeIsBefore   =  ss == 1;
22712         var nodeIsAfter    = ee == -1;
22713         
22714         if (nodeIsBefore && nodeIsAfter) {
22715             return 0; // outer
22716         }
22717         if (!nodeIsBefore && nodeIsAfter) {
22718             return 1; //right trailed.
22719         }
22720         
22721         if (nodeIsBefore && !nodeIsAfter) {
22722             return 2;  // left trailed.
22723         }
22724         // fully contined.
22725         return 3;
22726     },
22727
22728     // private? - in a new class?
22729     cleanUpPaste :  function()
22730     {
22731         // cleans up the whole document..
22732         Roo.log('cleanuppaste');
22733         
22734         this.cleanUpChildren(this.doc.body);
22735         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22736         if (clean != this.doc.body.innerHTML) {
22737             this.doc.body.innerHTML = clean;
22738         }
22739         
22740     },
22741     
22742     cleanWordChars : function(input) {// change the chars to hex code
22743         var he = Roo.HtmlEditorCore;
22744         
22745         var output = input;
22746         Roo.each(he.swapCodes, function(sw) { 
22747             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22748             
22749             output = output.replace(swapper, sw[1]);
22750         });
22751         
22752         return output;
22753     },
22754     
22755     
22756     cleanUpChildren : function (n)
22757     {
22758         if (!n.childNodes.length) {
22759             return;
22760         }
22761         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22762            this.cleanUpChild(n.childNodes[i]);
22763         }
22764     },
22765     
22766     
22767         
22768     
22769     cleanUpChild : function (node)
22770     {
22771         var ed = this;
22772         //console.log(node);
22773         if (node.nodeName == "#text") {
22774             // clean up silly Windows -- stuff?
22775             return; 
22776         }
22777         if (node.nodeName == "#comment") {
22778             node.parentNode.removeChild(node);
22779             // clean up silly Windows -- stuff?
22780             return; 
22781         }
22782         var lcname = node.tagName.toLowerCase();
22783         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22784         // whitelist of tags..
22785         
22786         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22787             // remove node.
22788             node.parentNode.removeChild(node);
22789             return;
22790             
22791         }
22792         
22793         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22794         
22795         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22796         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22797         
22798         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22799         //    remove_keep_children = true;
22800         //}
22801         
22802         if (remove_keep_children) {
22803             this.cleanUpChildren(node);
22804             // inserts everything just before this node...
22805             while (node.childNodes.length) {
22806                 var cn = node.childNodes[0];
22807                 node.removeChild(cn);
22808                 node.parentNode.insertBefore(cn, node);
22809             }
22810             node.parentNode.removeChild(node);
22811             return;
22812         }
22813         
22814         if (!node.attributes || !node.attributes.length) {
22815             this.cleanUpChildren(node);
22816             return;
22817         }
22818         
22819         function cleanAttr(n,v)
22820         {
22821             
22822             if (v.match(/^\./) || v.match(/^\//)) {
22823                 return;
22824             }
22825             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22826                 return;
22827             }
22828             if (v.match(/^#/)) {
22829                 return;
22830             }
22831 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22832             node.removeAttribute(n);
22833             
22834         }
22835         
22836         var cwhite = this.cwhite;
22837         var cblack = this.cblack;
22838             
22839         function cleanStyle(n,v)
22840         {
22841             if (v.match(/expression/)) { //XSS?? should we even bother..
22842                 node.removeAttribute(n);
22843                 return;
22844             }
22845             
22846             var parts = v.split(/;/);
22847             var clean = [];
22848             
22849             Roo.each(parts, function(p) {
22850                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22851                 if (!p.length) {
22852                     return true;
22853                 }
22854                 var l = p.split(':').shift().replace(/\s+/g,'');
22855                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22856                 
22857                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22858 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22859                     //node.removeAttribute(n);
22860                     return true;
22861                 }
22862                 //Roo.log()
22863                 // only allow 'c whitelisted system attributes'
22864                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22865 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22866                     //node.removeAttribute(n);
22867                     return true;
22868                 }
22869                 
22870                 
22871                  
22872                 
22873                 clean.push(p);
22874                 return true;
22875             });
22876             if (clean.length) { 
22877                 node.setAttribute(n, clean.join(';'));
22878             } else {
22879                 node.removeAttribute(n);
22880             }
22881             
22882         }
22883         
22884         
22885         for (var i = node.attributes.length-1; i > -1 ; i--) {
22886             var a = node.attributes[i];
22887             //console.log(a);
22888             
22889             if (a.name.toLowerCase().substr(0,2)=='on')  {
22890                 node.removeAttribute(a.name);
22891                 continue;
22892             }
22893             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22894                 node.removeAttribute(a.name);
22895                 continue;
22896             }
22897             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22898                 cleanAttr(a.name,a.value); // fixme..
22899                 continue;
22900             }
22901             if (a.name == 'style') {
22902                 cleanStyle(a.name,a.value);
22903                 continue;
22904             }
22905             /// clean up MS crap..
22906             // tecnically this should be a list of valid class'es..
22907             
22908             
22909             if (a.name == 'class') {
22910                 if (a.value.match(/^Mso/)) {
22911                     node.className = '';
22912                 }
22913                 
22914                 if (a.value.match(/^body$/)) {
22915                     node.className = '';
22916                 }
22917                 continue;
22918             }
22919             
22920             // style cleanup!?
22921             // class cleanup?
22922             
22923         }
22924         
22925         
22926         this.cleanUpChildren(node);
22927         
22928         
22929     },
22930     
22931     /**
22932      * Clean up MS wordisms...
22933      */
22934     cleanWord : function(node)
22935     {
22936         
22937         
22938         if (!node) {
22939             this.cleanWord(this.doc.body);
22940             return;
22941         }
22942         if (node.nodeName == "#text") {
22943             // clean up silly Windows -- stuff?
22944             return; 
22945         }
22946         if (node.nodeName == "#comment") {
22947             node.parentNode.removeChild(node);
22948             // clean up silly Windows -- stuff?
22949             return; 
22950         }
22951         
22952         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22953             node.parentNode.removeChild(node);
22954             return;
22955         }
22956         
22957         // remove - but keep children..
22958         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22959             while (node.childNodes.length) {
22960                 var cn = node.childNodes[0];
22961                 node.removeChild(cn);
22962                 node.parentNode.insertBefore(cn, node);
22963             }
22964             node.parentNode.removeChild(node);
22965             this.iterateChildren(node, this.cleanWord);
22966             return;
22967         }
22968         // clean styles
22969         if (node.className.length) {
22970             
22971             var cn = node.className.split(/\W+/);
22972             var cna = [];
22973             Roo.each(cn, function(cls) {
22974                 if (cls.match(/Mso[a-zA-Z]+/)) {
22975                     return;
22976                 }
22977                 cna.push(cls);
22978             });
22979             node.className = cna.length ? cna.join(' ') : '';
22980             if (!cna.length) {
22981                 node.removeAttribute("class");
22982             }
22983         }
22984         
22985         if (node.hasAttribute("lang")) {
22986             node.removeAttribute("lang");
22987         }
22988         
22989         if (node.hasAttribute("style")) {
22990             
22991             var styles = node.getAttribute("style").split(";");
22992             var nstyle = [];
22993             Roo.each(styles, function(s) {
22994                 if (!s.match(/:/)) {
22995                     return;
22996                 }
22997                 var kv = s.split(":");
22998                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22999                     return;
23000                 }
23001                 // what ever is left... we allow.
23002                 nstyle.push(s);
23003             });
23004             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23005             if (!nstyle.length) {
23006                 node.removeAttribute('style');
23007             }
23008         }
23009         this.iterateChildren(node, this.cleanWord);
23010         
23011         
23012         
23013     },
23014     /**
23015      * iterateChildren of a Node, calling fn each time, using this as the scole..
23016      * @param {DomNode} node node to iterate children of.
23017      * @param {Function} fn method of this class to call on each item.
23018      */
23019     iterateChildren : function(node, fn)
23020     {
23021         if (!node.childNodes.length) {
23022                 return;
23023         }
23024         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23025            fn.call(this, node.childNodes[i])
23026         }
23027     },
23028     
23029     
23030     /**
23031      * cleanTableWidths.
23032      *
23033      * Quite often pasting from word etc.. results in tables with column and widths.
23034      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23035      *
23036      */
23037     cleanTableWidths : function(node)
23038     {
23039          
23040          
23041         if (!node) {
23042             this.cleanTableWidths(this.doc.body);
23043             return;
23044         }
23045         
23046         // ignore list...
23047         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23048             return; 
23049         }
23050         Roo.log(node.tagName);
23051         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23052             this.iterateChildren(node, this.cleanTableWidths);
23053             return;
23054         }
23055         if (node.hasAttribute('width')) {
23056             node.removeAttribute('width');
23057         }
23058         
23059          
23060         if (node.hasAttribute("style")) {
23061             // pretty basic...
23062             
23063             var styles = node.getAttribute("style").split(";");
23064             var nstyle = [];
23065             Roo.each(styles, function(s) {
23066                 if (!s.match(/:/)) {
23067                     return;
23068                 }
23069                 var kv = s.split(":");
23070                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23071                     return;
23072                 }
23073                 // what ever is left... we allow.
23074                 nstyle.push(s);
23075             });
23076             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23077             if (!nstyle.length) {
23078                 node.removeAttribute('style');
23079             }
23080         }
23081         
23082         this.iterateChildren(node, this.cleanTableWidths);
23083         
23084         
23085     },
23086     
23087     
23088     
23089     
23090     domToHTML : function(currentElement, depth, nopadtext) {
23091         
23092         depth = depth || 0;
23093         nopadtext = nopadtext || false;
23094     
23095         if (!currentElement) {
23096             return this.domToHTML(this.doc.body);
23097         }
23098         
23099         //Roo.log(currentElement);
23100         var j;
23101         var allText = false;
23102         var nodeName = currentElement.nodeName;
23103         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23104         
23105         if  (nodeName == '#text') {
23106             
23107             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23108         }
23109         
23110         
23111         var ret = '';
23112         if (nodeName != 'BODY') {
23113              
23114             var i = 0;
23115             // Prints the node tagName, such as <A>, <IMG>, etc
23116             if (tagName) {
23117                 var attr = [];
23118                 for(i = 0; i < currentElement.attributes.length;i++) {
23119                     // quoting?
23120                     var aname = currentElement.attributes.item(i).name;
23121                     if (!currentElement.attributes.item(i).value.length) {
23122                         continue;
23123                     }
23124                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23125                 }
23126                 
23127                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23128             } 
23129             else {
23130                 
23131                 // eack
23132             }
23133         } else {
23134             tagName = false;
23135         }
23136         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23137             return ret;
23138         }
23139         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23140             nopadtext = true;
23141         }
23142         
23143         
23144         // Traverse the tree
23145         i = 0;
23146         var currentElementChild = currentElement.childNodes.item(i);
23147         var allText = true;
23148         var innerHTML  = '';
23149         lastnode = '';
23150         while (currentElementChild) {
23151             // Formatting code (indent the tree so it looks nice on the screen)
23152             var nopad = nopadtext;
23153             if (lastnode == 'SPAN') {
23154                 nopad  = true;
23155             }
23156             // text
23157             if  (currentElementChild.nodeName == '#text') {
23158                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23159                 toadd = nopadtext ? toadd : toadd.trim();
23160                 if (!nopad && toadd.length > 80) {
23161                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23162                 }
23163                 innerHTML  += toadd;
23164                 
23165                 i++;
23166                 currentElementChild = currentElement.childNodes.item(i);
23167                 lastNode = '';
23168                 continue;
23169             }
23170             allText = false;
23171             
23172             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23173                 
23174             // Recursively traverse the tree structure of the child node
23175             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23176             lastnode = currentElementChild.nodeName;
23177             i++;
23178             currentElementChild=currentElement.childNodes.item(i);
23179         }
23180         
23181         ret += innerHTML;
23182         
23183         if (!allText) {
23184                 // The remaining code is mostly for formatting the tree
23185             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23186         }
23187         
23188         
23189         if (tagName) {
23190             ret+= "</"+tagName+">";
23191         }
23192         return ret;
23193         
23194     },
23195         
23196     applyBlacklists : function()
23197     {
23198         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23199         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23200         
23201         this.white = [];
23202         this.black = [];
23203         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23204             if (b.indexOf(tag) > -1) {
23205                 return;
23206             }
23207             this.white.push(tag);
23208             
23209         }, this);
23210         
23211         Roo.each(w, function(tag) {
23212             if (b.indexOf(tag) > -1) {
23213                 return;
23214             }
23215             if (this.white.indexOf(tag) > -1) {
23216                 return;
23217             }
23218             this.white.push(tag);
23219             
23220         }, this);
23221         
23222         
23223         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23224             if (w.indexOf(tag) > -1) {
23225                 return;
23226             }
23227             this.black.push(tag);
23228             
23229         }, this);
23230         
23231         Roo.each(b, function(tag) {
23232             if (w.indexOf(tag) > -1) {
23233                 return;
23234             }
23235             if (this.black.indexOf(tag) > -1) {
23236                 return;
23237             }
23238             this.black.push(tag);
23239             
23240         }, this);
23241         
23242         
23243         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23244         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23245         
23246         this.cwhite = [];
23247         this.cblack = [];
23248         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23249             if (b.indexOf(tag) > -1) {
23250                 return;
23251             }
23252             this.cwhite.push(tag);
23253             
23254         }, this);
23255         
23256         Roo.each(w, function(tag) {
23257             if (b.indexOf(tag) > -1) {
23258                 return;
23259             }
23260             if (this.cwhite.indexOf(tag) > -1) {
23261                 return;
23262             }
23263             this.cwhite.push(tag);
23264             
23265         }, this);
23266         
23267         
23268         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23269             if (w.indexOf(tag) > -1) {
23270                 return;
23271             }
23272             this.cblack.push(tag);
23273             
23274         }, this);
23275         
23276         Roo.each(b, function(tag) {
23277             if (w.indexOf(tag) > -1) {
23278                 return;
23279             }
23280             if (this.cblack.indexOf(tag) > -1) {
23281                 return;
23282             }
23283             this.cblack.push(tag);
23284             
23285         }, this);
23286     },
23287     
23288     setStylesheets : function(stylesheets)
23289     {
23290         if(typeof(stylesheets) == 'string'){
23291             Roo.get(this.iframe.contentDocument.head).createChild({
23292                 tag : 'link',
23293                 rel : 'stylesheet',
23294                 type : 'text/css',
23295                 href : stylesheets
23296             });
23297             
23298             return;
23299         }
23300         var _this = this;
23301      
23302         Roo.each(stylesheets, function(s) {
23303             if(!s.length){
23304                 return;
23305             }
23306             
23307             Roo.get(_this.iframe.contentDocument.head).createChild({
23308                 tag : 'link',
23309                 rel : 'stylesheet',
23310                 type : 'text/css',
23311                 href : s
23312             });
23313         });
23314
23315         
23316     },
23317     
23318     removeStylesheets : function()
23319     {
23320         var _this = this;
23321         
23322         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23323             s.remove();
23324         });
23325     },
23326     
23327     setStyle : function(style)
23328     {
23329         Roo.get(this.iframe.contentDocument.head).createChild({
23330             tag : 'style',
23331             type : 'text/css',
23332             html : style
23333         });
23334
23335         return;
23336     }
23337     
23338     // hide stuff that is not compatible
23339     /**
23340      * @event blur
23341      * @hide
23342      */
23343     /**
23344      * @event change
23345      * @hide
23346      */
23347     /**
23348      * @event focus
23349      * @hide
23350      */
23351     /**
23352      * @event specialkey
23353      * @hide
23354      */
23355     /**
23356      * @cfg {String} fieldClass @hide
23357      */
23358     /**
23359      * @cfg {String} focusClass @hide
23360      */
23361     /**
23362      * @cfg {String} autoCreate @hide
23363      */
23364     /**
23365      * @cfg {String} inputType @hide
23366      */
23367     /**
23368      * @cfg {String} invalidClass @hide
23369      */
23370     /**
23371      * @cfg {String} invalidText @hide
23372      */
23373     /**
23374      * @cfg {String} msgFx @hide
23375      */
23376     /**
23377      * @cfg {String} validateOnBlur @hide
23378      */
23379 });
23380
23381 Roo.HtmlEditorCore.white = [
23382         'area', 'br', 'img', 'input', 'hr', 'wbr',
23383         
23384        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23385        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23386        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23387        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23388        'table',   'ul',         'xmp', 
23389        
23390        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23391       'thead',   'tr', 
23392      
23393       'dir', 'menu', 'ol', 'ul', 'dl',
23394        
23395       'embed',  'object'
23396 ];
23397
23398
23399 Roo.HtmlEditorCore.black = [
23400     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23401         'applet', // 
23402         'base',   'basefont', 'bgsound', 'blink',  'body', 
23403         'frame',  'frameset', 'head',    'html',   'ilayer', 
23404         'iframe', 'layer',  'link',     'meta',    'object',   
23405         'script', 'style' ,'title',  'xml' // clean later..
23406 ];
23407 Roo.HtmlEditorCore.clean = [
23408     'script', 'style', 'title', 'xml'
23409 ];
23410 Roo.HtmlEditorCore.remove = [
23411     'font'
23412 ];
23413 // attributes..
23414
23415 Roo.HtmlEditorCore.ablack = [
23416     'on'
23417 ];
23418     
23419 Roo.HtmlEditorCore.aclean = [ 
23420     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23421 ];
23422
23423 // protocols..
23424 Roo.HtmlEditorCore.pwhite= [
23425         'http',  'https',  'mailto'
23426 ];
23427
23428 // white listed style attributes.
23429 Roo.HtmlEditorCore.cwhite= [
23430       //  'text-align', /// default is to allow most things..
23431       
23432          
23433 //        'font-size'//??
23434 ];
23435
23436 // black listed style attributes.
23437 Roo.HtmlEditorCore.cblack= [
23438       //  'font-size' -- this can be set by the project 
23439 ];
23440
23441
23442 Roo.HtmlEditorCore.swapCodes   =[ 
23443     [    8211, "--" ], 
23444     [    8212, "--" ], 
23445     [    8216,  "'" ],  
23446     [    8217, "'" ],  
23447     [    8220, '"' ],  
23448     [    8221, '"' ],  
23449     [    8226, "*" ],  
23450     [    8230, "..." ]
23451 ]; 
23452
23453     /*
23454  * - LGPL
23455  *
23456  * HtmlEditor
23457  * 
23458  */
23459
23460 /**
23461  * @class Roo.bootstrap.HtmlEditor
23462  * @extends Roo.bootstrap.TextArea
23463  * Bootstrap HtmlEditor class
23464
23465  * @constructor
23466  * Create a new HtmlEditor
23467  * @param {Object} config The config object
23468  */
23469
23470 Roo.bootstrap.HtmlEditor = function(config){
23471     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23472     if (!this.toolbars) {
23473         this.toolbars = [];
23474     }
23475     
23476     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23477     this.addEvents({
23478             /**
23479              * @event initialize
23480              * Fires when the editor is fully initialized (including the iframe)
23481              * @param {HtmlEditor} this
23482              */
23483             initialize: true,
23484             /**
23485              * @event activate
23486              * Fires when the editor is first receives the focus. Any insertion must wait
23487              * until after this event.
23488              * @param {HtmlEditor} this
23489              */
23490             activate: true,
23491              /**
23492              * @event beforesync
23493              * Fires before the textarea is updated with content from the editor iframe. Return false
23494              * to cancel the sync.
23495              * @param {HtmlEditor} this
23496              * @param {String} html
23497              */
23498             beforesync: true,
23499              /**
23500              * @event beforepush
23501              * Fires before the iframe editor is updated with content from the textarea. Return false
23502              * to cancel the push.
23503              * @param {HtmlEditor} this
23504              * @param {String} html
23505              */
23506             beforepush: true,
23507              /**
23508              * @event sync
23509              * Fires when the textarea is updated with content from the editor iframe.
23510              * @param {HtmlEditor} this
23511              * @param {String} html
23512              */
23513             sync: true,
23514              /**
23515              * @event push
23516              * Fires when the iframe editor is updated with content from the textarea.
23517              * @param {HtmlEditor} this
23518              * @param {String} html
23519              */
23520             push: true,
23521              /**
23522              * @event editmodechange
23523              * Fires when the editor switches edit modes
23524              * @param {HtmlEditor} this
23525              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23526              */
23527             editmodechange: true,
23528             /**
23529              * @event editorevent
23530              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23531              * @param {HtmlEditor} this
23532              */
23533             editorevent: true,
23534             /**
23535              * @event firstfocus
23536              * Fires when on first focus - needed by toolbars..
23537              * @param {HtmlEditor} this
23538              */
23539             firstfocus: true,
23540             /**
23541              * @event autosave
23542              * Auto save the htmlEditor value as a file into Events
23543              * @param {HtmlEditor} this
23544              */
23545             autosave: true,
23546             /**
23547              * @event savedpreview
23548              * preview the saved version of htmlEditor
23549              * @param {HtmlEditor} this
23550              */
23551             savedpreview: true
23552         });
23553 };
23554
23555
23556 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23557     
23558     
23559       /**
23560      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23561      */
23562     toolbars : false,
23563     
23564      /**
23565     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23566     */
23567     btns : [],
23568    
23569      /**
23570      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23571      *                        Roo.resizable.
23572      */
23573     resizable : false,
23574      /**
23575      * @cfg {Number} height (in pixels)
23576      */   
23577     height: 300,
23578    /**
23579      * @cfg {Number} width (in pixels)
23580      */   
23581     width: false,
23582     
23583     /**
23584      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23585      * 
23586      */
23587     stylesheets: false,
23588     
23589     // id of frame..
23590     frameId: false,
23591     
23592     // private properties
23593     validationEvent : false,
23594     deferHeight: true,
23595     initialized : false,
23596     activated : false,
23597     
23598     onFocus : Roo.emptyFn,
23599     iframePad:3,
23600     hideMode:'offsets',
23601     
23602     tbContainer : false,
23603     
23604     bodyCls : '',
23605     
23606     toolbarContainer :function() {
23607         return this.wrap.select('.x-html-editor-tb',true).first();
23608     },
23609
23610     /**
23611      * Protected method that will not generally be called directly. It
23612      * is called when the editor creates its toolbar. Override this method if you need to
23613      * add custom toolbar buttons.
23614      * @param {HtmlEditor} editor
23615      */
23616     createToolbar : function(){
23617         Roo.log('renewing');
23618         Roo.log("create toolbars");
23619         
23620         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23621         this.toolbars[0].render(this.toolbarContainer());
23622         
23623         return;
23624         
23625 //        if (!editor.toolbars || !editor.toolbars.length) {
23626 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23627 //        }
23628 //        
23629 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23630 //            editor.toolbars[i] = Roo.factory(
23631 //                    typeof(editor.toolbars[i]) == 'string' ?
23632 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23633 //                Roo.bootstrap.HtmlEditor);
23634 //            editor.toolbars[i].init(editor);
23635 //        }
23636     },
23637
23638      
23639     // private
23640     onRender : function(ct, position)
23641     {
23642        // Roo.log("Call onRender: " + this.xtype);
23643         var _t = this;
23644         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23645       
23646         this.wrap = this.inputEl().wrap({
23647             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23648         });
23649         
23650         this.editorcore.onRender(ct, position);
23651          
23652         if (this.resizable) {
23653             this.resizeEl = new Roo.Resizable(this.wrap, {
23654                 pinned : true,
23655                 wrap: true,
23656                 dynamic : true,
23657                 minHeight : this.height,
23658                 height: this.height,
23659                 handles : this.resizable,
23660                 width: this.width,
23661                 listeners : {
23662                     resize : function(r, w, h) {
23663                         _t.onResize(w,h); // -something
23664                     }
23665                 }
23666             });
23667             
23668         }
23669         this.createToolbar(this);
23670        
23671         
23672         if(!this.width && this.resizable){
23673             this.setSize(this.wrap.getSize());
23674         }
23675         if (this.resizeEl) {
23676             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23677             // should trigger onReize..
23678         }
23679         
23680     },
23681
23682     // private
23683     onResize : function(w, h)
23684     {
23685         Roo.log('resize: ' +w + ',' + h );
23686         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23687         var ew = false;
23688         var eh = false;
23689         
23690         if(this.inputEl() ){
23691             if(typeof w == 'number'){
23692                 var aw = w - this.wrap.getFrameWidth('lr');
23693                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23694                 ew = aw;
23695             }
23696             if(typeof h == 'number'){
23697                  var tbh = -11;  // fixme it needs to tool bar size!
23698                 for (var i =0; i < this.toolbars.length;i++) {
23699                     // fixme - ask toolbars for heights?
23700                     tbh += this.toolbars[i].el.getHeight();
23701                     //if (this.toolbars[i].footer) {
23702                     //    tbh += this.toolbars[i].footer.el.getHeight();
23703                     //}
23704                 }
23705               
23706                 
23707                 
23708                 
23709                 
23710                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23711                 ah -= 5; // knock a few pixes off for look..
23712                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23713                 var eh = ah;
23714             }
23715         }
23716         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23717         this.editorcore.onResize(ew,eh);
23718         
23719     },
23720
23721     /**
23722      * Toggles the editor between standard and source edit mode.
23723      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23724      */
23725     toggleSourceEdit : function(sourceEditMode)
23726     {
23727         this.editorcore.toggleSourceEdit(sourceEditMode);
23728         
23729         if(this.editorcore.sourceEditMode){
23730             Roo.log('editor - showing textarea');
23731             
23732 //            Roo.log('in');
23733 //            Roo.log(this.syncValue());
23734             this.syncValue();
23735             this.inputEl().removeClass(['hide', 'x-hidden']);
23736             this.inputEl().dom.removeAttribute('tabIndex');
23737             this.inputEl().focus();
23738         }else{
23739             Roo.log('editor - hiding textarea');
23740 //            Roo.log('out')
23741 //            Roo.log(this.pushValue()); 
23742             this.pushValue();
23743             
23744             this.inputEl().addClass(['hide', 'x-hidden']);
23745             this.inputEl().dom.setAttribute('tabIndex', -1);
23746             //this.deferFocus();
23747         }
23748          
23749         if(this.resizable){
23750             this.setSize(this.wrap.getSize());
23751         }
23752         
23753         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23754     },
23755  
23756     // private (for BoxComponent)
23757     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23758
23759     // private (for BoxComponent)
23760     getResizeEl : function(){
23761         return this.wrap;
23762     },
23763
23764     // private (for BoxComponent)
23765     getPositionEl : function(){
23766         return this.wrap;
23767     },
23768
23769     // private
23770     initEvents : function(){
23771         this.originalValue = this.getValue();
23772     },
23773
23774 //    /**
23775 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23776 //     * @method
23777 //     */
23778 //    markInvalid : Roo.emptyFn,
23779 //    /**
23780 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23781 //     * @method
23782 //     */
23783 //    clearInvalid : Roo.emptyFn,
23784
23785     setValue : function(v){
23786         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23787         this.editorcore.pushValue();
23788     },
23789
23790      
23791     // private
23792     deferFocus : function(){
23793         this.focus.defer(10, this);
23794     },
23795
23796     // doc'ed in Field
23797     focus : function(){
23798         this.editorcore.focus();
23799         
23800     },
23801       
23802
23803     // private
23804     onDestroy : function(){
23805         
23806         
23807         
23808         if(this.rendered){
23809             
23810             for (var i =0; i < this.toolbars.length;i++) {
23811                 // fixme - ask toolbars for heights?
23812                 this.toolbars[i].onDestroy();
23813             }
23814             
23815             this.wrap.dom.innerHTML = '';
23816             this.wrap.remove();
23817         }
23818     },
23819
23820     // private
23821     onFirstFocus : function(){
23822         //Roo.log("onFirstFocus");
23823         this.editorcore.onFirstFocus();
23824          for (var i =0; i < this.toolbars.length;i++) {
23825             this.toolbars[i].onFirstFocus();
23826         }
23827         
23828     },
23829     
23830     // private
23831     syncValue : function()
23832     {   
23833         this.editorcore.syncValue();
23834     },
23835     
23836     pushValue : function()
23837     {   
23838         this.editorcore.pushValue();
23839     }
23840      
23841     
23842     // hide stuff that is not compatible
23843     /**
23844      * @event blur
23845      * @hide
23846      */
23847     /**
23848      * @event change
23849      * @hide
23850      */
23851     /**
23852      * @event focus
23853      * @hide
23854      */
23855     /**
23856      * @event specialkey
23857      * @hide
23858      */
23859     /**
23860      * @cfg {String} fieldClass @hide
23861      */
23862     /**
23863      * @cfg {String} focusClass @hide
23864      */
23865     /**
23866      * @cfg {String} autoCreate @hide
23867      */
23868     /**
23869      * @cfg {String} inputType @hide
23870      */
23871     /**
23872      * @cfg {String} invalidClass @hide
23873      */
23874     /**
23875      * @cfg {String} invalidText @hide
23876      */
23877     /**
23878      * @cfg {String} msgFx @hide
23879      */
23880     /**
23881      * @cfg {String} validateOnBlur @hide
23882      */
23883 });
23884  
23885     
23886    
23887    
23888    
23889       
23890 Roo.namespace('Roo.bootstrap.htmleditor');
23891 /**
23892  * @class Roo.bootstrap.HtmlEditorToolbar1
23893  * Basic Toolbar
23894  * 
23895  * Usage:
23896  *
23897  new Roo.bootstrap.HtmlEditor({
23898     ....
23899     toolbars : [
23900         new Roo.bootstrap.HtmlEditorToolbar1({
23901             disable : { fonts: 1 , format: 1, ..., ... , ...],
23902             btns : [ .... ]
23903         })
23904     }
23905      
23906  * 
23907  * @cfg {Object} disable List of elements to disable..
23908  * @cfg {Array} btns List of additional buttons.
23909  * 
23910  * 
23911  * NEEDS Extra CSS? 
23912  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23913  */
23914  
23915 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23916 {
23917     
23918     Roo.apply(this, config);
23919     
23920     // default disabled, based on 'good practice'..
23921     this.disable = this.disable || {};
23922     Roo.applyIf(this.disable, {
23923         fontSize : true,
23924         colors : true,
23925         specialElements : true
23926     });
23927     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23928     
23929     this.editor = config.editor;
23930     this.editorcore = config.editor.editorcore;
23931     
23932     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23933     
23934     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23935     // dont call parent... till later.
23936 }
23937 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23938      
23939     bar : true,
23940     
23941     editor : false,
23942     editorcore : false,
23943     
23944     
23945     formats : [
23946         "p" ,  
23947         "h1","h2","h3","h4","h5","h6", 
23948         "pre", "code", 
23949         "abbr", "acronym", "address", "cite", "samp", "var",
23950         'div','span'
23951     ],
23952     
23953     onRender : function(ct, position)
23954     {
23955        // Roo.log("Call onRender: " + this.xtype);
23956         
23957        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23958        Roo.log(this.el);
23959        this.el.dom.style.marginBottom = '0';
23960        var _this = this;
23961        var editorcore = this.editorcore;
23962        var editor= this.editor;
23963        
23964        var children = [];
23965        var btn = function(id,cmd , toggle, handler, html){
23966        
23967             var  event = toggle ? 'toggle' : 'click';
23968        
23969             var a = {
23970                 size : 'sm',
23971                 xtype: 'Button',
23972                 xns: Roo.bootstrap,
23973                 //glyphicon : id,
23974                 fa: id,
23975                 cmd : id || cmd,
23976                 enableToggle:toggle !== false,
23977                 html : html || '',
23978                 pressed : toggle ? false : null,
23979                 listeners : {}
23980             };
23981             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23982                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23983             };
23984             children.push(a);
23985             return a;
23986        }
23987        
23988     //    var cb_box = function...
23989         
23990         var style = {
23991                 xtype: 'Button',
23992                 size : 'sm',
23993                 xns: Roo.bootstrap,
23994                 fa : 'font',
23995                 //html : 'submit'
23996                 menu : {
23997                     xtype: 'Menu',
23998                     xns: Roo.bootstrap,
23999                     items:  []
24000                 }
24001         };
24002         Roo.each(this.formats, function(f) {
24003             style.menu.items.push({
24004                 xtype :'MenuItem',
24005                 xns: Roo.bootstrap,
24006                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24007                 tagname : f,
24008                 listeners : {
24009                     click : function()
24010                     {
24011                         editorcore.insertTag(this.tagname);
24012                         editor.focus();
24013                     }
24014                 }
24015                 
24016             });
24017         });
24018         children.push(style);   
24019         
24020         btn('bold',false,true);
24021         btn('italic',false,true);
24022         btn('align-left', 'justifyleft',true);
24023         btn('align-center', 'justifycenter',true);
24024         btn('align-right' , 'justifyright',true);
24025         btn('link', false, false, function(btn) {
24026             //Roo.log("create link?");
24027             var url = prompt(this.createLinkText, this.defaultLinkValue);
24028             if(url && url != 'http:/'+'/'){
24029                 this.editorcore.relayCmd('createlink', url);
24030             }
24031         }),
24032         btn('list','insertunorderedlist',true);
24033         btn('pencil', false,true, function(btn){
24034                 Roo.log(this);
24035                 this.toggleSourceEdit(btn.pressed);
24036         });
24037         
24038         if (this.editor.btns.length > 0) {
24039             for (var i = 0; i<this.editor.btns.length; i++) {
24040                 children.push(this.editor.btns[i]);
24041             }
24042         }
24043         
24044         /*
24045         var cog = {
24046                 xtype: 'Button',
24047                 size : 'sm',
24048                 xns: Roo.bootstrap,
24049                 glyphicon : 'cog',
24050                 //html : 'submit'
24051                 menu : {
24052                     xtype: 'Menu',
24053                     xns: Roo.bootstrap,
24054                     items:  []
24055                 }
24056         };
24057         
24058         cog.menu.items.push({
24059             xtype :'MenuItem',
24060             xns: Roo.bootstrap,
24061             html : Clean styles,
24062             tagname : f,
24063             listeners : {
24064                 click : function()
24065                 {
24066                     editorcore.insertTag(this.tagname);
24067                     editor.focus();
24068                 }
24069             }
24070             
24071         });
24072        */
24073         
24074          
24075        this.xtype = 'NavSimplebar';
24076         
24077         for(var i=0;i< children.length;i++) {
24078             
24079             this.buttons.add(this.addxtypeChild(children[i]));
24080             
24081         }
24082         
24083         editor.on('editorevent', this.updateToolbar, this);
24084     },
24085     onBtnClick : function(id)
24086     {
24087        this.editorcore.relayCmd(id);
24088        this.editorcore.focus();
24089     },
24090     
24091     /**
24092      * Protected method that will not generally be called directly. It triggers
24093      * a toolbar update by reading the markup state of the current selection in the editor.
24094      */
24095     updateToolbar: function(){
24096
24097         if(!this.editorcore.activated){
24098             this.editor.onFirstFocus(); // is this neeed?
24099             return;
24100         }
24101
24102         var btns = this.buttons; 
24103         var doc = this.editorcore.doc;
24104         btns.get('bold').setActive(doc.queryCommandState('bold'));
24105         btns.get('italic').setActive(doc.queryCommandState('italic'));
24106         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24107         
24108         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24109         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24110         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24111         
24112         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24113         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24114          /*
24115         
24116         var ans = this.editorcore.getAllAncestors();
24117         if (this.formatCombo) {
24118             
24119             
24120             var store = this.formatCombo.store;
24121             this.formatCombo.setValue("");
24122             for (var i =0; i < ans.length;i++) {
24123                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24124                     // select it..
24125                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24126                     break;
24127                 }
24128             }
24129         }
24130         
24131         
24132         
24133         // hides menus... - so this cant be on a menu...
24134         Roo.bootstrap.MenuMgr.hideAll();
24135         */
24136         Roo.bootstrap.MenuMgr.hideAll();
24137         //this.editorsyncValue();
24138     },
24139     onFirstFocus: function() {
24140         this.buttons.each(function(item){
24141            item.enable();
24142         });
24143     },
24144     toggleSourceEdit : function(sourceEditMode){
24145         
24146           
24147         if(sourceEditMode){
24148             Roo.log("disabling buttons");
24149            this.buttons.each( function(item){
24150                 if(item.cmd != 'pencil'){
24151                     item.disable();
24152                 }
24153             });
24154           
24155         }else{
24156             Roo.log("enabling buttons");
24157             if(this.editorcore.initialized){
24158                 this.buttons.each( function(item){
24159                     item.enable();
24160                 });
24161             }
24162             
24163         }
24164         Roo.log("calling toggole on editor");
24165         // tell the editor that it's been pressed..
24166         this.editor.toggleSourceEdit(sourceEditMode);
24167        
24168     }
24169 });
24170
24171
24172
24173
24174
24175 /**
24176  * @class Roo.bootstrap.Table.AbstractSelectionModel
24177  * @extends Roo.util.Observable
24178  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24179  * implemented by descendant classes.  This class should not be directly instantiated.
24180  * @constructor
24181  */
24182 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24183     this.locked = false;
24184     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24185 };
24186
24187
24188 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24189     /** @ignore Called by the grid automatically. Do not call directly. */
24190     init : function(grid){
24191         this.grid = grid;
24192         this.initEvents();
24193     },
24194
24195     /**
24196      * Locks the selections.
24197      */
24198     lock : function(){
24199         this.locked = true;
24200     },
24201
24202     /**
24203      * Unlocks the selections.
24204      */
24205     unlock : function(){
24206         this.locked = false;
24207     },
24208
24209     /**
24210      * Returns true if the selections are locked.
24211      * @return {Boolean}
24212      */
24213     isLocked : function(){
24214         return this.locked;
24215     }
24216 });
24217 /**
24218  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24219  * @class Roo.bootstrap.Table.RowSelectionModel
24220  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24221  * It supports multiple selections and keyboard selection/navigation. 
24222  * @constructor
24223  * @param {Object} config
24224  */
24225
24226 Roo.bootstrap.Table.RowSelectionModel = function(config){
24227     Roo.apply(this, config);
24228     this.selections = new Roo.util.MixedCollection(false, function(o){
24229         return o.id;
24230     });
24231
24232     this.last = false;
24233     this.lastActive = false;
24234
24235     this.addEvents({
24236         /**
24237              * @event selectionchange
24238              * Fires when the selection changes
24239              * @param {SelectionModel} this
24240              */
24241             "selectionchange" : true,
24242         /**
24243              * @event afterselectionchange
24244              * Fires after the selection changes (eg. by key press or clicking)
24245              * @param {SelectionModel} this
24246              */
24247             "afterselectionchange" : true,
24248         /**
24249              * @event beforerowselect
24250              * Fires when a row is selected being selected, return false to cancel.
24251              * @param {SelectionModel} this
24252              * @param {Number} rowIndex The selected index
24253              * @param {Boolean} keepExisting False if other selections will be cleared
24254              */
24255             "beforerowselect" : true,
24256         /**
24257              * @event rowselect
24258              * Fires when a row is selected.
24259              * @param {SelectionModel} this
24260              * @param {Number} rowIndex The selected index
24261              * @param {Roo.data.Record} r The record
24262              */
24263             "rowselect" : true,
24264         /**
24265              * @event rowdeselect
24266              * Fires when a row is deselected.
24267              * @param {SelectionModel} this
24268              * @param {Number} rowIndex The selected index
24269              */
24270         "rowdeselect" : true
24271     });
24272     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24273     this.locked = false;
24274  };
24275
24276 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24277     /**
24278      * @cfg {Boolean} singleSelect
24279      * True to allow selection of only one row at a time (defaults to false)
24280      */
24281     singleSelect : false,
24282
24283     // private
24284     initEvents : function()
24285     {
24286
24287         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24288         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24289         //}else{ // allow click to work like normal
24290          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24291         //}
24292         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24293         this.grid.on("rowclick", this.handleMouseDown, this);
24294         
24295         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24296             "up" : function(e){
24297                 if(!e.shiftKey){
24298                     this.selectPrevious(e.shiftKey);
24299                 }else if(this.last !== false && this.lastActive !== false){
24300                     var last = this.last;
24301                     this.selectRange(this.last,  this.lastActive-1);
24302                     this.grid.getView().focusRow(this.lastActive);
24303                     if(last !== false){
24304                         this.last = last;
24305                     }
24306                 }else{
24307                     this.selectFirstRow();
24308                 }
24309                 this.fireEvent("afterselectionchange", this);
24310             },
24311             "down" : function(e){
24312                 if(!e.shiftKey){
24313                     this.selectNext(e.shiftKey);
24314                 }else if(this.last !== false && this.lastActive !== false){
24315                     var last = this.last;
24316                     this.selectRange(this.last,  this.lastActive+1);
24317                     this.grid.getView().focusRow(this.lastActive);
24318                     if(last !== false){
24319                         this.last = last;
24320                     }
24321                 }else{
24322                     this.selectFirstRow();
24323                 }
24324                 this.fireEvent("afterselectionchange", this);
24325             },
24326             scope: this
24327         });
24328         this.grid.store.on('load', function(){
24329             this.selections.clear();
24330         },this);
24331         /*
24332         var view = this.grid.view;
24333         view.on("refresh", this.onRefresh, this);
24334         view.on("rowupdated", this.onRowUpdated, this);
24335         view.on("rowremoved", this.onRemove, this);
24336         */
24337     },
24338
24339     // private
24340     onRefresh : function()
24341     {
24342         var ds = this.grid.store, i, v = this.grid.view;
24343         var s = this.selections;
24344         s.each(function(r){
24345             if((i = ds.indexOfId(r.id)) != -1){
24346                 v.onRowSelect(i);
24347             }else{
24348                 s.remove(r);
24349             }
24350         });
24351     },
24352
24353     // private
24354     onRemove : function(v, index, r){
24355         this.selections.remove(r);
24356     },
24357
24358     // private
24359     onRowUpdated : function(v, index, r){
24360         if(this.isSelected(r)){
24361             v.onRowSelect(index);
24362         }
24363     },
24364
24365     /**
24366      * Select records.
24367      * @param {Array} records The records to select
24368      * @param {Boolean} keepExisting (optional) True to keep existing selections
24369      */
24370     selectRecords : function(records, keepExisting)
24371     {
24372         if(!keepExisting){
24373             this.clearSelections();
24374         }
24375             var ds = this.grid.store;
24376         for(var i = 0, len = records.length; i < len; i++){
24377             this.selectRow(ds.indexOf(records[i]), true);
24378         }
24379     },
24380
24381     /**
24382      * Gets the number of selected rows.
24383      * @return {Number}
24384      */
24385     getCount : function(){
24386         return this.selections.length;
24387     },
24388
24389     /**
24390      * Selects the first row in the grid.
24391      */
24392     selectFirstRow : function(){
24393         this.selectRow(0);
24394     },
24395
24396     /**
24397      * Select the last row.
24398      * @param {Boolean} keepExisting (optional) True to keep existing selections
24399      */
24400     selectLastRow : function(keepExisting){
24401         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24402         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24403     },
24404
24405     /**
24406      * Selects the row immediately following the last selected row.
24407      * @param {Boolean} keepExisting (optional) True to keep existing selections
24408      */
24409     selectNext : function(keepExisting)
24410     {
24411             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24412             this.selectRow(this.last+1, keepExisting);
24413             this.grid.getView().focusRow(this.last);
24414         }
24415     },
24416
24417     /**
24418      * Selects the row that precedes the last selected row.
24419      * @param {Boolean} keepExisting (optional) True to keep existing selections
24420      */
24421     selectPrevious : function(keepExisting){
24422         if(this.last){
24423             this.selectRow(this.last-1, keepExisting);
24424             this.grid.getView().focusRow(this.last);
24425         }
24426     },
24427
24428     /**
24429      * Returns the selected records
24430      * @return {Array} Array of selected records
24431      */
24432     getSelections : function(){
24433         return [].concat(this.selections.items);
24434     },
24435
24436     /**
24437      * Returns the first selected record.
24438      * @return {Record}
24439      */
24440     getSelected : function(){
24441         return this.selections.itemAt(0);
24442     },
24443
24444
24445     /**
24446      * Clears all selections.
24447      */
24448     clearSelections : function(fast)
24449     {
24450         if(this.locked) {
24451             return;
24452         }
24453         if(fast !== true){
24454                 var ds = this.grid.store;
24455             var s = this.selections;
24456             s.each(function(r){
24457                 this.deselectRow(ds.indexOfId(r.id));
24458             }, this);
24459             s.clear();
24460         }else{
24461             this.selections.clear();
24462         }
24463         this.last = false;
24464     },
24465
24466
24467     /**
24468      * Selects all rows.
24469      */
24470     selectAll : function(){
24471         if(this.locked) {
24472             return;
24473         }
24474         this.selections.clear();
24475         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24476             this.selectRow(i, true);
24477         }
24478     },
24479
24480     /**
24481      * Returns True if there is a selection.
24482      * @return {Boolean}
24483      */
24484     hasSelection : function(){
24485         return this.selections.length > 0;
24486     },
24487
24488     /**
24489      * Returns True if the specified row is selected.
24490      * @param {Number/Record} record The record or index of the record to check
24491      * @return {Boolean}
24492      */
24493     isSelected : function(index){
24494             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24495         return (r && this.selections.key(r.id) ? true : false);
24496     },
24497
24498     /**
24499      * Returns True if the specified record id is selected.
24500      * @param {String} id The id of record to check
24501      * @return {Boolean}
24502      */
24503     isIdSelected : function(id){
24504         return (this.selections.key(id) ? true : false);
24505     },
24506
24507
24508     // private
24509     handleMouseDBClick : function(e, t){
24510         
24511     },
24512     // private
24513     handleMouseDown : function(e, t)
24514     {
24515             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24516         if(this.isLocked() || rowIndex < 0 ){
24517             return;
24518         };
24519         if(e.shiftKey && this.last !== false){
24520             var last = this.last;
24521             this.selectRange(last, rowIndex, e.ctrlKey);
24522             this.last = last; // reset the last
24523             t.focus();
24524     
24525         }else{
24526             var isSelected = this.isSelected(rowIndex);
24527             //Roo.log("select row:" + rowIndex);
24528             if(isSelected){
24529                 this.deselectRow(rowIndex);
24530             } else {
24531                         this.selectRow(rowIndex, true);
24532             }
24533     
24534             /*
24535                 if(e.button !== 0 && isSelected){
24536                 alert('rowIndex 2: ' + rowIndex);
24537                     view.focusRow(rowIndex);
24538                 }else if(e.ctrlKey && isSelected){
24539                     this.deselectRow(rowIndex);
24540                 }else if(!isSelected){
24541                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24542                     view.focusRow(rowIndex);
24543                 }
24544             */
24545         }
24546         this.fireEvent("afterselectionchange", this);
24547     },
24548     // private
24549     handleDragableRowClick :  function(grid, rowIndex, e) 
24550     {
24551         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24552             this.selectRow(rowIndex, false);
24553             grid.view.focusRow(rowIndex);
24554              this.fireEvent("afterselectionchange", this);
24555         }
24556     },
24557     
24558     /**
24559      * Selects multiple rows.
24560      * @param {Array} rows Array of the indexes of the row to select
24561      * @param {Boolean} keepExisting (optional) True to keep existing selections
24562      */
24563     selectRows : function(rows, keepExisting){
24564         if(!keepExisting){
24565             this.clearSelections();
24566         }
24567         for(var i = 0, len = rows.length; i < len; i++){
24568             this.selectRow(rows[i], true);
24569         }
24570     },
24571
24572     /**
24573      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24574      * @param {Number} startRow The index of the first row in the range
24575      * @param {Number} endRow The index of the last row in the range
24576      * @param {Boolean} keepExisting (optional) True to retain existing selections
24577      */
24578     selectRange : function(startRow, endRow, keepExisting){
24579         if(this.locked) {
24580             return;
24581         }
24582         if(!keepExisting){
24583             this.clearSelections();
24584         }
24585         if(startRow <= endRow){
24586             for(var i = startRow; i <= endRow; i++){
24587                 this.selectRow(i, true);
24588             }
24589         }else{
24590             for(var i = startRow; i >= endRow; i--){
24591                 this.selectRow(i, true);
24592             }
24593         }
24594     },
24595
24596     /**
24597      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24598      * @param {Number} startRow The index of the first row in the range
24599      * @param {Number} endRow The index of the last row in the range
24600      */
24601     deselectRange : function(startRow, endRow, preventViewNotify){
24602         if(this.locked) {
24603             return;
24604         }
24605         for(var i = startRow; i <= endRow; i++){
24606             this.deselectRow(i, preventViewNotify);
24607         }
24608     },
24609
24610     /**
24611      * Selects a row.
24612      * @param {Number} row The index of the row to select
24613      * @param {Boolean} keepExisting (optional) True to keep existing selections
24614      */
24615     selectRow : function(index, keepExisting, preventViewNotify)
24616     {
24617             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24618             return;
24619         }
24620         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24621             if(!keepExisting || this.singleSelect){
24622                 this.clearSelections();
24623             }
24624             
24625             var r = this.grid.store.getAt(index);
24626             //console.log('selectRow - record id :' + r.id);
24627             
24628             this.selections.add(r);
24629             this.last = this.lastActive = index;
24630             if(!preventViewNotify){
24631                 var proxy = new Roo.Element(
24632                                 this.grid.getRowDom(index)
24633                 );
24634                 proxy.addClass('bg-info info');
24635             }
24636             this.fireEvent("rowselect", this, index, r);
24637             this.fireEvent("selectionchange", this);
24638         }
24639     },
24640
24641     /**
24642      * Deselects a row.
24643      * @param {Number} row The index of the row to deselect
24644      */
24645     deselectRow : function(index, preventViewNotify)
24646     {
24647         if(this.locked) {
24648             return;
24649         }
24650         if(this.last == index){
24651             this.last = false;
24652         }
24653         if(this.lastActive == index){
24654             this.lastActive = false;
24655         }
24656         
24657         var r = this.grid.store.getAt(index);
24658         if (!r) {
24659             return;
24660         }
24661         
24662         this.selections.remove(r);
24663         //.console.log('deselectRow - record id :' + r.id);
24664         if(!preventViewNotify){
24665         
24666             var proxy = new Roo.Element(
24667                 this.grid.getRowDom(index)
24668             );
24669             proxy.removeClass('bg-info info');
24670         }
24671         this.fireEvent("rowdeselect", this, index);
24672         this.fireEvent("selectionchange", this);
24673     },
24674
24675     // private
24676     restoreLast : function(){
24677         if(this._last){
24678             this.last = this._last;
24679         }
24680     },
24681
24682     // private
24683     acceptsNav : function(row, col, cm){
24684         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24685     },
24686
24687     // private
24688     onEditorKey : function(field, e){
24689         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24690         if(k == e.TAB){
24691             e.stopEvent();
24692             ed.completeEdit();
24693             if(e.shiftKey){
24694                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24695             }else{
24696                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24697             }
24698         }else if(k == e.ENTER && !e.ctrlKey){
24699             e.stopEvent();
24700             ed.completeEdit();
24701             if(e.shiftKey){
24702                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24703             }else{
24704                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24705             }
24706         }else if(k == e.ESC){
24707             ed.cancelEdit();
24708         }
24709         if(newCell){
24710             g.startEditing(newCell[0], newCell[1]);
24711         }
24712     }
24713 });
24714 /*
24715  * Based on:
24716  * Ext JS Library 1.1.1
24717  * Copyright(c) 2006-2007, Ext JS, LLC.
24718  *
24719  * Originally Released Under LGPL - original licence link has changed is not relivant.
24720  *
24721  * Fork - LGPL
24722  * <script type="text/javascript">
24723  */
24724  
24725 /**
24726  * @class Roo.bootstrap.PagingToolbar
24727  * @extends Roo.bootstrap.NavSimplebar
24728  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24729  * @constructor
24730  * Create a new PagingToolbar
24731  * @param {Object} config The config object
24732  * @param {Roo.data.Store} store
24733  */
24734 Roo.bootstrap.PagingToolbar = function(config)
24735 {
24736     // old args format still supported... - xtype is prefered..
24737         // created from xtype...
24738     
24739     this.ds = config.dataSource;
24740     
24741     if (config.store && !this.ds) {
24742         this.store= Roo.factory(config.store, Roo.data);
24743         this.ds = this.store;
24744         this.ds.xmodule = this.xmodule || false;
24745     }
24746     
24747     this.toolbarItems = [];
24748     if (config.items) {
24749         this.toolbarItems = config.items;
24750     }
24751     
24752     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24753     
24754     this.cursor = 0;
24755     
24756     if (this.ds) { 
24757         this.bind(this.ds);
24758     }
24759     
24760     if (Roo.bootstrap.version == 4) {
24761         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24762     } else {
24763         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24764     }
24765     
24766 };
24767
24768 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24769     /**
24770      * @cfg {Roo.data.Store} dataSource
24771      * The underlying data store providing the paged data
24772      */
24773     /**
24774      * @cfg {String/HTMLElement/Element} container
24775      * container The id or element that will contain the toolbar
24776      */
24777     /**
24778      * @cfg {Boolean} displayInfo
24779      * True to display the displayMsg (defaults to false)
24780      */
24781     /**
24782      * @cfg {Number} pageSize
24783      * The number of records to display per page (defaults to 20)
24784      */
24785     pageSize: 20,
24786     /**
24787      * @cfg {String} displayMsg
24788      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24789      */
24790     displayMsg : 'Displaying {0} - {1} of {2}',
24791     /**
24792      * @cfg {String} emptyMsg
24793      * The message to display when no records are found (defaults to "No data to display")
24794      */
24795     emptyMsg : 'No data to display',
24796     /**
24797      * Customizable piece of the default paging text (defaults to "Page")
24798      * @type String
24799      */
24800     beforePageText : "Page",
24801     /**
24802      * Customizable piece of the default paging text (defaults to "of %0")
24803      * @type String
24804      */
24805     afterPageText : "of {0}",
24806     /**
24807      * Customizable piece of the default paging text (defaults to "First Page")
24808      * @type String
24809      */
24810     firstText : "First Page",
24811     /**
24812      * Customizable piece of the default paging text (defaults to "Previous Page")
24813      * @type String
24814      */
24815     prevText : "Previous Page",
24816     /**
24817      * Customizable piece of the default paging text (defaults to "Next Page")
24818      * @type String
24819      */
24820     nextText : "Next Page",
24821     /**
24822      * Customizable piece of the default paging text (defaults to "Last Page")
24823      * @type String
24824      */
24825     lastText : "Last Page",
24826     /**
24827      * Customizable piece of the default paging text (defaults to "Refresh")
24828      * @type String
24829      */
24830     refreshText : "Refresh",
24831
24832     buttons : false,
24833     // private
24834     onRender : function(ct, position) 
24835     {
24836         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24837         this.navgroup.parentId = this.id;
24838         this.navgroup.onRender(this.el, null);
24839         // add the buttons to the navgroup
24840         
24841         if(this.displayInfo){
24842             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24843             this.displayEl = this.el.select('.x-paging-info', true).first();
24844 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24845 //            this.displayEl = navel.el.select('span',true).first();
24846         }
24847         
24848         var _this = this;
24849         
24850         if(this.buttons){
24851             Roo.each(_this.buttons, function(e){ // this might need to use render????
24852                Roo.factory(e).render(_this.el);
24853             });
24854         }
24855             
24856         Roo.each(_this.toolbarItems, function(e) {
24857             _this.navgroup.addItem(e);
24858         });
24859         
24860         
24861         this.first = this.navgroup.addItem({
24862             tooltip: this.firstText,
24863             cls: "prev btn-outline-secondary",
24864             html : ' <i class="fa fa-step-backward"></i>',
24865             disabled: true,
24866             preventDefault: true,
24867             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24868         });
24869         
24870         this.prev =  this.navgroup.addItem({
24871             tooltip: this.prevText,
24872             cls: "prev btn-outline-secondary",
24873             html : ' <i class="fa fa-backward"></i>',
24874             disabled: true,
24875             preventDefault: true,
24876             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24877         });
24878     //this.addSeparator();
24879         
24880         
24881         var field = this.navgroup.addItem( {
24882             tagtype : 'span',
24883             cls : 'x-paging-position  btn-outline-secondary',
24884              disabled: true,
24885             html : this.beforePageText  +
24886                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24887                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24888          } ); //?? escaped?
24889         
24890         this.field = field.el.select('input', true).first();
24891         this.field.on("keydown", this.onPagingKeydown, this);
24892         this.field.on("focus", function(){this.dom.select();});
24893     
24894     
24895         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24896         //this.field.setHeight(18);
24897         //this.addSeparator();
24898         this.next = this.navgroup.addItem({
24899             tooltip: this.nextText,
24900             cls: "next btn-outline-secondary",
24901             html : ' <i class="fa fa-forward"></i>',
24902             disabled: true,
24903             preventDefault: true,
24904             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24905         });
24906         this.last = this.navgroup.addItem({
24907             tooltip: this.lastText,
24908             html : ' <i class="fa fa-step-forward"></i>',
24909             cls: "next btn-outline-secondary",
24910             disabled: true,
24911             preventDefault: true,
24912             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24913         });
24914     //this.addSeparator();
24915         this.loading = this.navgroup.addItem({
24916             tooltip: this.refreshText,
24917             cls: "btn-outline-secondary",
24918             html : ' <i class="fa fa-refresh"></i>',
24919             preventDefault: true,
24920             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24921         });
24922         
24923     },
24924
24925     // private
24926     updateInfo : function(){
24927         if(this.displayEl){
24928             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24929             var msg = count == 0 ?
24930                 this.emptyMsg :
24931                 String.format(
24932                     this.displayMsg,
24933                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24934                 );
24935             this.displayEl.update(msg);
24936         }
24937     },
24938
24939     // private
24940     onLoad : function(ds, r, o)
24941     {
24942         this.cursor = o.params.start ? o.params.start : 0;
24943         
24944         var d = this.getPageData(),
24945             ap = d.activePage,
24946             ps = d.pages;
24947         
24948         
24949         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24950         this.field.dom.value = ap;
24951         this.first.setDisabled(ap == 1);
24952         this.prev.setDisabled(ap == 1);
24953         this.next.setDisabled(ap == ps);
24954         this.last.setDisabled(ap == ps);
24955         this.loading.enable();
24956         this.updateInfo();
24957     },
24958
24959     // private
24960     getPageData : function(){
24961         var total = this.ds.getTotalCount();
24962         return {
24963             total : total,
24964             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24965             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24966         };
24967     },
24968
24969     // private
24970     onLoadError : function(){
24971         this.loading.enable();
24972     },
24973
24974     // private
24975     onPagingKeydown : function(e){
24976         var k = e.getKey();
24977         var d = this.getPageData();
24978         if(k == e.RETURN){
24979             var v = this.field.dom.value, pageNum;
24980             if(!v || isNaN(pageNum = parseInt(v, 10))){
24981                 this.field.dom.value = d.activePage;
24982                 return;
24983             }
24984             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24985             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24986             e.stopEvent();
24987         }
24988         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))
24989         {
24990           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24991           this.field.dom.value = pageNum;
24992           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24993           e.stopEvent();
24994         }
24995         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24996         {
24997           var v = this.field.dom.value, pageNum; 
24998           var increment = (e.shiftKey) ? 10 : 1;
24999           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25000                 increment *= -1;
25001           }
25002           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25003             this.field.dom.value = d.activePage;
25004             return;
25005           }
25006           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25007           {
25008             this.field.dom.value = parseInt(v, 10) + increment;
25009             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25010             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25011           }
25012           e.stopEvent();
25013         }
25014     },
25015
25016     // private
25017     beforeLoad : function(){
25018         if(this.loading){
25019             this.loading.disable();
25020         }
25021     },
25022
25023     // private
25024     onClick : function(which){
25025         
25026         var ds = this.ds;
25027         if (!ds) {
25028             return;
25029         }
25030         
25031         switch(which){
25032             case "first":
25033                 ds.load({params:{start: 0, limit: this.pageSize}});
25034             break;
25035             case "prev":
25036                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25037             break;
25038             case "next":
25039                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25040             break;
25041             case "last":
25042                 var total = ds.getTotalCount();
25043                 var extra = total % this.pageSize;
25044                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25045                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25046             break;
25047             case "refresh":
25048                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25049             break;
25050         }
25051     },
25052
25053     /**
25054      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25055      * @param {Roo.data.Store} store The data store to unbind
25056      */
25057     unbind : function(ds){
25058         ds.un("beforeload", this.beforeLoad, this);
25059         ds.un("load", this.onLoad, this);
25060         ds.un("loadexception", this.onLoadError, this);
25061         ds.un("remove", this.updateInfo, this);
25062         ds.un("add", this.updateInfo, this);
25063         this.ds = undefined;
25064     },
25065
25066     /**
25067      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25068      * @param {Roo.data.Store} store The data store to bind
25069      */
25070     bind : function(ds){
25071         ds.on("beforeload", this.beforeLoad, this);
25072         ds.on("load", this.onLoad, this);
25073         ds.on("loadexception", this.onLoadError, this);
25074         ds.on("remove", this.updateInfo, this);
25075         ds.on("add", this.updateInfo, this);
25076         this.ds = ds;
25077     }
25078 });/*
25079  * - LGPL
25080  *
25081  * element
25082  * 
25083  */
25084
25085 /**
25086  * @class Roo.bootstrap.MessageBar
25087  * @extends Roo.bootstrap.Component
25088  * Bootstrap MessageBar class
25089  * @cfg {String} html contents of the MessageBar
25090  * @cfg {String} weight (info | success | warning | danger) default info
25091  * @cfg {String} beforeClass insert the bar before the given class
25092  * @cfg {Boolean} closable (true | false) default false
25093  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25094  * 
25095  * @constructor
25096  * Create a new Element
25097  * @param {Object} config The config object
25098  */
25099
25100 Roo.bootstrap.MessageBar = function(config){
25101     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25102 };
25103
25104 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25105     
25106     html: '',
25107     weight: 'info',
25108     closable: false,
25109     fixed: false,
25110     beforeClass: 'bootstrap-sticky-wrap',
25111     
25112     getAutoCreate : function(){
25113         
25114         var cfg = {
25115             tag: 'div',
25116             cls: 'alert alert-dismissable alert-' + this.weight,
25117             cn: [
25118                 {
25119                     tag: 'span',
25120                     cls: 'message',
25121                     html: this.html || ''
25122                 }
25123             ]
25124         };
25125         
25126         if(this.fixed){
25127             cfg.cls += ' alert-messages-fixed';
25128         }
25129         
25130         if(this.closable){
25131             cfg.cn.push({
25132                 tag: 'button',
25133                 cls: 'close',
25134                 html: 'x'
25135             });
25136         }
25137         
25138         return cfg;
25139     },
25140     
25141     onRender : function(ct, position)
25142     {
25143         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25144         
25145         if(!this.el){
25146             var cfg = Roo.apply({},  this.getAutoCreate());
25147             cfg.id = Roo.id();
25148             
25149             if (this.cls) {
25150                 cfg.cls += ' ' + this.cls;
25151             }
25152             if (this.style) {
25153                 cfg.style = this.style;
25154             }
25155             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25156             
25157             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25158         }
25159         
25160         this.el.select('>button.close').on('click', this.hide, this);
25161         
25162     },
25163     
25164     show : function()
25165     {
25166         if (!this.rendered) {
25167             this.render();
25168         }
25169         
25170         this.el.show();
25171         
25172         this.fireEvent('show', this);
25173         
25174     },
25175     
25176     hide : function()
25177     {
25178         if (!this.rendered) {
25179             this.render();
25180         }
25181         
25182         this.el.hide();
25183         
25184         this.fireEvent('hide', this);
25185     },
25186     
25187     update : function()
25188     {
25189 //        var e = this.el.dom.firstChild;
25190 //        
25191 //        if(this.closable){
25192 //            e = e.nextSibling;
25193 //        }
25194 //        
25195 //        e.data = this.html || '';
25196
25197         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25198     }
25199    
25200 });
25201
25202  
25203
25204      /*
25205  * - LGPL
25206  *
25207  * Graph
25208  * 
25209  */
25210
25211
25212 /**
25213  * @class Roo.bootstrap.Graph
25214  * @extends Roo.bootstrap.Component
25215  * Bootstrap Graph class
25216 > Prameters
25217  -sm {number} sm 4
25218  -md {number} md 5
25219  @cfg {String} graphtype  bar | vbar | pie
25220  @cfg {number} g_x coodinator | centre x (pie)
25221  @cfg {number} g_y coodinator | centre y (pie)
25222  @cfg {number} g_r radius (pie)
25223  @cfg {number} g_height height of the chart (respected by all elements in the set)
25224  @cfg {number} g_width width of the chart (respected by all elements in the set)
25225  @cfg {Object} title The title of the chart
25226     
25227  -{Array}  values
25228  -opts (object) options for the chart 
25229      o {
25230      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25231      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25232      o vgutter (number)
25233      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.
25234      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25235      o to
25236      o stretch (boolean)
25237      o }
25238  -opts (object) options for the pie
25239      o{
25240      o cut
25241      o startAngle (number)
25242      o endAngle (number)
25243      } 
25244  *
25245  * @constructor
25246  * Create a new Input
25247  * @param {Object} config The config object
25248  */
25249
25250 Roo.bootstrap.Graph = function(config){
25251     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25252     
25253     this.addEvents({
25254         // img events
25255         /**
25256          * @event click
25257          * The img click event for the img.
25258          * @param {Roo.EventObject} e
25259          */
25260         "click" : true
25261     });
25262 };
25263
25264 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25265     
25266     sm: 4,
25267     md: 5,
25268     graphtype: 'bar',
25269     g_height: 250,
25270     g_width: 400,
25271     g_x: 50,
25272     g_y: 50,
25273     g_r: 30,
25274     opts:{
25275         //g_colors: this.colors,
25276         g_type: 'soft',
25277         g_gutter: '20%'
25278
25279     },
25280     title : false,
25281
25282     getAutoCreate : function(){
25283         
25284         var cfg = {
25285             tag: 'div',
25286             html : null
25287         };
25288         
25289         
25290         return  cfg;
25291     },
25292
25293     onRender : function(ct,position){
25294         
25295         
25296         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25297         
25298         if (typeof(Raphael) == 'undefined') {
25299             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25300             return;
25301         }
25302         
25303         this.raphael = Raphael(this.el.dom);
25304         
25305                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25306                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25307                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25308                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25309                 /*
25310                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25311                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25312                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25313                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25314                 
25315                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25316                 r.barchart(330, 10, 300, 220, data1);
25317                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25318                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25319                 */
25320                 
25321                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25322                 // r.barchart(30, 30, 560, 250,  xdata, {
25323                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25324                 //     axis : "0 0 1 1",
25325                 //     axisxlabels :  xdata
25326                 //     //yvalues : cols,
25327                    
25328                 // });
25329 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25330 //        
25331 //        this.load(null,xdata,{
25332 //                axis : "0 0 1 1",
25333 //                axisxlabels :  xdata
25334 //                });
25335
25336     },
25337
25338     load : function(graphtype,xdata,opts)
25339     {
25340         this.raphael.clear();
25341         if(!graphtype) {
25342             graphtype = this.graphtype;
25343         }
25344         if(!opts){
25345             opts = this.opts;
25346         }
25347         var r = this.raphael,
25348             fin = function () {
25349                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25350             },
25351             fout = function () {
25352                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25353             },
25354             pfin = function() {
25355                 this.sector.stop();
25356                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25357
25358                 if (this.label) {
25359                     this.label[0].stop();
25360                     this.label[0].attr({ r: 7.5 });
25361                     this.label[1].attr({ "font-weight": 800 });
25362                 }
25363             },
25364             pfout = function() {
25365                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25366
25367                 if (this.label) {
25368                     this.label[0].animate({ r: 5 }, 500, "bounce");
25369                     this.label[1].attr({ "font-weight": 400 });
25370                 }
25371             };
25372
25373         switch(graphtype){
25374             case 'bar':
25375                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25376                 break;
25377             case 'hbar':
25378                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25379                 break;
25380             case 'pie':
25381 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25382 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25383 //            
25384                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25385                 
25386                 break;
25387
25388         }
25389         
25390         if(this.title){
25391             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25392         }
25393         
25394     },
25395     
25396     setTitle: function(o)
25397     {
25398         this.title = o;
25399     },
25400     
25401     initEvents: function() {
25402         
25403         if(!this.href){
25404             this.el.on('click', this.onClick, this);
25405         }
25406     },
25407     
25408     onClick : function(e)
25409     {
25410         Roo.log('img onclick');
25411         this.fireEvent('click', this, e);
25412     }
25413    
25414 });
25415
25416  
25417 /*
25418  * - LGPL
25419  *
25420  * numberBox
25421  * 
25422  */
25423 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25424
25425 /**
25426  * @class Roo.bootstrap.dash.NumberBox
25427  * @extends Roo.bootstrap.Component
25428  * Bootstrap NumberBox class
25429  * @cfg {String} headline Box headline
25430  * @cfg {String} content Box content
25431  * @cfg {String} icon Box icon
25432  * @cfg {String} footer Footer text
25433  * @cfg {String} fhref Footer href
25434  * 
25435  * @constructor
25436  * Create a new NumberBox
25437  * @param {Object} config The config object
25438  */
25439
25440
25441 Roo.bootstrap.dash.NumberBox = function(config){
25442     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25443     
25444 };
25445
25446 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25447     
25448     headline : '',
25449     content : '',
25450     icon : '',
25451     footer : '',
25452     fhref : '',
25453     ficon : '',
25454     
25455     getAutoCreate : function(){
25456         
25457         var cfg = {
25458             tag : 'div',
25459             cls : 'small-box ',
25460             cn : [
25461                 {
25462                     tag : 'div',
25463                     cls : 'inner',
25464                     cn :[
25465                         {
25466                             tag : 'h3',
25467                             cls : 'roo-headline',
25468                             html : this.headline
25469                         },
25470                         {
25471                             tag : 'p',
25472                             cls : 'roo-content',
25473                             html : this.content
25474                         }
25475                     ]
25476                 }
25477             ]
25478         };
25479         
25480         if(this.icon){
25481             cfg.cn.push({
25482                 tag : 'div',
25483                 cls : 'icon',
25484                 cn :[
25485                     {
25486                         tag : 'i',
25487                         cls : 'ion ' + this.icon
25488                     }
25489                 ]
25490             });
25491         }
25492         
25493         if(this.footer){
25494             var footer = {
25495                 tag : 'a',
25496                 cls : 'small-box-footer',
25497                 href : this.fhref || '#',
25498                 html : this.footer
25499             };
25500             
25501             cfg.cn.push(footer);
25502             
25503         }
25504         
25505         return  cfg;
25506     },
25507
25508     onRender : function(ct,position){
25509         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25510
25511
25512        
25513                 
25514     },
25515
25516     setHeadline: function (value)
25517     {
25518         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25519     },
25520     
25521     setFooter: function (value, href)
25522     {
25523         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25524         
25525         if(href){
25526             this.el.select('a.small-box-footer',true).first().attr('href', href);
25527         }
25528         
25529     },
25530
25531     setContent: function (value)
25532     {
25533         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25534     },
25535
25536     initEvents: function() 
25537     {   
25538         
25539     }
25540     
25541 });
25542
25543  
25544 /*
25545  * - LGPL
25546  *
25547  * TabBox
25548  * 
25549  */
25550 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25551
25552 /**
25553  * @class Roo.bootstrap.dash.TabBox
25554  * @extends Roo.bootstrap.Component
25555  * Bootstrap TabBox class
25556  * @cfg {String} title Title of the TabBox
25557  * @cfg {String} icon Icon of the TabBox
25558  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25559  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25560  * 
25561  * @constructor
25562  * Create a new TabBox
25563  * @param {Object} config The config object
25564  */
25565
25566
25567 Roo.bootstrap.dash.TabBox = function(config){
25568     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25569     this.addEvents({
25570         // raw events
25571         /**
25572          * @event addpane
25573          * When a pane is added
25574          * @param {Roo.bootstrap.dash.TabPane} pane
25575          */
25576         "addpane" : true,
25577         /**
25578          * @event activatepane
25579          * When a pane is activated
25580          * @param {Roo.bootstrap.dash.TabPane} pane
25581          */
25582         "activatepane" : true
25583         
25584          
25585     });
25586     
25587     this.panes = [];
25588 };
25589
25590 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25591
25592     title : '',
25593     icon : false,
25594     showtabs : true,
25595     tabScrollable : false,
25596     
25597     getChildContainer : function()
25598     {
25599         return this.el.select('.tab-content', true).first();
25600     },
25601     
25602     getAutoCreate : function(){
25603         
25604         var header = {
25605             tag: 'li',
25606             cls: 'pull-left header',
25607             html: this.title,
25608             cn : []
25609         };
25610         
25611         if(this.icon){
25612             header.cn.push({
25613                 tag: 'i',
25614                 cls: 'fa ' + this.icon
25615             });
25616         }
25617         
25618         var h = {
25619             tag: 'ul',
25620             cls: 'nav nav-tabs pull-right',
25621             cn: [
25622                 header
25623             ]
25624         };
25625         
25626         if(this.tabScrollable){
25627             h = {
25628                 tag: 'div',
25629                 cls: 'tab-header',
25630                 cn: [
25631                     {
25632                         tag: 'ul',
25633                         cls: 'nav nav-tabs pull-right',
25634                         cn: [
25635                             header
25636                         ]
25637                     }
25638                 ]
25639             };
25640         }
25641         
25642         var cfg = {
25643             tag: 'div',
25644             cls: 'nav-tabs-custom',
25645             cn: [
25646                 h,
25647                 {
25648                     tag: 'div',
25649                     cls: 'tab-content no-padding',
25650                     cn: []
25651                 }
25652             ]
25653         };
25654
25655         return  cfg;
25656     },
25657     initEvents : function()
25658     {
25659         //Roo.log('add add pane handler');
25660         this.on('addpane', this.onAddPane, this);
25661     },
25662      /**
25663      * Updates the box title
25664      * @param {String} html to set the title to.
25665      */
25666     setTitle : function(value)
25667     {
25668         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25669     },
25670     onAddPane : function(pane)
25671     {
25672         this.panes.push(pane);
25673         //Roo.log('addpane');
25674         //Roo.log(pane);
25675         // tabs are rendere left to right..
25676         if(!this.showtabs){
25677             return;
25678         }
25679         
25680         var ctr = this.el.select('.nav-tabs', true).first();
25681          
25682          
25683         var existing = ctr.select('.nav-tab',true);
25684         var qty = existing.getCount();;
25685         
25686         
25687         var tab = ctr.createChild({
25688             tag : 'li',
25689             cls : 'nav-tab' + (qty ? '' : ' active'),
25690             cn : [
25691                 {
25692                     tag : 'a',
25693                     href:'#',
25694                     html : pane.title
25695                 }
25696             ]
25697         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25698         pane.tab = tab;
25699         
25700         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25701         if (!qty) {
25702             pane.el.addClass('active');
25703         }
25704         
25705                 
25706     },
25707     onTabClick : function(ev,un,ob,pane)
25708     {
25709         //Roo.log('tab - prev default');
25710         ev.preventDefault();
25711         
25712         
25713         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25714         pane.tab.addClass('active');
25715         //Roo.log(pane.title);
25716         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25717         // technically we should have a deactivate event.. but maybe add later.
25718         // and it should not de-activate the selected tab...
25719         this.fireEvent('activatepane', pane);
25720         pane.el.addClass('active');
25721         pane.fireEvent('activate');
25722         
25723         
25724     },
25725     
25726     getActivePane : function()
25727     {
25728         var r = false;
25729         Roo.each(this.panes, function(p) {
25730             if(p.el.hasClass('active')){
25731                 r = p;
25732                 return false;
25733             }
25734             
25735             return;
25736         });
25737         
25738         return r;
25739     }
25740     
25741     
25742 });
25743
25744  
25745 /*
25746  * - LGPL
25747  *
25748  * Tab pane
25749  * 
25750  */
25751 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25752 /**
25753  * @class Roo.bootstrap.TabPane
25754  * @extends Roo.bootstrap.Component
25755  * Bootstrap TabPane class
25756  * @cfg {Boolean} active (false | true) Default false
25757  * @cfg {String} title title of panel
25758
25759  * 
25760  * @constructor
25761  * Create a new TabPane
25762  * @param {Object} config The config object
25763  */
25764
25765 Roo.bootstrap.dash.TabPane = function(config){
25766     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25767     
25768     this.addEvents({
25769         // raw events
25770         /**
25771          * @event activate
25772          * When a pane is activated
25773          * @param {Roo.bootstrap.dash.TabPane} pane
25774          */
25775         "activate" : true
25776          
25777     });
25778 };
25779
25780 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25781     
25782     active : false,
25783     title : '',
25784     
25785     // the tabBox that this is attached to.
25786     tab : false,
25787      
25788     getAutoCreate : function() 
25789     {
25790         var cfg = {
25791             tag: 'div',
25792             cls: 'tab-pane'
25793         };
25794         
25795         if(this.active){
25796             cfg.cls += ' active';
25797         }
25798         
25799         return cfg;
25800     },
25801     initEvents  : function()
25802     {
25803         //Roo.log('trigger add pane handler');
25804         this.parent().fireEvent('addpane', this)
25805     },
25806     
25807      /**
25808      * Updates the tab title 
25809      * @param {String} html to set the title to.
25810      */
25811     setTitle: function(str)
25812     {
25813         if (!this.tab) {
25814             return;
25815         }
25816         this.title = str;
25817         this.tab.select('a', true).first().dom.innerHTML = str;
25818         
25819     }
25820     
25821     
25822     
25823 });
25824
25825  
25826
25827
25828  /*
25829  * - LGPL
25830  *
25831  * menu
25832  * 
25833  */
25834 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25835
25836 /**
25837  * @class Roo.bootstrap.menu.Menu
25838  * @extends Roo.bootstrap.Component
25839  * Bootstrap Menu class - container for Menu
25840  * @cfg {String} html Text of the menu
25841  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25842  * @cfg {String} icon Font awesome icon
25843  * @cfg {String} pos Menu align to (top | bottom) default bottom
25844  * 
25845  * 
25846  * @constructor
25847  * Create a new Menu
25848  * @param {Object} config The config object
25849  */
25850
25851
25852 Roo.bootstrap.menu.Menu = function(config){
25853     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25854     
25855     this.addEvents({
25856         /**
25857          * @event beforeshow
25858          * Fires before this menu is displayed
25859          * @param {Roo.bootstrap.menu.Menu} this
25860          */
25861         beforeshow : true,
25862         /**
25863          * @event beforehide
25864          * Fires before this menu is hidden
25865          * @param {Roo.bootstrap.menu.Menu} this
25866          */
25867         beforehide : true,
25868         /**
25869          * @event show
25870          * Fires after this menu is displayed
25871          * @param {Roo.bootstrap.menu.Menu} this
25872          */
25873         show : true,
25874         /**
25875          * @event hide
25876          * Fires after this menu is hidden
25877          * @param {Roo.bootstrap.menu.Menu} this
25878          */
25879         hide : true,
25880         /**
25881          * @event click
25882          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25883          * @param {Roo.bootstrap.menu.Menu} this
25884          * @param {Roo.EventObject} e
25885          */
25886         click : true
25887     });
25888     
25889 };
25890
25891 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25892     
25893     submenu : false,
25894     html : '',
25895     weight : 'default',
25896     icon : false,
25897     pos : 'bottom',
25898     
25899     
25900     getChildContainer : function() {
25901         if(this.isSubMenu){
25902             return this.el;
25903         }
25904         
25905         return this.el.select('ul.dropdown-menu', true).first();  
25906     },
25907     
25908     getAutoCreate : function()
25909     {
25910         var text = [
25911             {
25912                 tag : 'span',
25913                 cls : 'roo-menu-text',
25914                 html : this.html
25915             }
25916         ];
25917         
25918         if(this.icon){
25919             text.unshift({
25920                 tag : 'i',
25921                 cls : 'fa ' + this.icon
25922             })
25923         }
25924         
25925         
25926         var cfg = {
25927             tag : 'div',
25928             cls : 'btn-group',
25929             cn : [
25930                 {
25931                     tag : 'button',
25932                     cls : 'dropdown-button btn btn-' + this.weight,
25933                     cn : text
25934                 },
25935                 {
25936                     tag : 'button',
25937                     cls : 'dropdown-toggle btn btn-' + this.weight,
25938                     cn : [
25939                         {
25940                             tag : 'span',
25941                             cls : 'caret'
25942                         }
25943                     ]
25944                 },
25945                 {
25946                     tag : 'ul',
25947                     cls : 'dropdown-menu'
25948                 }
25949             ]
25950             
25951         };
25952         
25953         if(this.pos == 'top'){
25954             cfg.cls += ' dropup';
25955         }
25956         
25957         if(this.isSubMenu){
25958             cfg = {
25959                 tag : 'ul',
25960                 cls : 'dropdown-menu'
25961             }
25962         }
25963         
25964         return cfg;
25965     },
25966     
25967     onRender : function(ct, position)
25968     {
25969         this.isSubMenu = ct.hasClass('dropdown-submenu');
25970         
25971         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25972     },
25973     
25974     initEvents : function() 
25975     {
25976         if(this.isSubMenu){
25977             return;
25978         }
25979         
25980         this.hidden = true;
25981         
25982         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25983         this.triggerEl.on('click', this.onTriggerPress, this);
25984         
25985         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25986         this.buttonEl.on('click', this.onClick, this);
25987         
25988     },
25989     
25990     list : function()
25991     {
25992         if(this.isSubMenu){
25993             return this.el;
25994         }
25995         
25996         return this.el.select('ul.dropdown-menu', true).first();
25997     },
25998     
25999     onClick : function(e)
26000     {
26001         this.fireEvent("click", this, e);
26002     },
26003     
26004     onTriggerPress  : function(e)
26005     {   
26006         if (this.isVisible()) {
26007             this.hide();
26008         } else {
26009             this.show();
26010         }
26011     },
26012     
26013     isVisible : function(){
26014         return !this.hidden;
26015     },
26016     
26017     show : function()
26018     {
26019         this.fireEvent("beforeshow", this);
26020         
26021         this.hidden = false;
26022         this.el.addClass('open');
26023         
26024         Roo.get(document).on("mouseup", this.onMouseUp, this);
26025         
26026         this.fireEvent("show", this);
26027         
26028         
26029     },
26030     
26031     hide : function()
26032     {
26033         this.fireEvent("beforehide", this);
26034         
26035         this.hidden = true;
26036         this.el.removeClass('open');
26037         
26038         Roo.get(document).un("mouseup", this.onMouseUp);
26039         
26040         this.fireEvent("hide", this);
26041     },
26042     
26043     onMouseUp : function()
26044     {
26045         this.hide();
26046     }
26047     
26048 });
26049
26050  
26051  /*
26052  * - LGPL
26053  *
26054  * menu item
26055  * 
26056  */
26057 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26058
26059 /**
26060  * @class Roo.bootstrap.menu.Item
26061  * @extends Roo.bootstrap.Component
26062  * Bootstrap MenuItem class
26063  * @cfg {Boolean} submenu (true | false) default false
26064  * @cfg {String} html text of the item
26065  * @cfg {String} href the link
26066  * @cfg {Boolean} disable (true | false) default false
26067  * @cfg {Boolean} preventDefault (true | false) default true
26068  * @cfg {String} icon Font awesome icon
26069  * @cfg {String} pos Submenu align to (left | right) default right 
26070  * 
26071  * 
26072  * @constructor
26073  * Create a new Item
26074  * @param {Object} config The config object
26075  */
26076
26077
26078 Roo.bootstrap.menu.Item = function(config){
26079     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26080     this.addEvents({
26081         /**
26082          * @event mouseover
26083          * Fires when the mouse is hovering over this menu
26084          * @param {Roo.bootstrap.menu.Item} this
26085          * @param {Roo.EventObject} e
26086          */
26087         mouseover : true,
26088         /**
26089          * @event mouseout
26090          * Fires when the mouse exits this menu
26091          * @param {Roo.bootstrap.menu.Item} this
26092          * @param {Roo.EventObject} e
26093          */
26094         mouseout : true,
26095         // raw events
26096         /**
26097          * @event click
26098          * The raw click event for the entire grid.
26099          * @param {Roo.EventObject} e
26100          */
26101         click : true
26102     });
26103 };
26104
26105 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26106     
26107     submenu : false,
26108     href : '',
26109     html : '',
26110     preventDefault: true,
26111     disable : false,
26112     icon : false,
26113     pos : 'right',
26114     
26115     getAutoCreate : function()
26116     {
26117         var text = [
26118             {
26119                 tag : 'span',
26120                 cls : 'roo-menu-item-text',
26121                 html : this.html
26122             }
26123         ];
26124         
26125         if(this.icon){
26126             text.unshift({
26127                 tag : 'i',
26128                 cls : 'fa ' + this.icon
26129             })
26130         }
26131         
26132         var cfg = {
26133             tag : 'li',
26134             cn : [
26135                 {
26136                     tag : 'a',
26137                     href : this.href || '#',
26138                     cn : text
26139                 }
26140             ]
26141         };
26142         
26143         if(this.disable){
26144             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26145         }
26146         
26147         if(this.submenu){
26148             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26149             
26150             if(this.pos == 'left'){
26151                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26152             }
26153         }
26154         
26155         return cfg;
26156     },
26157     
26158     initEvents : function() 
26159     {
26160         this.el.on('mouseover', this.onMouseOver, this);
26161         this.el.on('mouseout', this.onMouseOut, this);
26162         
26163         this.el.select('a', true).first().on('click', this.onClick, this);
26164         
26165     },
26166     
26167     onClick : function(e)
26168     {
26169         if(this.preventDefault){
26170             e.preventDefault();
26171         }
26172         
26173         this.fireEvent("click", this, e);
26174     },
26175     
26176     onMouseOver : function(e)
26177     {
26178         if(this.submenu && this.pos == 'left'){
26179             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26180         }
26181         
26182         this.fireEvent("mouseover", this, e);
26183     },
26184     
26185     onMouseOut : function(e)
26186     {
26187         this.fireEvent("mouseout", this, e);
26188     }
26189 });
26190
26191  
26192
26193  /*
26194  * - LGPL
26195  *
26196  * menu separator
26197  * 
26198  */
26199 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26200
26201 /**
26202  * @class Roo.bootstrap.menu.Separator
26203  * @extends Roo.bootstrap.Component
26204  * Bootstrap Separator class
26205  * 
26206  * @constructor
26207  * Create a new Separator
26208  * @param {Object} config The config object
26209  */
26210
26211
26212 Roo.bootstrap.menu.Separator = function(config){
26213     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26214 };
26215
26216 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26217     
26218     getAutoCreate : function(){
26219         var cfg = {
26220             tag : 'li',
26221             cls: 'divider'
26222         };
26223         
26224         return cfg;
26225     }
26226    
26227 });
26228
26229  
26230
26231  /*
26232  * - LGPL
26233  *
26234  * Tooltip
26235  * 
26236  */
26237
26238 /**
26239  * @class Roo.bootstrap.Tooltip
26240  * Bootstrap Tooltip class
26241  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26242  * to determine which dom element triggers the tooltip.
26243  * 
26244  * It needs to add support for additional attributes like tooltip-position
26245  * 
26246  * @constructor
26247  * Create a new Toolti
26248  * @param {Object} config The config object
26249  */
26250
26251 Roo.bootstrap.Tooltip = function(config){
26252     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26253     
26254     this.alignment = Roo.bootstrap.Tooltip.alignment;
26255     
26256     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26257         this.alignment = config.alignment;
26258     }
26259     
26260 };
26261
26262 Roo.apply(Roo.bootstrap.Tooltip, {
26263     /**
26264      * @function init initialize tooltip monitoring.
26265      * @static
26266      */
26267     currentEl : false,
26268     currentTip : false,
26269     currentRegion : false,
26270     
26271     //  init : delay?
26272     
26273     init : function()
26274     {
26275         Roo.get(document).on('mouseover', this.enter ,this);
26276         Roo.get(document).on('mouseout', this.leave, this);
26277          
26278         
26279         this.currentTip = new Roo.bootstrap.Tooltip();
26280     },
26281     
26282     enter : function(ev)
26283     {
26284         var dom = ev.getTarget();
26285         
26286         //Roo.log(['enter',dom]);
26287         var el = Roo.fly(dom);
26288         if (this.currentEl) {
26289             //Roo.log(dom);
26290             //Roo.log(this.currentEl);
26291             //Roo.log(this.currentEl.contains(dom));
26292             if (this.currentEl == el) {
26293                 return;
26294             }
26295             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26296                 return;
26297             }
26298
26299         }
26300         
26301         if (this.currentTip.el) {
26302             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26303         }    
26304         //Roo.log(ev);
26305         
26306         if(!el || el.dom == document){
26307             return;
26308         }
26309         
26310         var bindEl = el;
26311         
26312         // you can not look for children, as if el is the body.. then everythign is the child..
26313         if (!el.attr('tooltip')) { //
26314             if (!el.select("[tooltip]").elements.length) {
26315                 return;
26316             }
26317             // is the mouse over this child...?
26318             bindEl = el.select("[tooltip]").first();
26319             var xy = ev.getXY();
26320             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26321                 //Roo.log("not in region.");
26322                 return;
26323             }
26324             //Roo.log("child element over..");
26325             
26326         }
26327         this.currentEl = bindEl;
26328         this.currentTip.bind(bindEl);
26329         this.currentRegion = Roo.lib.Region.getRegion(dom);
26330         this.currentTip.enter();
26331         
26332     },
26333     leave : function(ev)
26334     {
26335         var dom = ev.getTarget();
26336         //Roo.log(['leave',dom]);
26337         if (!this.currentEl) {
26338             return;
26339         }
26340         
26341         
26342         if (dom != this.currentEl.dom) {
26343             return;
26344         }
26345         var xy = ev.getXY();
26346         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26347             return;
26348         }
26349         // only activate leave if mouse cursor is outside... bounding box..
26350         
26351         
26352         
26353         
26354         if (this.currentTip) {
26355             this.currentTip.leave();
26356         }
26357         //Roo.log('clear currentEl');
26358         this.currentEl = false;
26359         
26360         
26361     },
26362     alignment : {
26363         'left' : ['r-l', [-2,0], 'right'],
26364         'right' : ['l-r', [2,0], 'left'],
26365         'bottom' : ['t-b', [0,2], 'top'],
26366         'top' : [ 'b-t', [0,-2], 'bottom']
26367     }
26368     
26369 });
26370
26371
26372 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26373     
26374     
26375     bindEl : false,
26376     
26377     delay : null, // can be { show : 300 , hide: 500}
26378     
26379     timeout : null,
26380     
26381     hoverState : null, //???
26382     
26383     placement : 'bottom', 
26384     
26385     alignment : false,
26386     
26387     getAutoCreate : function(){
26388     
26389         var cfg = {
26390            cls : 'tooltip',
26391            role : 'tooltip',
26392            cn : [
26393                 {
26394                     cls : 'tooltip-arrow'
26395                 },
26396                 {
26397                     cls : 'tooltip-inner'
26398                 }
26399            ]
26400         };
26401         
26402         return cfg;
26403     },
26404     bind : function(el)
26405     {
26406         this.bindEl = el;
26407     },
26408       
26409     
26410     enter : function () {
26411        
26412         if (this.timeout != null) {
26413             clearTimeout(this.timeout);
26414         }
26415         
26416         this.hoverState = 'in';
26417          //Roo.log("enter - show");
26418         if (!this.delay || !this.delay.show) {
26419             this.show();
26420             return;
26421         }
26422         var _t = this;
26423         this.timeout = setTimeout(function () {
26424             if (_t.hoverState == 'in') {
26425                 _t.show();
26426             }
26427         }, this.delay.show);
26428     },
26429     leave : function()
26430     {
26431         clearTimeout(this.timeout);
26432     
26433         this.hoverState = 'out';
26434          if (!this.delay || !this.delay.hide) {
26435             this.hide();
26436             return;
26437         }
26438        
26439         var _t = this;
26440         this.timeout = setTimeout(function () {
26441             //Roo.log("leave - timeout");
26442             
26443             if (_t.hoverState == 'out') {
26444                 _t.hide();
26445                 Roo.bootstrap.Tooltip.currentEl = false;
26446             }
26447         }, delay);
26448     },
26449     
26450     show : function (msg)
26451     {
26452         if (!this.el) {
26453             this.render(document.body);
26454         }
26455         // set content.
26456         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26457         
26458         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26459         
26460         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26461         
26462         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26463         
26464         var placement = typeof this.placement == 'function' ?
26465             this.placement.call(this, this.el, on_el) :
26466             this.placement;
26467             
26468         var autoToken = /\s?auto?\s?/i;
26469         var autoPlace = autoToken.test(placement);
26470         if (autoPlace) {
26471             placement = placement.replace(autoToken, '') || 'top';
26472         }
26473         
26474         //this.el.detach()
26475         //this.el.setXY([0,0]);
26476         this.el.show();
26477         //this.el.dom.style.display='block';
26478         
26479         //this.el.appendTo(on_el);
26480         
26481         var p = this.getPosition();
26482         var box = this.el.getBox();
26483         
26484         if (autoPlace) {
26485             // fixme..
26486         }
26487         
26488         var align = this.alignment[placement];
26489         
26490         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26491         
26492         if(placement == 'top' || placement == 'bottom'){
26493             if(xy[0] < 0){
26494                 placement = 'right';
26495             }
26496             
26497             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26498                 placement = 'left';
26499             }
26500             
26501             var scroll = Roo.select('body', true).first().getScroll();
26502             
26503             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26504                 placement = 'top';
26505             }
26506             
26507             align = this.alignment[placement];
26508         }
26509         
26510         this.el.alignTo(this.bindEl, align[0],align[1]);
26511         //var arrow = this.el.select('.arrow',true).first();
26512         //arrow.set(align[2], 
26513         
26514         this.el.addClass(placement);
26515         
26516         this.el.addClass('in fade');
26517         
26518         this.hoverState = null;
26519         
26520         if (this.el.hasClass('fade')) {
26521             // fade it?
26522         }
26523         
26524     },
26525     hide : function()
26526     {
26527          
26528         if (!this.el) {
26529             return;
26530         }
26531         //this.el.setXY([0,0]);
26532         this.el.removeClass('in');
26533         //this.el.hide();
26534         
26535     }
26536     
26537 });
26538  
26539
26540  /*
26541  * - LGPL
26542  *
26543  * Location Picker
26544  * 
26545  */
26546
26547 /**
26548  * @class Roo.bootstrap.LocationPicker
26549  * @extends Roo.bootstrap.Component
26550  * Bootstrap LocationPicker class
26551  * @cfg {Number} latitude Position when init default 0
26552  * @cfg {Number} longitude Position when init default 0
26553  * @cfg {Number} zoom default 15
26554  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26555  * @cfg {Boolean} mapTypeControl default false
26556  * @cfg {Boolean} disableDoubleClickZoom default false
26557  * @cfg {Boolean} scrollwheel default true
26558  * @cfg {Boolean} streetViewControl default false
26559  * @cfg {Number} radius default 0
26560  * @cfg {String} locationName
26561  * @cfg {Boolean} draggable default true
26562  * @cfg {Boolean} enableAutocomplete default false
26563  * @cfg {Boolean} enableReverseGeocode default true
26564  * @cfg {String} markerTitle
26565  * 
26566  * @constructor
26567  * Create a new LocationPicker
26568  * @param {Object} config The config object
26569  */
26570
26571
26572 Roo.bootstrap.LocationPicker = function(config){
26573     
26574     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26575     
26576     this.addEvents({
26577         /**
26578          * @event initial
26579          * Fires when the picker initialized.
26580          * @param {Roo.bootstrap.LocationPicker} this
26581          * @param {Google Location} location
26582          */
26583         initial : true,
26584         /**
26585          * @event positionchanged
26586          * Fires when the picker position changed.
26587          * @param {Roo.bootstrap.LocationPicker} this
26588          * @param {Google Location} location
26589          */
26590         positionchanged : true,
26591         /**
26592          * @event resize
26593          * Fires when the map resize.
26594          * @param {Roo.bootstrap.LocationPicker} this
26595          */
26596         resize : true,
26597         /**
26598          * @event show
26599          * Fires when the map show.
26600          * @param {Roo.bootstrap.LocationPicker} this
26601          */
26602         show : true,
26603         /**
26604          * @event hide
26605          * Fires when the map hide.
26606          * @param {Roo.bootstrap.LocationPicker} this
26607          */
26608         hide : true,
26609         /**
26610          * @event mapClick
26611          * Fires when click the map.
26612          * @param {Roo.bootstrap.LocationPicker} this
26613          * @param {Map event} e
26614          */
26615         mapClick : true,
26616         /**
26617          * @event mapRightClick
26618          * Fires when right click the map.
26619          * @param {Roo.bootstrap.LocationPicker} this
26620          * @param {Map event} e
26621          */
26622         mapRightClick : true,
26623         /**
26624          * @event markerClick
26625          * Fires when click the marker.
26626          * @param {Roo.bootstrap.LocationPicker} this
26627          * @param {Map event} e
26628          */
26629         markerClick : true,
26630         /**
26631          * @event markerRightClick
26632          * Fires when right click the marker.
26633          * @param {Roo.bootstrap.LocationPicker} this
26634          * @param {Map event} e
26635          */
26636         markerRightClick : true,
26637         /**
26638          * @event OverlayViewDraw
26639          * Fires when OverlayView Draw
26640          * @param {Roo.bootstrap.LocationPicker} this
26641          */
26642         OverlayViewDraw : true,
26643         /**
26644          * @event OverlayViewOnAdd
26645          * Fires when OverlayView Draw
26646          * @param {Roo.bootstrap.LocationPicker} this
26647          */
26648         OverlayViewOnAdd : true,
26649         /**
26650          * @event OverlayViewOnRemove
26651          * Fires when OverlayView Draw
26652          * @param {Roo.bootstrap.LocationPicker} this
26653          */
26654         OverlayViewOnRemove : true,
26655         /**
26656          * @event OverlayViewShow
26657          * Fires when OverlayView Draw
26658          * @param {Roo.bootstrap.LocationPicker} this
26659          * @param {Pixel} cpx
26660          */
26661         OverlayViewShow : true,
26662         /**
26663          * @event OverlayViewHide
26664          * Fires when OverlayView Draw
26665          * @param {Roo.bootstrap.LocationPicker} this
26666          */
26667         OverlayViewHide : true,
26668         /**
26669          * @event loadexception
26670          * Fires when load google lib failed.
26671          * @param {Roo.bootstrap.LocationPicker} this
26672          */
26673         loadexception : true
26674     });
26675         
26676 };
26677
26678 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26679     
26680     gMapContext: false,
26681     
26682     latitude: 0,
26683     longitude: 0,
26684     zoom: 15,
26685     mapTypeId: false,
26686     mapTypeControl: false,
26687     disableDoubleClickZoom: false,
26688     scrollwheel: true,
26689     streetViewControl: false,
26690     radius: 0,
26691     locationName: '',
26692     draggable: true,
26693     enableAutocomplete: false,
26694     enableReverseGeocode: true,
26695     markerTitle: '',
26696     
26697     getAutoCreate: function()
26698     {
26699
26700         var cfg = {
26701             tag: 'div',
26702             cls: 'roo-location-picker'
26703         };
26704         
26705         return cfg
26706     },
26707     
26708     initEvents: function(ct, position)
26709     {       
26710         if(!this.el.getWidth() || this.isApplied()){
26711             return;
26712         }
26713         
26714         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26715         
26716         this.initial();
26717     },
26718     
26719     initial: function()
26720     {
26721         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26722             this.fireEvent('loadexception', this);
26723             return;
26724         }
26725         
26726         if(!this.mapTypeId){
26727             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26728         }
26729         
26730         this.gMapContext = this.GMapContext();
26731         
26732         this.initOverlayView();
26733         
26734         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26735         
26736         var _this = this;
26737                 
26738         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26739             _this.setPosition(_this.gMapContext.marker.position);
26740         });
26741         
26742         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26743             _this.fireEvent('mapClick', this, event);
26744             
26745         });
26746
26747         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26748             _this.fireEvent('mapRightClick', this, event);
26749             
26750         });
26751         
26752         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26753             _this.fireEvent('markerClick', this, event);
26754             
26755         });
26756
26757         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26758             _this.fireEvent('markerRightClick', this, event);
26759             
26760         });
26761         
26762         this.setPosition(this.gMapContext.location);
26763         
26764         this.fireEvent('initial', this, this.gMapContext.location);
26765     },
26766     
26767     initOverlayView: function()
26768     {
26769         var _this = this;
26770         
26771         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26772             
26773             draw: function()
26774             {
26775                 _this.fireEvent('OverlayViewDraw', _this);
26776             },
26777             
26778             onAdd: function()
26779             {
26780                 _this.fireEvent('OverlayViewOnAdd', _this);
26781             },
26782             
26783             onRemove: function()
26784             {
26785                 _this.fireEvent('OverlayViewOnRemove', _this);
26786             },
26787             
26788             show: function(cpx)
26789             {
26790                 _this.fireEvent('OverlayViewShow', _this, cpx);
26791             },
26792             
26793             hide: function()
26794             {
26795                 _this.fireEvent('OverlayViewHide', _this);
26796             }
26797             
26798         });
26799     },
26800     
26801     fromLatLngToContainerPixel: function(event)
26802     {
26803         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26804     },
26805     
26806     isApplied: function() 
26807     {
26808         return this.getGmapContext() == false ? false : true;
26809     },
26810     
26811     getGmapContext: function() 
26812     {
26813         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26814     },
26815     
26816     GMapContext: function() 
26817     {
26818         var position = new google.maps.LatLng(this.latitude, this.longitude);
26819         
26820         var _map = new google.maps.Map(this.el.dom, {
26821             center: position,
26822             zoom: this.zoom,
26823             mapTypeId: this.mapTypeId,
26824             mapTypeControl: this.mapTypeControl,
26825             disableDoubleClickZoom: this.disableDoubleClickZoom,
26826             scrollwheel: this.scrollwheel,
26827             streetViewControl: this.streetViewControl,
26828             locationName: this.locationName,
26829             draggable: this.draggable,
26830             enableAutocomplete: this.enableAutocomplete,
26831             enableReverseGeocode: this.enableReverseGeocode
26832         });
26833         
26834         var _marker = new google.maps.Marker({
26835             position: position,
26836             map: _map,
26837             title: this.markerTitle,
26838             draggable: this.draggable
26839         });
26840         
26841         return {
26842             map: _map,
26843             marker: _marker,
26844             circle: null,
26845             location: position,
26846             radius: this.radius,
26847             locationName: this.locationName,
26848             addressComponents: {
26849                 formatted_address: null,
26850                 addressLine1: null,
26851                 addressLine2: null,
26852                 streetName: null,
26853                 streetNumber: null,
26854                 city: null,
26855                 district: null,
26856                 state: null,
26857                 stateOrProvince: null
26858             },
26859             settings: this,
26860             domContainer: this.el.dom,
26861             geodecoder: new google.maps.Geocoder()
26862         };
26863     },
26864     
26865     drawCircle: function(center, radius, options) 
26866     {
26867         if (this.gMapContext.circle != null) {
26868             this.gMapContext.circle.setMap(null);
26869         }
26870         if (radius > 0) {
26871             radius *= 1;
26872             options = Roo.apply({}, options, {
26873                 strokeColor: "#0000FF",
26874                 strokeOpacity: .35,
26875                 strokeWeight: 2,
26876                 fillColor: "#0000FF",
26877                 fillOpacity: .2
26878             });
26879             
26880             options.map = this.gMapContext.map;
26881             options.radius = radius;
26882             options.center = center;
26883             this.gMapContext.circle = new google.maps.Circle(options);
26884             return this.gMapContext.circle;
26885         }
26886         
26887         return null;
26888     },
26889     
26890     setPosition: function(location) 
26891     {
26892         this.gMapContext.location = location;
26893         this.gMapContext.marker.setPosition(location);
26894         this.gMapContext.map.panTo(location);
26895         this.drawCircle(location, this.gMapContext.radius, {});
26896         
26897         var _this = this;
26898         
26899         if (this.gMapContext.settings.enableReverseGeocode) {
26900             this.gMapContext.geodecoder.geocode({
26901                 latLng: this.gMapContext.location
26902             }, function(results, status) {
26903                 
26904                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26905                     _this.gMapContext.locationName = results[0].formatted_address;
26906                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26907                     
26908                     _this.fireEvent('positionchanged', this, location);
26909                 }
26910             });
26911             
26912             return;
26913         }
26914         
26915         this.fireEvent('positionchanged', this, location);
26916     },
26917     
26918     resize: function()
26919     {
26920         google.maps.event.trigger(this.gMapContext.map, "resize");
26921         
26922         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26923         
26924         this.fireEvent('resize', this);
26925     },
26926     
26927     setPositionByLatLng: function(latitude, longitude)
26928     {
26929         this.setPosition(new google.maps.LatLng(latitude, longitude));
26930     },
26931     
26932     getCurrentPosition: function() 
26933     {
26934         return {
26935             latitude: this.gMapContext.location.lat(),
26936             longitude: this.gMapContext.location.lng()
26937         };
26938     },
26939     
26940     getAddressName: function() 
26941     {
26942         return this.gMapContext.locationName;
26943     },
26944     
26945     getAddressComponents: function() 
26946     {
26947         return this.gMapContext.addressComponents;
26948     },
26949     
26950     address_component_from_google_geocode: function(address_components) 
26951     {
26952         var result = {};
26953         
26954         for (var i = 0; i < address_components.length; i++) {
26955             var component = address_components[i];
26956             if (component.types.indexOf("postal_code") >= 0) {
26957                 result.postalCode = component.short_name;
26958             } else if (component.types.indexOf("street_number") >= 0) {
26959                 result.streetNumber = component.short_name;
26960             } else if (component.types.indexOf("route") >= 0) {
26961                 result.streetName = component.short_name;
26962             } else if (component.types.indexOf("neighborhood") >= 0) {
26963                 result.city = component.short_name;
26964             } else if (component.types.indexOf("locality") >= 0) {
26965                 result.city = component.short_name;
26966             } else if (component.types.indexOf("sublocality") >= 0) {
26967                 result.district = component.short_name;
26968             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26969                 result.stateOrProvince = component.short_name;
26970             } else if (component.types.indexOf("country") >= 0) {
26971                 result.country = component.short_name;
26972             }
26973         }
26974         
26975         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26976         result.addressLine2 = "";
26977         return result;
26978     },
26979     
26980     setZoomLevel: function(zoom)
26981     {
26982         this.gMapContext.map.setZoom(zoom);
26983     },
26984     
26985     show: function()
26986     {
26987         if(!this.el){
26988             return;
26989         }
26990         
26991         this.el.show();
26992         
26993         this.resize();
26994         
26995         this.fireEvent('show', this);
26996     },
26997     
26998     hide: function()
26999     {
27000         if(!this.el){
27001             return;
27002         }
27003         
27004         this.el.hide();
27005         
27006         this.fireEvent('hide', this);
27007     }
27008     
27009 });
27010
27011 Roo.apply(Roo.bootstrap.LocationPicker, {
27012     
27013     OverlayView : function(map, options)
27014     {
27015         options = options || {};
27016         
27017         this.setMap(map);
27018     }
27019     
27020     
27021 });/*
27022  * - LGPL
27023  *
27024  * Alert
27025  * 
27026  */
27027
27028 /**
27029  * @class Roo.bootstrap.Alert
27030  * @extends Roo.bootstrap.Component
27031  * Bootstrap Alert class
27032  * @cfg {String} title The title of alert
27033  * @cfg {String} html The content of alert
27034  * @cfg {String} weight (  success | info | warning | danger )
27035  * @cfg {String} faicon font-awesomeicon
27036  * 
27037  * @constructor
27038  * Create a new alert
27039  * @param {Object} config The config object
27040  */
27041
27042
27043 Roo.bootstrap.Alert = function(config){
27044     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27045     
27046 };
27047
27048 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27049     
27050     title: '',
27051     html: '',
27052     weight: false,
27053     faicon: false,
27054     
27055     getAutoCreate : function()
27056     {
27057         
27058         var cfg = {
27059             tag : 'div',
27060             cls : 'alert',
27061             cn : [
27062                 {
27063                     tag : 'i',
27064                     cls : 'roo-alert-icon'
27065                     
27066                 },
27067                 {
27068                     tag : 'b',
27069                     cls : 'roo-alert-title',
27070                     html : this.title
27071                 },
27072                 {
27073                     tag : 'span',
27074                     cls : 'roo-alert-text',
27075                     html : this.html
27076                 }
27077             ]
27078         };
27079         
27080         if(this.faicon){
27081             cfg.cn[0].cls += ' fa ' + this.faicon;
27082         }
27083         
27084         if(this.weight){
27085             cfg.cls += ' alert-' + this.weight;
27086         }
27087         
27088         return cfg;
27089     },
27090     
27091     initEvents: function() 
27092     {
27093         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27094     },
27095     
27096     setTitle : function(str)
27097     {
27098         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27099     },
27100     
27101     setText : function(str)
27102     {
27103         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27104     },
27105     
27106     setWeight : function(weight)
27107     {
27108         if(this.weight){
27109             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27110         }
27111         
27112         this.weight = weight;
27113         
27114         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27115     },
27116     
27117     setIcon : function(icon)
27118     {
27119         if(this.faicon){
27120             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27121         }
27122         
27123         this.faicon = icon;
27124         
27125         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27126     },
27127     
27128     hide: function() 
27129     {
27130         this.el.hide();   
27131     },
27132     
27133     show: function() 
27134     {  
27135         this.el.show();   
27136     }
27137     
27138 });
27139
27140  
27141 /*
27142 * Licence: LGPL
27143 */
27144
27145 /**
27146  * @class Roo.bootstrap.UploadCropbox
27147  * @extends Roo.bootstrap.Component
27148  * Bootstrap UploadCropbox class
27149  * @cfg {String} emptyText show when image has been loaded
27150  * @cfg {String} rotateNotify show when image too small to rotate
27151  * @cfg {Number} errorTimeout default 3000
27152  * @cfg {Number} minWidth default 300
27153  * @cfg {Number} minHeight default 300
27154  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27155  * @cfg {Boolean} isDocument (true|false) default false
27156  * @cfg {String} url action url
27157  * @cfg {String} paramName default 'imageUpload'
27158  * @cfg {String} method default POST
27159  * @cfg {Boolean} loadMask (true|false) default true
27160  * @cfg {Boolean} loadingText default 'Loading...'
27161  * 
27162  * @constructor
27163  * Create a new UploadCropbox
27164  * @param {Object} config The config object
27165  */
27166
27167 Roo.bootstrap.UploadCropbox = function(config){
27168     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27169     
27170     this.addEvents({
27171         /**
27172          * @event beforeselectfile
27173          * Fire before select file
27174          * @param {Roo.bootstrap.UploadCropbox} this
27175          */
27176         "beforeselectfile" : true,
27177         /**
27178          * @event initial
27179          * Fire after initEvent
27180          * @param {Roo.bootstrap.UploadCropbox} this
27181          */
27182         "initial" : true,
27183         /**
27184          * @event crop
27185          * Fire after initEvent
27186          * @param {Roo.bootstrap.UploadCropbox} this
27187          * @param {String} data
27188          */
27189         "crop" : true,
27190         /**
27191          * @event prepare
27192          * Fire when preparing the file data
27193          * @param {Roo.bootstrap.UploadCropbox} this
27194          * @param {Object} file
27195          */
27196         "prepare" : true,
27197         /**
27198          * @event exception
27199          * Fire when get exception
27200          * @param {Roo.bootstrap.UploadCropbox} this
27201          * @param {XMLHttpRequest} xhr
27202          */
27203         "exception" : true,
27204         /**
27205          * @event beforeloadcanvas
27206          * Fire before load the canvas
27207          * @param {Roo.bootstrap.UploadCropbox} this
27208          * @param {String} src
27209          */
27210         "beforeloadcanvas" : true,
27211         /**
27212          * @event trash
27213          * Fire when trash image
27214          * @param {Roo.bootstrap.UploadCropbox} this
27215          */
27216         "trash" : true,
27217         /**
27218          * @event download
27219          * Fire when download the image
27220          * @param {Roo.bootstrap.UploadCropbox} this
27221          */
27222         "download" : true,
27223         /**
27224          * @event footerbuttonclick
27225          * Fire when footerbuttonclick
27226          * @param {Roo.bootstrap.UploadCropbox} this
27227          * @param {String} type
27228          */
27229         "footerbuttonclick" : true,
27230         /**
27231          * @event resize
27232          * Fire when resize
27233          * @param {Roo.bootstrap.UploadCropbox} this
27234          */
27235         "resize" : true,
27236         /**
27237          * @event rotate
27238          * Fire when rotate the image
27239          * @param {Roo.bootstrap.UploadCropbox} this
27240          * @param {String} pos
27241          */
27242         "rotate" : true,
27243         /**
27244          * @event inspect
27245          * Fire when inspect the file
27246          * @param {Roo.bootstrap.UploadCropbox} this
27247          * @param {Object} file
27248          */
27249         "inspect" : true,
27250         /**
27251          * @event upload
27252          * Fire when xhr upload the file
27253          * @param {Roo.bootstrap.UploadCropbox} this
27254          * @param {Object} data
27255          */
27256         "upload" : true,
27257         /**
27258          * @event arrange
27259          * Fire when arrange the file data
27260          * @param {Roo.bootstrap.UploadCropbox} this
27261          * @param {Object} formData
27262          */
27263         "arrange" : true
27264     });
27265     
27266     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27267 };
27268
27269 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27270     
27271     emptyText : 'Click to upload image',
27272     rotateNotify : 'Image is too small to rotate',
27273     errorTimeout : 3000,
27274     scale : 0,
27275     baseScale : 1,
27276     rotate : 0,
27277     dragable : false,
27278     pinching : false,
27279     mouseX : 0,
27280     mouseY : 0,
27281     cropData : false,
27282     minWidth : 300,
27283     minHeight : 300,
27284     file : false,
27285     exif : {},
27286     baseRotate : 1,
27287     cropType : 'image/jpeg',
27288     buttons : false,
27289     canvasLoaded : false,
27290     isDocument : false,
27291     method : 'POST',
27292     paramName : 'imageUpload',
27293     loadMask : true,
27294     loadingText : 'Loading...',
27295     maskEl : false,
27296     
27297     getAutoCreate : function()
27298     {
27299         var cfg = {
27300             tag : 'div',
27301             cls : 'roo-upload-cropbox',
27302             cn : [
27303                 {
27304                     tag : 'input',
27305                     cls : 'roo-upload-cropbox-selector',
27306                     type : 'file'
27307                 },
27308                 {
27309                     tag : 'div',
27310                     cls : 'roo-upload-cropbox-body',
27311                     style : 'cursor:pointer',
27312                     cn : [
27313                         {
27314                             tag : 'div',
27315                             cls : 'roo-upload-cropbox-preview'
27316                         },
27317                         {
27318                             tag : 'div',
27319                             cls : 'roo-upload-cropbox-thumb'
27320                         },
27321                         {
27322                             tag : 'div',
27323                             cls : 'roo-upload-cropbox-empty-notify',
27324                             html : this.emptyText
27325                         },
27326                         {
27327                             tag : 'div',
27328                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27329                             html : this.rotateNotify
27330                         }
27331                     ]
27332                 },
27333                 {
27334                     tag : 'div',
27335                     cls : 'roo-upload-cropbox-footer',
27336                     cn : {
27337                         tag : 'div',
27338                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27339                         cn : []
27340                     }
27341                 }
27342             ]
27343         };
27344         
27345         return cfg;
27346     },
27347     
27348     onRender : function(ct, position)
27349     {
27350         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27351         
27352         if (this.buttons.length) {
27353             
27354             Roo.each(this.buttons, function(bb) {
27355                 
27356                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27357                 
27358                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27359                 
27360             }, this);
27361         }
27362         
27363         if(this.loadMask){
27364             this.maskEl = this.el;
27365         }
27366     },
27367     
27368     initEvents : function()
27369     {
27370         this.urlAPI = (window.createObjectURL && window) || 
27371                                 (window.URL && URL.revokeObjectURL && URL) || 
27372                                 (window.webkitURL && webkitURL);
27373                         
27374         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27375         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27376         
27377         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27378         this.selectorEl.hide();
27379         
27380         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27381         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27382         
27383         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27384         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27385         this.thumbEl.hide();
27386         
27387         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27388         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27389         
27390         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27391         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27392         this.errorEl.hide();
27393         
27394         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27395         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27396         this.footerEl.hide();
27397         
27398         this.setThumbBoxSize();
27399         
27400         this.bind();
27401         
27402         this.resize();
27403         
27404         this.fireEvent('initial', this);
27405     },
27406
27407     bind : function()
27408     {
27409         var _this = this;
27410         
27411         window.addEventListener("resize", function() { _this.resize(); } );
27412         
27413         this.bodyEl.on('click', this.beforeSelectFile, this);
27414         
27415         if(Roo.isTouch){
27416             this.bodyEl.on('touchstart', this.onTouchStart, this);
27417             this.bodyEl.on('touchmove', this.onTouchMove, this);
27418             this.bodyEl.on('touchend', this.onTouchEnd, this);
27419         }
27420         
27421         if(!Roo.isTouch){
27422             this.bodyEl.on('mousedown', this.onMouseDown, this);
27423             this.bodyEl.on('mousemove', this.onMouseMove, this);
27424             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27425             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27426             Roo.get(document).on('mouseup', this.onMouseUp, this);
27427         }
27428         
27429         this.selectorEl.on('change', this.onFileSelected, this);
27430     },
27431     
27432     reset : function()
27433     {    
27434         this.scale = 0;
27435         this.baseScale = 1;
27436         this.rotate = 0;
27437         this.baseRotate = 1;
27438         this.dragable = false;
27439         this.pinching = false;
27440         this.mouseX = 0;
27441         this.mouseY = 0;
27442         this.cropData = false;
27443         this.notifyEl.dom.innerHTML = this.emptyText;
27444         
27445         this.selectorEl.dom.value = '';
27446         
27447     },
27448     
27449     resize : function()
27450     {
27451         if(this.fireEvent('resize', this) != false){
27452             this.setThumbBoxPosition();
27453             this.setCanvasPosition();
27454         }
27455     },
27456     
27457     onFooterButtonClick : function(e, el, o, type)
27458     {
27459         switch (type) {
27460             case 'rotate-left' :
27461                 this.onRotateLeft(e);
27462                 break;
27463             case 'rotate-right' :
27464                 this.onRotateRight(e);
27465                 break;
27466             case 'picture' :
27467                 this.beforeSelectFile(e);
27468                 break;
27469             case 'trash' :
27470                 this.trash(e);
27471                 break;
27472             case 'crop' :
27473                 this.crop(e);
27474                 break;
27475             case 'download' :
27476                 this.download(e);
27477                 break;
27478             default :
27479                 break;
27480         }
27481         
27482         this.fireEvent('footerbuttonclick', this, type);
27483     },
27484     
27485     beforeSelectFile : function(e)
27486     {
27487         e.preventDefault();
27488         
27489         if(this.fireEvent('beforeselectfile', this) != false){
27490             this.selectorEl.dom.click();
27491         }
27492     },
27493     
27494     onFileSelected : function(e)
27495     {
27496         e.preventDefault();
27497         
27498         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27499             return;
27500         }
27501         
27502         var file = this.selectorEl.dom.files[0];
27503         
27504         if(this.fireEvent('inspect', this, file) != false){
27505             this.prepare(file);
27506         }
27507         
27508     },
27509     
27510     trash : function(e)
27511     {
27512         this.fireEvent('trash', this);
27513     },
27514     
27515     download : function(e)
27516     {
27517         this.fireEvent('download', this);
27518     },
27519     
27520     loadCanvas : function(src)
27521     {   
27522         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27523             
27524             this.reset();
27525             
27526             this.imageEl = document.createElement('img');
27527             
27528             var _this = this;
27529             
27530             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27531             
27532             this.imageEl.src = src;
27533         }
27534     },
27535     
27536     onLoadCanvas : function()
27537     {   
27538         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27539         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27540         
27541         this.bodyEl.un('click', this.beforeSelectFile, this);
27542         
27543         this.notifyEl.hide();
27544         this.thumbEl.show();
27545         this.footerEl.show();
27546         
27547         this.baseRotateLevel();
27548         
27549         if(this.isDocument){
27550             this.setThumbBoxSize();
27551         }
27552         
27553         this.setThumbBoxPosition();
27554         
27555         this.baseScaleLevel();
27556         
27557         this.draw();
27558         
27559         this.resize();
27560         
27561         this.canvasLoaded = true;
27562         
27563         if(this.loadMask){
27564             this.maskEl.unmask();
27565         }
27566         
27567     },
27568     
27569     setCanvasPosition : function()
27570     {   
27571         if(!this.canvasEl){
27572             return;
27573         }
27574         
27575         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27576         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27577         
27578         this.previewEl.setLeft(pw);
27579         this.previewEl.setTop(ph);
27580         
27581     },
27582     
27583     onMouseDown : function(e)
27584     {   
27585         e.stopEvent();
27586         
27587         this.dragable = true;
27588         this.pinching = false;
27589         
27590         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27591             this.dragable = false;
27592             return;
27593         }
27594         
27595         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27596         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27597         
27598     },
27599     
27600     onMouseMove : function(e)
27601     {   
27602         e.stopEvent();
27603         
27604         if(!this.canvasLoaded){
27605             return;
27606         }
27607         
27608         if (!this.dragable){
27609             return;
27610         }
27611         
27612         var minX = Math.ceil(this.thumbEl.getLeft(true));
27613         var minY = Math.ceil(this.thumbEl.getTop(true));
27614         
27615         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27616         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27617         
27618         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27619         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27620         
27621         x = x - this.mouseX;
27622         y = y - this.mouseY;
27623         
27624         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27625         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27626         
27627         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27628         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27629         
27630         this.previewEl.setLeft(bgX);
27631         this.previewEl.setTop(bgY);
27632         
27633         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27634         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27635     },
27636     
27637     onMouseUp : function(e)
27638     {   
27639         e.stopEvent();
27640         
27641         this.dragable = false;
27642     },
27643     
27644     onMouseWheel : function(e)
27645     {   
27646         e.stopEvent();
27647         
27648         this.startScale = this.scale;
27649         
27650         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27651         
27652         if(!this.zoomable()){
27653             this.scale = this.startScale;
27654             return;
27655         }
27656         
27657         this.draw();
27658         
27659         return;
27660     },
27661     
27662     zoomable : function()
27663     {
27664         var minScale = this.thumbEl.getWidth() / this.minWidth;
27665         
27666         if(this.minWidth < this.minHeight){
27667             minScale = this.thumbEl.getHeight() / this.minHeight;
27668         }
27669         
27670         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27671         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27672         
27673         if(
27674                 this.isDocument &&
27675                 (this.rotate == 0 || this.rotate == 180) && 
27676                 (
27677                     width > this.imageEl.OriginWidth || 
27678                     height > this.imageEl.OriginHeight ||
27679                     (width < this.minWidth && height < this.minHeight)
27680                 )
27681         ){
27682             return false;
27683         }
27684         
27685         if(
27686                 this.isDocument &&
27687                 (this.rotate == 90 || this.rotate == 270) && 
27688                 (
27689                     width > this.imageEl.OriginWidth || 
27690                     height > this.imageEl.OriginHeight ||
27691                     (width < this.minHeight && height < this.minWidth)
27692                 )
27693         ){
27694             return false;
27695         }
27696         
27697         if(
27698                 !this.isDocument &&
27699                 (this.rotate == 0 || this.rotate == 180) && 
27700                 (
27701                     width < this.minWidth || 
27702                     width > this.imageEl.OriginWidth || 
27703                     height < this.minHeight || 
27704                     height > this.imageEl.OriginHeight
27705                 )
27706         ){
27707             return false;
27708         }
27709         
27710         if(
27711                 !this.isDocument &&
27712                 (this.rotate == 90 || this.rotate == 270) && 
27713                 (
27714                     width < this.minHeight || 
27715                     width > this.imageEl.OriginWidth || 
27716                     height < this.minWidth || 
27717                     height > this.imageEl.OriginHeight
27718                 )
27719         ){
27720             return false;
27721         }
27722         
27723         return true;
27724         
27725     },
27726     
27727     onRotateLeft : function(e)
27728     {   
27729         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27730             
27731             var minScale = this.thumbEl.getWidth() / this.minWidth;
27732             
27733             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27734             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27735             
27736             this.startScale = this.scale;
27737             
27738             while (this.getScaleLevel() < minScale){
27739             
27740                 this.scale = this.scale + 1;
27741                 
27742                 if(!this.zoomable()){
27743                     break;
27744                 }
27745                 
27746                 if(
27747                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27748                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27749                 ){
27750                     continue;
27751                 }
27752                 
27753                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27754
27755                 this.draw();
27756                 
27757                 return;
27758             }
27759             
27760             this.scale = this.startScale;
27761             
27762             this.onRotateFail();
27763             
27764             return false;
27765         }
27766         
27767         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27768
27769         if(this.isDocument){
27770             this.setThumbBoxSize();
27771             this.setThumbBoxPosition();
27772             this.setCanvasPosition();
27773         }
27774         
27775         this.draw();
27776         
27777         this.fireEvent('rotate', this, 'left');
27778         
27779     },
27780     
27781     onRotateRight : function(e)
27782     {
27783         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27784             
27785             var minScale = this.thumbEl.getWidth() / this.minWidth;
27786         
27787             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27788             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27789             
27790             this.startScale = this.scale;
27791             
27792             while (this.getScaleLevel() < minScale){
27793             
27794                 this.scale = this.scale + 1;
27795                 
27796                 if(!this.zoomable()){
27797                     break;
27798                 }
27799                 
27800                 if(
27801                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27802                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27803                 ){
27804                     continue;
27805                 }
27806                 
27807                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27808
27809                 this.draw();
27810                 
27811                 return;
27812             }
27813             
27814             this.scale = this.startScale;
27815             
27816             this.onRotateFail();
27817             
27818             return false;
27819         }
27820         
27821         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27822
27823         if(this.isDocument){
27824             this.setThumbBoxSize();
27825             this.setThumbBoxPosition();
27826             this.setCanvasPosition();
27827         }
27828         
27829         this.draw();
27830         
27831         this.fireEvent('rotate', this, 'right');
27832     },
27833     
27834     onRotateFail : function()
27835     {
27836         this.errorEl.show(true);
27837         
27838         var _this = this;
27839         
27840         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27841     },
27842     
27843     draw : function()
27844     {
27845         this.previewEl.dom.innerHTML = '';
27846         
27847         var canvasEl = document.createElement("canvas");
27848         
27849         var contextEl = canvasEl.getContext("2d");
27850         
27851         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27852         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27853         var center = this.imageEl.OriginWidth / 2;
27854         
27855         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27856             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27857             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27858             center = this.imageEl.OriginHeight / 2;
27859         }
27860         
27861         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27862         
27863         contextEl.translate(center, center);
27864         contextEl.rotate(this.rotate * Math.PI / 180);
27865
27866         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27867         
27868         this.canvasEl = document.createElement("canvas");
27869         
27870         this.contextEl = this.canvasEl.getContext("2d");
27871         
27872         switch (this.rotate) {
27873             case 0 :
27874                 
27875                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27876                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27877                 
27878                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27879                 
27880                 break;
27881             case 90 : 
27882                 
27883                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27884                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27885                 
27886                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27887                     this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27888                     break;
27889                 }
27890                 
27891                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27892                 
27893                 break;
27894             case 180 :
27895                 
27896                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27897                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27898                 
27899                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27900                     this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27901                     break;
27902                 }
27903                 
27904                 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27905                 
27906                 break;
27907             case 270 :
27908                 
27909                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27910                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27911         
27912                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27913                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27914                     break;
27915                 }
27916                 
27917                 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27918                 
27919                 break;
27920             default : 
27921                 break;
27922         }
27923         
27924         this.previewEl.appendChild(this.canvasEl);
27925         
27926         this.setCanvasPosition();
27927     },
27928     
27929     crop : function()
27930     {
27931         if(!this.canvasLoaded){
27932             return;
27933         }
27934         
27935         var imageCanvas = document.createElement("canvas");
27936         
27937         var imageContext = imageCanvas.getContext("2d");
27938         
27939         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27940         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27941         
27942         var center = imageCanvas.width / 2;
27943         
27944         imageContext.translate(center, center);
27945         
27946         imageContext.rotate(this.rotate * Math.PI / 180);
27947         
27948         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27949         
27950         var canvas = document.createElement("canvas");
27951         
27952         var context = canvas.getContext("2d");
27953                 
27954         canvas.width = this.minWidth;
27955         canvas.height = this.minHeight;
27956
27957         switch (this.rotate) {
27958             case 0 :
27959                 
27960                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27961                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27962                 
27963                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27964                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27965                 
27966                 var targetWidth = this.minWidth - 2 * x;
27967                 var targetHeight = this.minHeight - 2 * y;
27968                 
27969                 var scale = 1;
27970                 
27971                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27972                     scale = targetWidth / width;
27973                 }
27974                 
27975                 if(x > 0 && y == 0){
27976                     scale = targetHeight / height;
27977                 }
27978                 
27979                 if(x > 0 && y > 0){
27980                     scale = targetWidth / width;
27981                     
27982                     if(width < height){
27983                         scale = targetHeight / height;
27984                     }
27985                 }
27986                 
27987                 context.scale(scale, scale);
27988                 
27989                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27990                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27991
27992                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27993                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27994
27995                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27996                 
27997                 break;
27998             case 90 : 
27999                 
28000                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28001                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28002                 
28003                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28004                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28005                 
28006                 var targetWidth = this.minWidth - 2 * x;
28007                 var targetHeight = this.minHeight - 2 * y;
28008                 
28009                 var scale = 1;
28010                 
28011                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28012                     scale = targetWidth / width;
28013                 }
28014                 
28015                 if(x > 0 && y == 0){
28016                     scale = targetHeight / height;
28017                 }
28018                 
28019                 if(x > 0 && y > 0){
28020                     scale = targetWidth / width;
28021                     
28022                     if(width < height){
28023                         scale = targetHeight / height;
28024                     }
28025                 }
28026                 
28027                 context.scale(scale, scale);
28028                 
28029                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28030                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28031
28032                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28033                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28034                 
28035                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28036                 
28037                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28038                 
28039                 break;
28040             case 180 :
28041                 
28042                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28043                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28044                 
28045                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28046                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28047                 
28048                 var targetWidth = this.minWidth - 2 * x;
28049                 var targetHeight = this.minHeight - 2 * y;
28050                 
28051                 var scale = 1;
28052                 
28053                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28054                     scale = targetWidth / width;
28055                 }
28056                 
28057                 if(x > 0 && y == 0){
28058                     scale = targetHeight / height;
28059                 }
28060                 
28061                 if(x > 0 && y > 0){
28062                     scale = targetWidth / width;
28063                     
28064                     if(width < height){
28065                         scale = targetHeight / height;
28066                     }
28067                 }
28068                 
28069                 context.scale(scale, scale);
28070                 
28071                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28072                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28073
28074                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28075                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28076
28077                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28078                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28079                 
28080                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28081                 
28082                 break;
28083             case 270 :
28084                 
28085                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28086                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28087                 
28088                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28089                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28090                 
28091                 var targetWidth = this.minWidth - 2 * x;
28092                 var targetHeight = this.minHeight - 2 * y;
28093                 
28094                 var scale = 1;
28095                 
28096                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28097                     scale = targetWidth / width;
28098                 }
28099                 
28100                 if(x > 0 && y == 0){
28101                     scale = targetHeight / height;
28102                 }
28103                 
28104                 if(x > 0 && y > 0){
28105                     scale = targetWidth / width;
28106                     
28107                     if(width < height){
28108                         scale = targetHeight / height;
28109                     }
28110                 }
28111                 
28112                 context.scale(scale, scale);
28113                 
28114                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28115                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28116
28117                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28118                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28119                 
28120                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28121                 
28122                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28123                 
28124                 break;
28125             default : 
28126                 break;
28127         }
28128         
28129         this.cropData = canvas.toDataURL(this.cropType);
28130         
28131         if(this.fireEvent('crop', this, this.cropData) !== false){
28132             this.process(this.file, this.cropData);
28133         }
28134         
28135         return;
28136         
28137     },
28138     
28139     setThumbBoxSize : function()
28140     {
28141         var width, height;
28142         
28143         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28144             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28145             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28146             
28147             this.minWidth = width;
28148             this.minHeight = height;
28149             
28150             if(this.rotate == 90 || this.rotate == 270){
28151                 this.minWidth = height;
28152                 this.minHeight = width;
28153             }
28154         }
28155         
28156         height = 300;
28157         width = Math.ceil(this.minWidth * height / this.minHeight);
28158         
28159         if(this.minWidth > this.minHeight){
28160             width = 300;
28161             height = Math.ceil(this.minHeight * width / this.minWidth);
28162         }
28163         
28164         this.thumbEl.setStyle({
28165             width : width + 'px',
28166             height : height + 'px'
28167         });
28168
28169         return;
28170             
28171     },
28172     
28173     setThumbBoxPosition : function()
28174     {
28175         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28176         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28177         
28178         this.thumbEl.setLeft(x);
28179         this.thumbEl.setTop(y);
28180         
28181     },
28182     
28183     baseRotateLevel : function()
28184     {
28185         this.baseRotate = 1;
28186         
28187         if(
28188                 typeof(this.exif) != 'undefined' &&
28189                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28190                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28191         ){
28192             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28193         }
28194         
28195         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28196         
28197     },
28198     
28199     baseScaleLevel : function()
28200     {
28201         var width, height;
28202         
28203         if(this.isDocument){
28204             
28205             if(this.baseRotate == 6 || this.baseRotate == 8){
28206             
28207                 height = this.thumbEl.getHeight();
28208                 this.baseScale = height / this.imageEl.OriginWidth;
28209
28210                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28211                     width = this.thumbEl.getWidth();
28212                     this.baseScale = width / this.imageEl.OriginHeight;
28213                 }
28214
28215                 return;
28216             }
28217
28218             height = this.thumbEl.getHeight();
28219             this.baseScale = height / this.imageEl.OriginHeight;
28220
28221             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28222                 width = this.thumbEl.getWidth();
28223                 this.baseScale = width / this.imageEl.OriginWidth;
28224             }
28225
28226             return;
28227         }
28228         
28229         if(this.baseRotate == 6 || this.baseRotate == 8){
28230             
28231             width = this.thumbEl.getHeight();
28232             this.baseScale = width / this.imageEl.OriginHeight;
28233             
28234             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28235                 height = this.thumbEl.getWidth();
28236                 this.baseScale = height / this.imageEl.OriginHeight;
28237             }
28238             
28239             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28240                 height = this.thumbEl.getWidth();
28241                 this.baseScale = height / this.imageEl.OriginHeight;
28242                 
28243                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28244                     width = this.thumbEl.getHeight();
28245                     this.baseScale = width / this.imageEl.OriginWidth;
28246                 }
28247             }
28248             
28249             return;
28250         }
28251         
28252         width = this.thumbEl.getWidth();
28253         this.baseScale = width / this.imageEl.OriginWidth;
28254         
28255         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28256             height = this.thumbEl.getHeight();
28257             this.baseScale = height / this.imageEl.OriginHeight;
28258         }
28259         
28260         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28261             
28262             height = this.thumbEl.getHeight();
28263             this.baseScale = height / this.imageEl.OriginHeight;
28264             
28265             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28266                 width = this.thumbEl.getWidth();
28267                 this.baseScale = width / this.imageEl.OriginWidth;
28268             }
28269             
28270         }
28271         
28272         return;
28273     },
28274     
28275     getScaleLevel : function()
28276     {
28277         return this.baseScale * Math.pow(1.1, this.scale);
28278     },
28279     
28280     onTouchStart : function(e)
28281     {
28282         if(!this.canvasLoaded){
28283             this.beforeSelectFile(e);
28284             return;
28285         }
28286         
28287         var touches = e.browserEvent.touches;
28288         
28289         if(!touches){
28290             return;
28291         }
28292         
28293         if(touches.length == 1){
28294             this.onMouseDown(e);
28295             return;
28296         }
28297         
28298         if(touches.length != 2){
28299             return;
28300         }
28301         
28302         var coords = [];
28303         
28304         for(var i = 0, finger; finger = touches[i]; i++){
28305             coords.push(finger.pageX, finger.pageY);
28306         }
28307         
28308         var x = Math.pow(coords[0] - coords[2], 2);
28309         var y = Math.pow(coords[1] - coords[3], 2);
28310         
28311         this.startDistance = Math.sqrt(x + y);
28312         
28313         this.startScale = this.scale;
28314         
28315         this.pinching = true;
28316         this.dragable = false;
28317         
28318     },
28319     
28320     onTouchMove : function(e)
28321     {
28322         if(!this.pinching && !this.dragable){
28323             return;
28324         }
28325         
28326         var touches = e.browserEvent.touches;
28327         
28328         if(!touches){
28329             return;
28330         }
28331         
28332         if(this.dragable){
28333             this.onMouseMove(e);
28334             return;
28335         }
28336         
28337         var coords = [];
28338         
28339         for(var i = 0, finger; finger = touches[i]; i++){
28340             coords.push(finger.pageX, finger.pageY);
28341         }
28342         
28343         var x = Math.pow(coords[0] - coords[2], 2);
28344         var y = Math.pow(coords[1] - coords[3], 2);
28345         
28346         this.endDistance = Math.sqrt(x + y);
28347         
28348         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28349         
28350         if(!this.zoomable()){
28351             this.scale = this.startScale;
28352             return;
28353         }
28354         
28355         this.draw();
28356         
28357     },
28358     
28359     onTouchEnd : function(e)
28360     {
28361         this.pinching = false;
28362         this.dragable = false;
28363         
28364     },
28365     
28366     process : function(file, crop)
28367     {
28368         if(this.loadMask){
28369             this.maskEl.mask(this.loadingText);
28370         }
28371         
28372         this.xhr = new XMLHttpRequest();
28373         
28374         file.xhr = this.xhr;
28375
28376         this.xhr.open(this.method, this.url, true);
28377         
28378         var headers = {
28379             "Accept": "application/json",
28380             "Cache-Control": "no-cache",
28381             "X-Requested-With": "XMLHttpRequest"
28382         };
28383         
28384         for (var headerName in headers) {
28385             var headerValue = headers[headerName];
28386             if (headerValue) {
28387                 this.xhr.setRequestHeader(headerName, headerValue);
28388             }
28389         }
28390         
28391         var _this = this;
28392         
28393         this.xhr.onload = function()
28394         {
28395             _this.xhrOnLoad(_this.xhr);
28396         }
28397         
28398         this.xhr.onerror = function()
28399         {
28400             _this.xhrOnError(_this.xhr);
28401         }
28402         
28403         var formData = new FormData();
28404
28405         formData.append('returnHTML', 'NO');
28406         
28407         if(crop){
28408             formData.append('crop', crop);
28409         }
28410         
28411         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28412             formData.append(this.paramName, file, file.name);
28413         }
28414         
28415         if(typeof(file.filename) != 'undefined'){
28416             formData.append('filename', file.filename);
28417         }
28418         
28419         if(typeof(file.mimetype) != 'undefined'){
28420             formData.append('mimetype', file.mimetype);
28421         }
28422         
28423         if(this.fireEvent('arrange', this, formData) != false){
28424             this.xhr.send(formData);
28425         };
28426     },
28427     
28428     xhrOnLoad : function(xhr)
28429     {
28430         if(this.loadMask){
28431             this.maskEl.unmask();
28432         }
28433         
28434         if (xhr.readyState !== 4) {
28435             this.fireEvent('exception', this, xhr);
28436             return;
28437         }
28438
28439         var response = Roo.decode(xhr.responseText);
28440         
28441         if(!response.success){
28442             this.fireEvent('exception', this, xhr);
28443             return;
28444         }
28445         
28446         var response = Roo.decode(xhr.responseText);
28447         
28448         this.fireEvent('upload', this, response);
28449         
28450     },
28451     
28452     xhrOnError : function()
28453     {
28454         if(this.loadMask){
28455             this.maskEl.unmask();
28456         }
28457         
28458         Roo.log('xhr on error');
28459         
28460         var response = Roo.decode(xhr.responseText);
28461           
28462         Roo.log(response);
28463         
28464     },
28465     
28466     prepare : function(file)
28467     {   
28468         if(this.loadMask){
28469             this.maskEl.mask(this.loadingText);
28470         }
28471         
28472         this.file = false;
28473         this.exif = {};
28474         
28475         if(typeof(file) === 'string'){
28476             this.loadCanvas(file);
28477             return;
28478         }
28479         
28480         if(!file || !this.urlAPI){
28481             return;
28482         }
28483         
28484         this.file = file;
28485         this.cropType = file.type;
28486         
28487         var _this = this;
28488         
28489         if(this.fireEvent('prepare', this, this.file) != false){
28490             
28491             var reader = new FileReader();
28492             
28493             reader.onload = function (e) {
28494                 if (e.target.error) {
28495                     Roo.log(e.target.error);
28496                     return;
28497                 }
28498                 
28499                 var buffer = e.target.result,
28500                     dataView = new DataView(buffer),
28501                     offset = 2,
28502                     maxOffset = dataView.byteLength - 4,
28503                     markerBytes,
28504                     markerLength;
28505                 
28506                 if (dataView.getUint16(0) === 0xffd8) {
28507                     while (offset < maxOffset) {
28508                         markerBytes = dataView.getUint16(offset);
28509                         
28510                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28511                             markerLength = dataView.getUint16(offset + 2) + 2;
28512                             if (offset + markerLength > dataView.byteLength) {
28513                                 Roo.log('Invalid meta data: Invalid segment size.');
28514                                 break;
28515                             }
28516                             
28517                             if(markerBytes == 0xffe1){
28518                                 _this.parseExifData(
28519                                     dataView,
28520                                     offset,
28521                                     markerLength
28522                                 );
28523                             }
28524                             
28525                             offset += markerLength;
28526                             
28527                             continue;
28528                         }
28529                         
28530                         break;
28531                     }
28532                     
28533                 }
28534                 
28535                 var url = _this.urlAPI.createObjectURL(_this.file);
28536                 
28537                 _this.loadCanvas(url);
28538                 
28539                 return;
28540             }
28541             
28542             reader.readAsArrayBuffer(this.file);
28543             
28544         }
28545         
28546     },
28547     
28548     parseExifData : function(dataView, offset, length)
28549     {
28550         var tiffOffset = offset + 10,
28551             littleEndian,
28552             dirOffset;
28553     
28554         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28555             // No Exif data, might be XMP data instead
28556             return;
28557         }
28558         
28559         // Check for the ASCII code for "Exif" (0x45786966):
28560         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28561             // No Exif data, might be XMP data instead
28562             return;
28563         }
28564         if (tiffOffset + 8 > dataView.byteLength) {
28565             Roo.log('Invalid Exif data: Invalid segment size.');
28566             return;
28567         }
28568         // Check for the two null bytes:
28569         if (dataView.getUint16(offset + 8) !== 0x0000) {
28570             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28571             return;
28572         }
28573         // Check the byte alignment:
28574         switch (dataView.getUint16(tiffOffset)) {
28575         case 0x4949:
28576             littleEndian = true;
28577             break;
28578         case 0x4D4D:
28579             littleEndian = false;
28580             break;
28581         default:
28582             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28583             return;
28584         }
28585         // Check for the TIFF tag marker (0x002A):
28586         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28587             Roo.log('Invalid Exif data: Missing TIFF marker.');
28588             return;
28589         }
28590         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28591         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28592         
28593         this.parseExifTags(
28594             dataView,
28595             tiffOffset,
28596             tiffOffset + dirOffset,
28597             littleEndian
28598         );
28599     },
28600     
28601     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28602     {
28603         var tagsNumber,
28604             dirEndOffset,
28605             i;
28606         if (dirOffset + 6 > dataView.byteLength) {
28607             Roo.log('Invalid Exif data: Invalid directory offset.');
28608             return;
28609         }
28610         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28611         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28612         if (dirEndOffset + 4 > dataView.byteLength) {
28613             Roo.log('Invalid Exif data: Invalid directory size.');
28614             return;
28615         }
28616         for (i = 0; i < tagsNumber; i += 1) {
28617             this.parseExifTag(
28618                 dataView,
28619                 tiffOffset,
28620                 dirOffset + 2 + 12 * i, // tag offset
28621                 littleEndian
28622             );
28623         }
28624         // Return the offset to the next directory:
28625         return dataView.getUint32(dirEndOffset, littleEndian);
28626     },
28627     
28628     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28629     {
28630         var tag = dataView.getUint16(offset, littleEndian);
28631         
28632         this.exif[tag] = this.getExifValue(
28633             dataView,
28634             tiffOffset,
28635             offset,
28636             dataView.getUint16(offset + 2, littleEndian), // tag type
28637             dataView.getUint32(offset + 4, littleEndian), // tag length
28638             littleEndian
28639         );
28640     },
28641     
28642     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28643     {
28644         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28645             tagSize,
28646             dataOffset,
28647             values,
28648             i,
28649             str,
28650             c;
28651     
28652         if (!tagType) {
28653             Roo.log('Invalid Exif data: Invalid tag type.');
28654             return;
28655         }
28656         
28657         tagSize = tagType.size * length;
28658         // Determine if the value is contained in the dataOffset bytes,
28659         // or if the value at the dataOffset is a pointer to the actual data:
28660         dataOffset = tagSize > 4 ?
28661                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28662         if (dataOffset + tagSize > dataView.byteLength) {
28663             Roo.log('Invalid Exif data: Invalid data offset.');
28664             return;
28665         }
28666         if (length === 1) {
28667             return tagType.getValue(dataView, dataOffset, littleEndian);
28668         }
28669         values = [];
28670         for (i = 0; i < length; i += 1) {
28671             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28672         }
28673         
28674         if (tagType.ascii) {
28675             str = '';
28676             // Concatenate the chars:
28677             for (i = 0; i < values.length; i += 1) {
28678                 c = values[i];
28679                 // Ignore the terminating NULL byte(s):
28680                 if (c === '\u0000') {
28681                     break;
28682                 }
28683                 str += c;
28684             }
28685             return str;
28686         }
28687         return values;
28688     }
28689     
28690 });
28691
28692 Roo.apply(Roo.bootstrap.UploadCropbox, {
28693     tags : {
28694         'Orientation': 0x0112
28695     },
28696     
28697     Orientation: {
28698             1: 0, //'top-left',
28699 //            2: 'top-right',
28700             3: 180, //'bottom-right',
28701 //            4: 'bottom-left',
28702 //            5: 'left-top',
28703             6: 90, //'right-top',
28704 //            7: 'right-bottom',
28705             8: 270 //'left-bottom'
28706     },
28707     
28708     exifTagTypes : {
28709         // byte, 8-bit unsigned int:
28710         1: {
28711             getValue: function (dataView, dataOffset) {
28712                 return dataView.getUint8(dataOffset);
28713             },
28714             size: 1
28715         },
28716         // ascii, 8-bit byte:
28717         2: {
28718             getValue: function (dataView, dataOffset) {
28719                 return String.fromCharCode(dataView.getUint8(dataOffset));
28720             },
28721             size: 1,
28722             ascii: true
28723         },
28724         // short, 16 bit int:
28725         3: {
28726             getValue: function (dataView, dataOffset, littleEndian) {
28727                 return dataView.getUint16(dataOffset, littleEndian);
28728             },
28729             size: 2
28730         },
28731         // long, 32 bit int:
28732         4: {
28733             getValue: function (dataView, dataOffset, littleEndian) {
28734                 return dataView.getUint32(dataOffset, littleEndian);
28735             },
28736             size: 4
28737         },
28738         // rational = two long values, first is numerator, second is denominator:
28739         5: {
28740             getValue: function (dataView, dataOffset, littleEndian) {
28741                 return dataView.getUint32(dataOffset, littleEndian) /
28742                     dataView.getUint32(dataOffset + 4, littleEndian);
28743             },
28744             size: 8
28745         },
28746         // slong, 32 bit signed int:
28747         9: {
28748             getValue: function (dataView, dataOffset, littleEndian) {
28749                 return dataView.getInt32(dataOffset, littleEndian);
28750             },
28751             size: 4
28752         },
28753         // srational, two slongs, first is numerator, second is denominator:
28754         10: {
28755             getValue: function (dataView, dataOffset, littleEndian) {
28756                 return dataView.getInt32(dataOffset, littleEndian) /
28757                     dataView.getInt32(dataOffset + 4, littleEndian);
28758             },
28759             size: 8
28760         }
28761     },
28762     
28763     footer : {
28764         STANDARD : [
28765             {
28766                 tag : 'div',
28767                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28768                 action : 'rotate-left',
28769                 cn : [
28770                     {
28771                         tag : 'button',
28772                         cls : 'btn btn-default',
28773                         html : '<i class="fa fa-undo"></i>'
28774                     }
28775                 ]
28776             },
28777             {
28778                 tag : 'div',
28779                 cls : 'btn-group roo-upload-cropbox-picture',
28780                 action : 'picture',
28781                 cn : [
28782                     {
28783                         tag : 'button',
28784                         cls : 'btn btn-default',
28785                         html : '<i class="fa fa-picture-o"></i>'
28786                     }
28787                 ]
28788             },
28789             {
28790                 tag : 'div',
28791                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28792                 action : 'rotate-right',
28793                 cn : [
28794                     {
28795                         tag : 'button',
28796                         cls : 'btn btn-default',
28797                         html : '<i class="fa fa-repeat"></i>'
28798                     }
28799                 ]
28800             }
28801         ],
28802         DOCUMENT : [
28803             {
28804                 tag : 'div',
28805                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28806                 action : 'rotate-left',
28807                 cn : [
28808                     {
28809                         tag : 'button',
28810                         cls : 'btn btn-default',
28811                         html : '<i class="fa fa-undo"></i>'
28812                     }
28813                 ]
28814             },
28815             {
28816                 tag : 'div',
28817                 cls : 'btn-group roo-upload-cropbox-download',
28818                 action : 'download',
28819                 cn : [
28820                     {
28821                         tag : 'button',
28822                         cls : 'btn btn-default',
28823                         html : '<i class="fa fa-download"></i>'
28824                     }
28825                 ]
28826             },
28827             {
28828                 tag : 'div',
28829                 cls : 'btn-group roo-upload-cropbox-crop',
28830                 action : 'crop',
28831                 cn : [
28832                     {
28833                         tag : 'button',
28834                         cls : 'btn btn-default',
28835                         html : '<i class="fa fa-crop"></i>'
28836                     }
28837                 ]
28838             },
28839             {
28840                 tag : 'div',
28841                 cls : 'btn-group roo-upload-cropbox-trash',
28842                 action : 'trash',
28843                 cn : [
28844                     {
28845                         tag : 'button',
28846                         cls : 'btn btn-default',
28847                         html : '<i class="fa fa-trash"></i>'
28848                     }
28849                 ]
28850             },
28851             {
28852                 tag : 'div',
28853                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28854                 action : 'rotate-right',
28855                 cn : [
28856                     {
28857                         tag : 'button',
28858                         cls : 'btn btn-default',
28859                         html : '<i class="fa fa-repeat"></i>'
28860                     }
28861                 ]
28862             }
28863         ],
28864         ROTATOR : [
28865             {
28866                 tag : 'div',
28867                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28868                 action : 'rotate-left',
28869                 cn : [
28870                     {
28871                         tag : 'button',
28872                         cls : 'btn btn-default',
28873                         html : '<i class="fa fa-undo"></i>'
28874                     }
28875                 ]
28876             },
28877             {
28878                 tag : 'div',
28879                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28880                 action : 'rotate-right',
28881                 cn : [
28882                     {
28883                         tag : 'button',
28884                         cls : 'btn btn-default',
28885                         html : '<i class="fa fa-repeat"></i>'
28886                     }
28887                 ]
28888             }
28889         ]
28890     }
28891 });
28892
28893 /*
28894 * Licence: LGPL
28895 */
28896
28897 /**
28898  * @class Roo.bootstrap.DocumentManager
28899  * @extends Roo.bootstrap.Component
28900  * Bootstrap DocumentManager class
28901  * @cfg {String} paramName default 'imageUpload'
28902  * @cfg {String} toolTipName default 'filename'
28903  * @cfg {String} method default POST
28904  * @cfg {String} url action url
28905  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28906  * @cfg {Boolean} multiple multiple upload default true
28907  * @cfg {Number} thumbSize default 300
28908  * @cfg {String} fieldLabel
28909  * @cfg {Number} labelWidth default 4
28910  * @cfg {String} labelAlign (left|top) default left
28911  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28912 * @cfg {Number} labellg set the width of label (1-12)
28913  * @cfg {Number} labelmd set the width of label (1-12)
28914  * @cfg {Number} labelsm set the width of label (1-12)
28915  * @cfg {Number} labelxs set the width of label (1-12)
28916  * 
28917  * @constructor
28918  * Create a new DocumentManager
28919  * @param {Object} config The config object
28920  */
28921
28922 Roo.bootstrap.DocumentManager = function(config){
28923     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28924     
28925     this.files = [];
28926     this.delegates = [];
28927     
28928     this.addEvents({
28929         /**
28930          * @event initial
28931          * Fire when initial the DocumentManager
28932          * @param {Roo.bootstrap.DocumentManager} this
28933          */
28934         "initial" : true,
28935         /**
28936          * @event inspect
28937          * inspect selected file
28938          * @param {Roo.bootstrap.DocumentManager} this
28939          * @param {File} file
28940          */
28941         "inspect" : true,
28942         /**
28943          * @event exception
28944          * Fire when xhr load exception
28945          * @param {Roo.bootstrap.DocumentManager} this
28946          * @param {XMLHttpRequest} xhr
28947          */
28948         "exception" : true,
28949         /**
28950          * @event afterupload
28951          * Fire when xhr load exception
28952          * @param {Roo.bootstrap.DocumentManager} this
28953          * @param {XMLHttpRequest} xhr
28954          */
28955         "afterupload" : true,
28956         /**
28957          * @event prepare
28958          * prepare the form data
28959          * @param {Roo.bootstrap.DocumentManager} this
28960          * @param {Object} formData
28961          */
28962         "prepare" : true,
28963         /**
28964          * @event remove
28965          * Fire when remove the file
28966          * @param {Roo.bootstrap.DocumentManager} this
28967          * @param {Object} file
28968          */
28969         "remove" : true,
28970         /**
28971          * @event refresh
28972          * Fire after refresh the file
28973          * @param {Roo.bootstrap.DocumentManager} this
28974          */
28975         "refresh" : true,
28976         /**
28977          * @event click
28978          * Fire after click the image
28979          * @param {Roo.bootstrap.DocumentManager} this
28980          * @param {Object} file
28981          */
28982         "click" : true,
28983         /**
28984          * @event edit
28985          * Fire when upload a image and editable set to true
28986          * @param {Roo.bootstrap.DocumentManager} this
28987          * @param {Object} file
28988          */
28989         "edit" : true,
28990         /**
28991          * @event beforeselectfile
28992          * Fire before select file
28993          * @param {Roo.bootstrap.DocumentManager} this
28994          */
28995         "beforeselectfile" : true,
28996         /**
28997          * @event process
28998          * Fire before process file
28999          * @param {Roo.bootstrap.DocumentManager} this
29000          * @param {Object} file
29001          */
29002         "process" : true,
29003         /**
29004          * @event previewrendered
29005          * Fire when preview rendered
29006          * @param {Roo.bootstrap.DocumentManager} this
29007          * @param {Object} file
29008          */
29009         "previewrendered" : true,
29010         /**
29011          */
29012         "previewResize" : true
29013         
29014     });
29015 };
29016
29017 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29018     
29019     boxes : 0,
29020     inputName : '',
29021     thumbSize : 300,
29022     multiple : true,
29023     files : false,
29024     method : 'POST',
29025     url : '',
29026     paramName : 'imageUpload',
29027     toolTipName : 'filename',
29028     fieldLabel : '',
29029     labelWidth : 4,
29030     labelAlign : 'left',
29031     editable : true,
29032     delegates : false,
29033     xhr : false, 
29034     
29035     labellg : 0,
29036     labelmd : 0,
29037     labelsm : 0,
29038     labelxs : 0,
29039     
29040     getAutoCreate : function()
29041     {   
29042         var managerWidget = {
29043             tag : 'div',
29044             cls : 'roo-document-manager',
29045             cn : [
29046                 {
29047                     tag : 'input',
29048                     cls : 'roo-document-manager-selector',
29049                     type : 'file'
29050                 },
29051                 {
29052                     tag : 'div',
29053                     cls : 'roo-document-manager-uploader',
29054                     cn : [
29055                         {
29056                             tag : 'div',
29057                             cls : 'roo-document-manager-upload-btn',
29058                             html : '<i class="fa fa-plus"></i>'
29059                         }
29060                     ]
29061                     
29062                 }
29063             ]
29064         };
29065         
29066         var content = [
29067             {
29068                 tag : 'div',
29069                 cls : 'column col-md-12',
29070                 cn : managerWidget
29071             }
29072         ];
29073         
29074         if(this.fieldLabel.length){
29075             
29076             content = [
29077                 {
29078                     tag : 'div',
29079                     cls : 'column col-md-12',
29080                     html : this.fieldLabel
29081                 },
29082                 {
29083                     tag : 'div',
29084                     cls : 'column col-md-12',
29085                     cn : managerWidget
29086                 }
29087             ];
29088
29089             if(this.labelAlign == 'left'){
29090                 content = [
29091                     {
29092                         tag : 'div',
29093                         cls : 'column',
29094                         html : this.fieldLabel
29095                     },
29096                     {
29097                         tag : 'div',
29098                         cls : 'column',
29099                         cn : managerWidget
29100                     }
29101                 ];
29102                 
29103                 if(this.labelWidth > 12){
29104                     content[0].style = "width: " + this.labelWidth + 'px';
29105                 }
29106
29107                 if(this.labelWidth < 13 && this.labelmd == 0){
29108                     this.labelmd = this.labelWidth;
29109                 }
29110
29111                 if(this.labellg > 0){
29112                     content[0].cls += ' col-lg-' + this.labellg;
29113                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29114                 }
29115
29116                 if(this.labelmd > 0){
29117                     content[0].cls += ' col-md-' + this.labelmd;
29118                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29119                 }
29120
29121                 if(this.labelsm > 0){
29122                     content[0].cls += ' col-sm-' + this.labelsm;
29123                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29124                 }
29125
29126                 if(this.labelxs > 0){
29127                     content[0].cls += ' col-xs-' + this.labelxs;
29128                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29129                 }
29130                 
29131             }
29132         }
29133         
29134         var cfg = {
29135             tag : 'div',
29136             cls : 'row clearfix',
29137             cn : content
29138         };
29139         
29140         return cfg;
29141         
29142     },
29143     
29144     initEvents : function()
29145     {
29146         this.managerEl = this.el.select('.roo-document-manager', true).first();
29147         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29148         
29149         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29150         this.selectorEl.hide();
29151         
29152         if(this.multiple){
29153             this.selectorEl.attr('multiple', 'multiple');
29154         }
29155         
29156         this.selectorEl.on('change', this.onFileSelected, this);
29157         
29158         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29159         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29160         
29161         this.uploader.on('click', this.onUploaderClick, this);
29162         
29163         this.renderProgressDialog();
29164         
29165         var _this = this;
29166         
29167         window.addEventListener("resize", function() { _this.refresh(); } );
29168         
29169         this.fireEvent('initial', this);
29170     },
29171     
29172     renderProgressDialog : function()
29173     {
29174         var _this = this;
29175         
29176         this.progressDialog = new Roo.bootstrap.Modal({
29177             cls : 'roo-document-manager-progress-dialog',
29178             allow_close : false,
29179             title : '',
29180             buttons : [
29181                 {
29182                     name  :'cancel',
29183                     weight : 'danger',
29184                     html : 'Cancel'
29185                 }
29186             ], 
29187             listeners : { 
29188                 btnclick : function() {
29189                     _this.uploadCancel();
29190                     this.hide();
29191                 }
29192             }
29193         });
29194          
29195         this.progressDialog.render(Roo.get(document.body));
29196          
29197         this.progress = new Roo.bootstrap.Progress({
29198             cls : 'roo-document-manager-progress',
29199             active : true,
29200             striped : true
29201         });
29202         
29203         this.progress.render(this.progressDialog.getChildContainer());
29204         
29205         this.progressBar = new Roo.bootstrap.ProgressBar({
29206             cls : 'roo-document-manager-progress-bar',
29207             aria_valuenow : 0,
29208             aria_valuemin : 0,
29209             aria_valuemax : 12,
29210             panel : 'success'
29211         });
29212         
29213         this.progressBar.render(this.progress.getChildContainer());
29214     },
29215     
29216     onUploaderClick : function(e)
29217     {
29218         e.preventDefault();
29219      
29220         if(this.fireEvent('beforeselectfile', this) != false){
29221             this.selectorEl.dom.click();
29222         }
29223         
29224     },
29225     
29226     onFileSelected : function(e)
29227     {
29228         e.preventDefault();
29229         
29230         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29231             return;
29232         }
29233         
29234         Roo.each(this.selectorEl.dom.files, function(file){
29235             if(this.fireEvent('inspect', this, file) != false){
29236                 this.files.push(file);
29237             }
29238         }, this);
29239         
29240         this.queue();
29241         
29242     },
29243     
29244     queue : function()
29245     {
29246         this.selectorEl.dom.value = '';
29247         
29248         if(!this.files || !this.files.length){
29249             return;
29250         }
29251         
29252         if(this.boxes > 0 && this.files.length > this.boxes){
29253             this.files = this.files.slice(0, this.boxes);
29254         }
29255         
29256         this.uploader.show();
29257         
29258         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29259             this.uploader.hide();
29260         }
29261         
29262         var _this = this;
29263         
29264         var files = [];
29265         
29266         var docs = [];
29267         
29268         Roo.each(this.files, function(file){
29269             
29270             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29271                 var f = this.renderPreview(file);
29272                 files.push(f);
29273                 return;
29274             }
29275             
29276             if(file.type.indexOf('image') != -1){
29277                 this.delegates.push(
29278                     (function(){
29279                         _this.process(file);
29280                     }).createDelegate(this)
29281                 );
29282         
29283                 return;
29284             }
29285             
29286             docs.push(
29287                 (function(){
29288                     _this.process(file);
29289                 }).createDelegate(this)
29290             );
29291             
29292         }, this);
29293         
29294         this.files = files;
29295         
29296         this.delegates = this.delegates.concat(docs);
29297         
29298         if(!this.delegates.length){
29299             this.refresh();
29300             return;
29301         }
29302         
29303         this.progressBar.aria_valuemax = this.delegates.length;
29304         
29305         this.arrange();
29306         
29307         return;
29308     },
29309     
29310     arrange : function()
29311     {
29312         if(!this.delegates.length){
29313             this.progressDialog.hide();
29314             this.refresh();
29315             return;
29316         }
29317         
29318         var delegate = this.delegates.shift();
29319         
29320         this.progressDialog.show();
29321         
29322         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29323         
29324         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29325         
29326         delegate();
29327     },
29328     
29329     refresh : function()
29330     {
29331         this.uploader.show();
29332         
29333         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29334             this.uploader.hide();
29335         }
29336         
29337         Roo.isTouch ? this.closable(false) : this.closable(true);
29338         
29339         this.fireEvent('refresh', this);
29340     },
29341     
29342     onRemove : function(e, el, o)
29343     {
29344         e.preventDefault();
29345         
29346         this.fireEvent('remove', this, o);
29347         
29348     },
29349     
29350     remove : function(o)
29351     {
29352         var files = [];
29353         
29354         Roo.each(this.files, function(file){
29355             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29356                 files.push(file);
29357                 return;
29358             }
29359
29360             o.target.remove();
29361
29362         }, this);
29363         
29364         this.files = files;
29365         
29366         this.refresh();
29367     },
29368     
29369     clear : function()
29370     {
29371         Roo.each(this.files, function(file){
29372             if(!file.target){
29373                 return;
29374             }
29375             
29376             file.target.remove();
29377
29378         }, this);
29379         
29380         this.files = [];
29381         
29382         this.refresh();
29383     },
29384     
29385     onClick : function(e, el, o)
29386     {
29387         e.preventDefault();
29388         
29389         this.fireEvent('click', this, o);
29390         
29391     },
29392     
29393     closable : function(closable)
29394     {
29395         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29396             
29397             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29398             
29399             if(closable){
29400                 el.show();
29401                 return;
29402             }
29403             
29404             el.hide();
29405             
29406         }, this);
29407     },
29408     
29409     xhrOnLoad : function(xhr)
29410     {
29411         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29412             el.remove();
29413         }, this);
29414         
29415         if (xhr.readyState !== 4) {
29416             this.arrange();
29417             this.fireEvent('exception', this, xhr);
29418             return;
29419         }
29420
29421         var response = Roo.decode(xhr.responseText);
29422         
29423         if(!response.success){
29424             this.arrange();
29425             this.fireEvent('exception', this, xhr);
29426             return;
29427         }
29428         
29429         var file = this.renderPreview(response.data);
29430         
29431         this.files.push(file);
29432         
29433         this.arrange();
29434         
29435         this.fireEvent('afterupload', this, xhr);
29436         
29437     },
29438     
29439     xhrOnError : function(xhr)
29440     {
29441         Roo.log('xhr on error');
29442         
29443         var response = Roo.decode(xhr.responseText);
29444           
29445         Roo.log(response);
29446         
29447         this.arrange();
29448     },
29449     
29450     process : function(file)
29451     {
29452         if(this.fireEvent('process', this, file) !== false){
29453             if(this.editable && file.type.indexOf('image') != -1){
29454                 this.fireEvent('edit', this, file);
29455                 return;
29456             }
29457
29458             this.uploadStart(file, false);
29459
29460             return;
29461         }
29462         
29463     },
29464     
29465     uploadStart : function(file, crop)
29466     {
29467         this.xhr = new XMLHttpRequest();
29468         
29469         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29470             this.arrange();
29471             return;
29472         }
29473         
29474         file.xhr = this.xhr;
29475             
29476         this.managerEl.createChild({
29477             tag : 'div',
29478             cls : 'roo-document-manager-loading',
29479             cn : [
29480                 {
29481                     tag : 'div',
29482                     tooltip : file.name,
29483                     cls : 'roo-document-manager-thumb',
29484                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29485                 }
29486             ]
29487
29488         });
29489
29490         this.xhr.open(this.method, this.url, true);
29491         
29492         var headers = {
29493             "Accept": "application/json",
29494             "Cache-Control": "no-cache",
29495             "X-Requested-With": "XMLHttpRequest"
29496         };
29497         
29498         for (var headerName in headers) {
29499             var headerValue = headers[headerName];
29500             if (headerValue) {
29501                 this.xhr.setRequestHeader(headerName, headerValue);
29502             }
29503         }
29504         
29505         var _this = this;
29506         
29507         this.xhr.onload = function()
29508         {
29509             _this.xhrOnLoad(_this.xhr);
29510         }
29511         
29512         this.xhr.onerror = function()
29513         {
29514             _this.xhrOnError(_this.xhr);
29515         }
29516         
29517         var formData = new FormData();
29518
29519         formData.append('returnHTML', 'NO');
29520         
29521         if(crop){
29522             formData.append('crop', crop);
29523         }
29524         
29525         formData.append(this.paramName, file, file.name);
29526         
29527         var options = {
29528             file : file, 
29529             manually : false
29530         };
29531         
29532         if(this.fireEvent('prepare', this, formData, options) != false){
29533             
29534             if(options.manually){
29535                 return;
29536             }
29537             
29538             this.xhr.send(formData);
29539             return;
29540         };
29541         
29542         this.uploadCancel();
29543     },
29544     
29545     uploadCancel : function()
29546     {
29547         if (this.xhr) {
29548             this.xhr.abort();
29549         }
29550         
29551         this.delegates = [];
29552         
29553         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29554             el.remove();
29555         }, this);
29556         
29557         this.arrange();
29558     },
29559     
29560     renderPreview : function(file)
29561     {
29562         if(typeof(file.target) != 'undefined' && file.target){
29563             return file;
29564         }
29565         
29566         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29567         
29568         var previewEl = this.managerEl.createChild({
29569             tag : 'div',
29570             cls : 'roo-document-manager-preview',
29571             cn : [
29572                 {
29573                     tag : 'div',
29574                     tooltip : file[this.toolTipName],
29575                     cls : 'roo-document-manager-thumb',
29576                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29577                 },
29578                 {
29579                     tag : 'button',
29580                     cls : 'close',
29581                     html : '<i class="fa fa-times-circle"></i>'
29582                 }
29583             ]
29584         });
29585
29586         var close = previewEl.select('button.close', true).first();
29587
29588         close.on('click', this.onRemove, this, file);
29589
29590         file.target = previewEl;
29591
29592         var image = previewEl.select('img', true).first();
29593         
29594         var _this = this;
29595         
29596         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29597         
29598         image.on('click', this.onClick, this, file);
29599         
29600         this.fireEvent('previewrendered', this, file);
29601         
29602         return file;
29603         
29604     },
29605     
29606     onPreviewLoad : function(file, image)
29607     {
29608         if(typeof(file.target) == 'undefined' || !file.target){
29609             return;
29610         }
29611         
29612         var width = image.dom.naturalWidth || image.dom.width;
29613         var height = image.dom.naturalHeight || image.dom.height;
29614         
29615         if(!this.previewResize) {
29616             return;
29617         }
29618         
29619         if(width > height){
29620             file.target.addClass('wide');
29621             return;
29622         }
29623         
29624         file.target.addClass('tall');
29625         return;
29626         
29627     },
29628     
29629     uploadFromSource : function(file, crop)
29630     {
29631         this.xhr = new XMLHttpRequest();
29632         
29633         this.managerEl.createChild({
29634             tag : 'div',
29635             cls : 'roo-document-manager-loading',
29636             cn : [
29637                 {
29638                     tag : 'div',
29639                     tooltip : file.name,
29640                     cls : 'roo-document-manager-thumb',
29641                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29642                 }
29643             ]
29644
29645         });
29646
29647         this.xhr.open(this.method, this.url, true);
29648         
29649         var headers = {
29650             "Accept": "application/json",
29651             "Cache-Control": "no-cache",
29652             "X-Requested-With": "XMLHttpRequest"
29653         };
29654         
29655         for (var headerName in headers) {
29656             var headerValue = headers[headerName];
29657             if (headerValue) {
29658                 this.xhr.setRequestHeader(headerName, headerValue);
29659             }
29660         }
29661         
29662         var _this = this;
29663         
29664         this.xhr.onload = function()
29665         {
29666             _this.xhrOnLoad(_this.xhr);
29667         }
29668         
29669         this.xhr.onerror = function()
29670         {
29671             _this.xhrOnError(_this.xhr);
29672         }
29673         
29674         var formData = new FormData();
29675
29676         formData.append('returnHTML', 'NO');
29677         
29678         formData.append('crop', crop);
29679         
29680         if(typeof(file.filename) != 'undefined'){
29681             formData.append('filename', file.filename);
29682         }
29683         
29684         if(typeof(file.mimetype) != 'undefined'){
29685             formData.append('mimetype', file.mimetype);
29686         }
29687         
29688         Roo.log(formData);
29689         
29690         if(this.fireEvent('prepare', this, formData) != false){
29691             this.xhr.send(formData);
29692         };
29693     }
29694 });
29695
29696 /*
29697 * Licence: LGPL
29698 */
29699
29700 /**
29701  * @class Roo.bootstrap.DocumentViewer
29702  * @extends Roo.bootstrap.Component
29703  * Bootstrap DocumentViewer class
29704  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29705  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29706  * 
29707  * @constructor
29708  * Create a new DocumentViewer
29709  * @param {Object} config The config object
29710  */
29711
29712 Roo.bootstrap.DocumentViewer = function(config){
29713     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29714     
29715     this.addEvents({
29716         /**
29717          * @event initial
29718          * Fire after initEvent
29719          * @param {Roo.bootstrap.DocumentViewer} this
29720          */
29721         "initial" : true,
29722         /**
29723          * @event click
29724          * Fire after click
29725          * @param {Roo.bootstrap.DocumentViewer} this
29726          */
29727         "click" : true,
29728         /**
29729          * @event download
29730          * Fire after download button
29731          * @param {Roo.bootstrap.DocumentViewer} this
29732          */
29733         "download" : true,
29734         /**
29735          * @event trash
29736          * Fire after trash button
29737          * @param {Roo.bootstrap.DocumentViewer} this
29738          */
29739         "trash" : true
29740         
29741     });
29742 };
29743
29744 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29745     
29746     showDownload : true,
29747     
29748     showTrash : true,
29749     
29750     getAutoCreate : function()
29751     {
29752         var cfg = {
29753             tag : 'div',
29754             cls : 'roo-document-viewer',
29755             cn : [
29756                 {
29757                     tag : 'div',
29758                     cls : 'roo-document-viewer-body',
29759                     cn : [
29760                         {
29761                             tag : 'div',
29762                             cls : 'roo-document-viewer-thumb',
29763                             cn : [
29764                                 {
29765                                     tag : 'img',
29766                                     cls : 'roo-document-viewer-image'
29767                                 }
29768                             ]
29769                         }
29770                     ]
29771                 },
29772                 {
29773                     tag : 'div',
29774                     cls : 'roo-document-viewer-footer',
29775                     cn : {
29776                         tag : 'div',
29777                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29778                         cn : [
29779                             {
29780                                 tag : 'div',
29781                                 cls : 'btn-group roo-document-viewer-download',
29782                                 cn : [
29783                                     {
29784                                         tag : 'button',
29785                                         cls : 'btn btn-default',
29786                                         html : '<i class="fa fa-download"></i>'
29787                                     }
29788                                 ]
29789                             },
29790                             {
29791                                 tag : 'div',
29792                                 cls : 'btn-group roo-document-viewer-trash',
29793                                 cn : [
29794                                     {
29795                                         tag : 'button',
29796                                         cls : 'btn btn-default',
29797                                         html : '<i class="fa fa-trash"></i>'
29798                                     }
29799                                 ]
29800                             }
29801                         ]
29802                     }
29803                 }
29804             ]
29805         };
29806         
29807         return cfg;
29808     },
29809     
29810     initEvents : function()
29811     {
29812         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29813         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29814         
29815         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29816         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29817         
29818         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29819         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29820         
29821         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29822         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29823         
29824         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29825         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29826         
29827         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29828         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29829         
29830         this.bodyEl.on('click', this.onClick, this);
29831         this.downloadBtn.on('click', this.onDownload, this);
29832         this.trashBtn.on('click', this.onTrash, this);
29833         
29834         this.downloadBtn.hide();
29835         this.trashBtn.hide();
29836         
29837         if(this.showDownload){
29838             this.downloadBtn.show();
29839         }
29840         
29841         if(this.showTrash){
29842             this.trashBtn.show();
29843         }
29844         
29845         if(!this.showDownload && !this.showTrash) {
29846             this.footerEl.hide();
29847         }
29848         
29849     },
29850     
29851     initial : function()
29852     {
29853         this.fireEvent('initial', this);
29854         
29855     },
29856     
29857     onClick : function(e)
29858     {
29859         e.preventDefault();
29860         
29861         this.fireEvent('click', this);
29862     },
29863     
29864     onDownload : function(e)
29865     {
29866         e.preventDefault();
29867         
29868         this.fireEvent('download', this);
29869     },
29870     
29871     onTrash : function(e)
29872     {
29873         e.preventDefault();
29874         
29875         this.fireEvent('trash', this);
29876     }
29877     
29878 });
29879 /*
29880  * - LGPL
29881  *
29882  * nav progress bar
29883  * 
29884  */
29885
29886 /**
29887  * @class Roo.bootstrap.NavProgressBar
29888  * @extends Roo.bootstrap.Component
29889  * Bootstrap NavProgressBar class
29890  * 
29891  * @constructor
29892  * Create a new nav progress bar
29893  * @param {Object} config The config object
29894  */
29895
29896 Roo.bootstrap.NavProgressBar = function(config){
29897     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29898
29899     this.bullets = this.bullets || [];
29900    
29901 //    Roo.bootstrap.NavProgressBar.register(this);
29902      this.addEvents({
29903         /**
29904              * @event changed
29905              * Fires when the active item changes
29906              * @param {Roo.bootstrap.NavProgressBar} this
29907              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29908              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29909          */
29910         'changed': true
29911      });
29912     
29913 };
29914
29915 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29916     
29917     bullets : [],
29918     barItems : [],
29919     
29920     getAutoCreate : function()
29921     {
29922         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29923         
29924         cfg = {
29925             tag : 'div',
29926             cls : 'roo-navigation-bar-group',
29927             cn : [
29928                 {
29929                     tag : 'div',
29930                     cls : 'roo-navigation-top-bar'
29931                 },
29932                 {
29933                     tag : 'div',
29934                     cls : 'roo-navigation-bullets-bar',
29935                     cn : [
29936                         {
29937                             tag : 'ul',
29938                             cls : 'roo-navigation-bar'
29939                         }
29940                     ]
29941                 },
29942                 
29943                 {
29944                     tag : 'div',
29945                     cls : 'roo-navigation-bottom-bar'
29946                 }
29947             ]
29948             
29949         };
29950         
29951         return cfg;
29952         
29953     },
29954     
29955     initEvents: function() 
29956     {
29957         
29958     },
29959     
29960     onRender : function(ct, position) 
29961     {
29962         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29963         
29964         if(this.bullets.length){
29965             Roo.each(this.bullets, function(b){
29966                this.addItem(b);
29967             }, this);
29968         }
29969         
29970         this.format();
29971         
29972     },
29973     
29974     addItem : function(cfg)
29975     {
29976         var item = new Roo.bootstrap.NavProgressItem(cfg);
29977         
29978         item.parentId = this.id;
29979         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29980         
29981         if(cfg.html){
29982             var top = new Roo.bootstrap.Element({
29983                 tag : 'div',
29984                 cls : 'roo-navigation-bar-text'
29985             });
29986             
29987             var bottom = new Roo.bootstrap.Element({
29988                 tag : 'div',
29989                 cls : 'roo-navigation-bar-text'
29990             });
29991             
29992             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29993             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29994             
29995             var topText = new Roo.bootstrap.Element({
29996                 tag : 'span',
29997                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29998             });
29999             
30000             var bottomText = new Roo.bootstrap.Element({
30001                 tag : 'span',
30002                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30003             });
30004             
30005             topText.onRender(top.el, null);
30006             bottomText.onRender(bottom.el, null);
30007             
30008             item.topEl = top;
30009             item.bottomEl = bottom;
30010         }
30011         
30012         this.barItems.push(item);
30013         
30014         return item;
30015     },
30016     
30017     getActive : function()
30018     {
30019         var active = false;
30020         
30021         Roo.each(this.barItems, function(v){
30022             
30023             if (!v.isActive()) {
30024                 return;
30025             }
30026             
30027             active = v;
30028             return false;
30029             
30030         });
30031         
30032         return active;
30033     },
30034     
30035     setActiveItem : function(item)
30036     {
30037         var prev = false;
30038         
30039         Roo.each(this.barItems, function(v){
30040             if (v.rid == item.rid) {
30041                 return ;
30042             }
30043             
30044             if (v.isActive()) {
30045                 v.setActive(false);
30046                 prev = v;
30047             }
30048         });
30049
30050         item.setActive(true);
30051         
30052         this.fireEvent('changed', this, item, prev);
30053     },
30054     
30055     getBarItem: function(rid)
30056     {
30057         var ret = false;
30058         
30059         Roo.each(this.barItems, function(e) {
30060             if (e.rid != rid) {
30061                 return;
30062             }
30063             
30064             ret =  e;
30065             return false;
30066         });
30067         
30068         return ret;
30069     },
30070     
30071     indexOfItem : function(item)
30072     {
30073         var index = false;
30074         
30075         Roo.each(this.barItems, function(v, i){
30076             
30077             if (v.rid != item.rid) {
30078                 return;
30079             }
30080             
30081             index = i;
30082             return false
30083         });
30084         
30085         return index;
30086     },
30087     
30088     setActiveNext : function()
30089     {
30090         var i = this.indexOfItem(this.getActive());
30091         
30092         if (i > this.barItems.length) {
30093             return;
30094         }
30095         
30096         this.setActiveItem(this.barItems[i+1]);
30097     },
30098     
30099     setActivePrev : function()
30100     {
30101         var i = this.indexOfItem(this.getActive());
30102         
30103         if (i  < 1) {
30104             return;
30105         }
30106         
30107         this.setActiveItem(this.barItems[i-1]);
30108     },
30109     
30110     format : function()
30111     {
30112         if(!this.barItems.length){
30113             return;
30114         }
30115      
30116         var width = 100 / this.barItems.length;
30117         
30118         Roo.each(this.barItems, function(i){
30119             i.el.setStyle('width', width + '%');
30120             i.topEl.el.setStyle('width', width + '%');
30121             i.bottomEl.el.setStyle('width', width + '%');
30122         }, this);
30123         
30124     }
30125     
30126 });
30127 /*
30128  * - LGPL
30129  *
30130  * Nav Progress Item
30131  * 
30132  */
30133
30134 /**
30135  * @class Roo.bootstrap.NavProgressItem
30136  * @extends Roo.bootstrap.Component
30137  * Bootstrap NavProgressItem class
30138  * @cfg {String} rid the reference id
30139  * @cfg {Boolean} active (true|false) Is item active default false
30140  * @cfg {Boolean} disabled (true|false) Is item active default false
30141  * @cfg {String} html
30142  * @cfg {String} position (top|bottom) text position default bottom
30143  * @cfg {String} icon show icon instead of number
30144  * 
30145  * @constructor
30146  * Create a new NavProgressItem
30147  * @param {Object} config The config object
30148  */
30149 Roo.bootstrap.NavProgressItem = function(config){
30150     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30151     this.addEvents({
30152         // raw events
30153         /**
30154          * @event click
30155          * The raw click event for the entire grid.
30156          * @param {Roo.bootstrap.NavProgressItem} this
30157          * @param {Roo.EventObject} e
30158          */
30159         "click" : true
30160     });
30161    
30162 };
30163
30164 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30165     
30166     rid : '',
30167     active : false,
30168     disabled : false,
30169     html : '',
30170     position : 'bottom',
30171     icon : false,
30172     
30173     getAutoCreate : function()
30174     {
30175         var iconCls = 'roo-navigation-bar-item-icon';
30176         
30177         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30178         
30179         var cfg = {
30180             tag: 'li',
30181             cls: 'roo-navigation-bar-item',
30182             cn : [
30183                 {
30184                     tag : 'i',
30185                     cls : iconCls
30186                 }
30187             ]
30188         };
30189         
30190         if(this.active){
30191             cfg.cls += ' active';
30192         }
30193         if(this.disabled){
30194             cfg.cls += ' disabled';
30195         }
30196         
30197         return cfg;
30198     },
30199     
30200     disable : function()
30201     {
30202         this.setDisabled(true);
30203     },
30204     
30205     enable : function()
30206     {
30207         this.setDisabled(false);
30208     },
30209     
30210     initEvents: function() 
30211     {
30212         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30213         
30214         this.iconEl.on('click', this.onClick, this);
30215     },
30216     
30217     onClick : function(e)
30218     {
30219         e.preventDefault();
30220         
30221         if(this.disabled){
30222             return;
30223         }
30224         
30225         if(this.fireEvent('click', this, e) === false){
30226             return;
30227         };
30228         
30229         this.parent().setActiveItem(this);
30230     },
30231     
30232     isActive: function () 
30233     {
30234         return this.active;
30235     },
30236     
30237     setActive : function(state)
30238     {
30239         if(this.active == state){
30240             return;
30241         }
30242         
30243         this.active = state;
30244         
30245         if (state) {
30246             this.el.addClass('active');
30247             return;
30248         }
30249         
30250         this.el.removeClass('active');
30251         
30252         return;
30253     },
30254     
30255     setDisabled : function(state)
30256     {
30257         if(this.disabled == state){
30258             return;
30259         }
30260         
30261         this.disabled = state;
30262         
30263         if (state) {
30264             this.el.addClass('disabled');
30265             return;
30266         }
30267         
30268         this.el.removeClass('disabled');
30269     },
30270     
30271     tooltipEl : function()
30272     {
30273         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30274     }
30275 });
30276  
30277
30278  /*
30279  * - LGPL
30280  *
30281  * FieldLabel
30282  * 
30283  */
30284
30285 /**
30286  * @class Roo.bootstrap.FieldLabel
30287  * @extends Roo.bootstrap.Component
30288  * Bootstrap FieldLabel class
30289  * @cfg {String} html contents of the element
30290  * @cfg {String} tag tag of the element default label
30291  * @cfg {String} cls class of the element
30292  * @cfg {String} target label target 
30293  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30294  * @cfg {String} invalidClass default "text-warning"
30295  * @cfg {String} validClass default "text-success"
30296  * @cfg {String} iconTooltip default "This field is required"
30297  * @cfg {String} indicatorpos (left|right) default left
30298  * 
30299  * @constructor
30300  * Create a new FieldLabel
30301  * @param {Object} config The config object
30302  */
30303
30304 Roo.bootstrap.FieldLabel = function(config){
30305     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30306     
30307     this.addEvents({
30308             /**
30309              * @event invalid
30310              * Fires after the field has been marked as invalid.
30311              * @param {Roo.form.FieldLabel} this
30312              * @param {String} msg The validation message
30313              */
30314             invalid : true,
30315             /**
30316              * @event valid
30317              * Fires after the field has been validated with no errors.
30318              * @param {Roo.form.FieldLabel} this
30319              */
30320             valid : true
30321         });
30322 };
30323
30324 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30325     
30326     tag: 'label',
30327     cls: '',
30328     html: '',
30329     target: '',
30330     allowBlank : true,
30331     invalidClass : 'has-warning',
30332     validClass : 'has-success',
30333     iconTooltip : 'This field is required',
30334     indicatorpos : 'left',
30335     
30336     getAutoCreate : function(){
30337         
30338         var cls = "";
30339         if (!this.allowBlank) {
30340             cls  = "visible";
30341         }
30342         
30343         var cfg = {
30344             tag : this.tag,
30345             cls : 'roo-bootstrap-field-label ' + this.cls,
30346             for : this.target,
30347             cn : [
30348                 {
30349                     tag : 'i',
30350                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30351                     tooltip : this.iconTooltip
30352                 },
30353                 {
30354                     tag : 'span',
30355                     html : this.html
30356                 }
30357             ] 
30358         };
30359         
30360         if(this.indicatorpos == 'right'){
30361             var cfg = {
30362                 tag : this.tag,
30363                 cls : 'roo-bootstrap-field-label ' + this.cls,
30364                 for : this.target,
30365                 cn : [
30366                     {
30367                         tag : 'span',
30368                         html : this.html
30369                     },
30370                     {
30371                         tag : 'i',
30372                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30373                         tooltip : this.iconTooltip
30374                     }
30375                 ] 
30376             };
30377         }
30378         
30379         return cfg;
30380     },
30381     
30382     initEvents: function() 
30383     {
30384         Roo.bootstrap.Element.superclass.initEvents.call(this);
30385         
30386         this.indicator = this.indicatorEl();
30387         
30388         if(this.indicator){
30389             this.indicator.removeClass('visible');
30390             this.indicator.addClass('invisible');
30391         }
30392         
30393         Roo.bootstrap.FieldLabel.register(this);
30394     },
30395     
30396     indicatorEl : function()
30397     {
30398         var indicator = this.el.select('i.roo-required-indicator',true).first();
30399         
30400         if(!indicator){
30401             return false;
30402         }
30403         
30404         return indicator;
30405         
30406     },
30407     
30408     /**
30409      * Mark this field as valid
30410      */
30411     markValid : function()
30412     {
30413         if(this.indicator){
30414             this.indicator.removeClass('visible');
30415             this.indicator.addClass('invisible');
30416         }
30417         
30418         this.el.removeClass(this.invalidClass);
30419         
30420         this.el.addClass(this.validClass);
30421         
30422         this.fireEvent('valid', this);
30423     },
30424     
30425     /**
30426      * Mark this field as invalid
30427      * @param {String} msg The validation message
30428      */
30429     markInvalid : function(msg)
30430     {
30431         if(this.indicator){
30432             this.indicator.removeClass('invisible');
30433             this.indicator.addClass('visible');
30434         }
30435         
30436         this.el.removeClass(this.validClass);
30437         
30438         this.el.addClass(this.invalidClass);
30439         
30440         this.fireEvent('invalid', this, msg);
30441     }
30442     
30443    
30444 });
30445
30446 Roo.apply(Roo.bootstrap.FieldLabel, {
30447     
30448     groups: {},
30449     
30450      /**
30451     * register a FieldLabel Group
30452     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30453     */
30454     register : function(label)
30455     {
30456         if(this.groups.hasOwnProperty(label.target)){
30457             return;
30458         }
30459      
30460         this.groups[label.target] = label;
30461         
30462     },
30463     /**
30464     * fetch a FieldLabel Group based on the target
30465     * @param {string} target
30466     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30467     */
30468     get: function(target) {
30469         if (typeof(this.groups[target]) == 'undefined') {
30470             return false;
30471         }
30472         
30473         return this.groups[target] ;
30474     }
30475 });
30476
30477  
30478
30479  /*
30480  * - LGPL
30481  *
30482  * page DateSplitField.
30483  * 
30484  */
30485
30486
30487 /**
30488  * @class Roo.bootstrap.DateSplitField
30489  * @extends Roo.bootstrap.Component
30490  * Bootstrap DateSplitField class
30491  * @cfg {string} fieldLabel - the label associated
30492  * @cfg {Number} labelWidth set the width of label (0-12)
30493  * @cfg {String} labelAlign (top|left)
30494  * @cfg {Boolean} dayAllowBlank (true|false) default false
30495  * @cfg {Boolean} monthAllowBlank (true|false) default false
30496  * @cfg {Boolean} yearAllowBlank (true|false) default false
30497  * @cfg {string} dayPlaceholder 
30498  * @cfg {string} monthPlaceholder
30499  * @cfg {string} yearPlaceholder
30500  * @cfg {string} dayFormat default 'd'
30501  * @cfg {string} monthFormat default 'm'
30502  * @cfg {string} yearFormat default 'Y'
30503  * @cfg {Number} labellg set the width of label (1-12)
30504  * @cfg {Number} labelmd set the width of label (1-12)
30505  * @cfg {Number} labelsm set the width of label (1-12)
30506  * @cfg {Number} labelxs set the width of label (1-12)
30507
30508  *     
30509  * @constructor
30510  * Create a new DateSplitField
30511  * @param {Object} config The config object
30512  */
30513
30514 Roo.bootstrap.DateSplitField = function(config){
30515     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30516     
30517     this.addEvents({
30518         // raw events
30519          /**
30520          * @event years
30521          * getting the data of years
30522          * @param {Roo.bootstrap.DateSplitField} this
30523          * @param {Object} years
30524          */
30525         "years" : true,
30526         /**
30527          * @event days
30528          * getting the data of days
30529          * @param {Roo.bootstrap.DateSplitField} this
30530          * @param {Object} days
30531          */
30532         "days" : true,
30533         /**
30534          * @event invalid
30535          * Fires after the field has been marked as invalid.
30536          * @param {Roo.form.Field} this
30537          * @param {String} msg The validation message
30538          */
30539         invalid : true,
30540        /**
30541          * @event valid
30542          * Fires after the field has been validated with no errors.
30543          * @param {Roo.form.Field} this
30544          */
30545         valid : true
30546     });
30547 };
30548
30549 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30550     
30551     fieldLabel : '',
30552     labelAlign : 'top',
30553     labelWidth : 3,
30554     dayAllowBlank : false,
30555     monthAllowBlank : false,
30556     yearAllowBlank : false,
30557     dayPlaceholder : '',
30558     monthPlaceholder : '',
30559     yearPlaceholder : '',
30560     dayFormat : 'd',
30561     monthFormat : 'm',
30562     yearFormat : 'Y',
30563     isFormField : true,
30564     labellg : 0,
30565     labelmd : 0,
30566     labelsm : 0,
30567     labelxs : 0,
30568     
30569     getAutoCreate : function()
30570     {
30571         var cfg = {
30572             tag : 'div',
30573             cls : 'row roo-date-split-field-group',
30574             cn : [
30575                 {
30576                     tag : 'input',
30577                     type : 'hidden',
30578                     cls : 'form-hidden-field roo-date-split-field-group-value',
30579                     name : this.name
30580                 }
30581             ]
30582         };
30583         
30584         var labelCls = 'col-md-12';
30585         var contentCls = 'col-md-4';
30586         
30587         if(this.fieldLabel){
30588             
30589             var label = {
30590                 tag : 'div',
30591                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30592                 cn : [
30593                     {
30594                         tag : 'label',
30595                         html : this.fieldLabel
30596                     }
30597                 ]
30598             };
30599             
30600             if(this.labelAlign == 'left'){
30601             
30602                 if(this.labelWidth > 12){
30603                     label.style = "width: " + this.labelWidth + 'px';
30604                 }
30605
30606                 if(this.labelWidth < 13 && this.labelmd == 0){
30607                     this.labelmd = this.labelWidth;
30608                 }
30609
30610                 if(this.labellg > 0){
30611                     labelCls = ' col-lg-' + this.labellg;
30612                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30613                 }
30614
30615                 if(this.labelmd > 0){
30616                     labelCls = ' col-md-' + this.labelmd;
30617                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30618                 }
30619
30620                 if(this.labelsm > 0){
30621                     labelCls = ' col-sm-' + this.labelsm;
30622                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30623                 }
30624
30625                 if(this.labelxs > 0){
30626                     labelCls = ' col-xs-' + this.labelxs;
30627                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30628                 }
30629             }
30630             
30631             label.cls += ' ' + labelCls;
30632             
30633             cfg.cn.push(label);
30634         }
30635         
30636         Roo.each(['day', 'month', 'year'], function(t){
30637             cfg.cn.push({
30638                 tag : 'div',
30639                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30640             });
30641         }, this);
30642         
30643         return cfg;
30644     },
30645     
30646     inputEl: function ()
30647     {
30648         return this.el.select('.roo-date-split-field-group-value', true).first();
30649     },
30650     
30651     onRender : function(ct, position) 
30652     {
30653         var _this = this;
30654         
30655         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30656         
30657         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30658         
30659         this.dayField = new Roo.bootstrap.ComboBox({
30660             allowBlank : this.dayAllowBlank,
30661             alwaysQuery : true,
30662             displayField : 'value',
30663             editable : false,
30664             fieldLabel : '',
30665             forceSelection : true,
30666             mode : 'local',
30667             placeholder : this.dayPlaceholder,
30668             selectOnFocus : true,
30669             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30670             triggerAction : 'all',
30671             typeAhead : true,
30672             valueField : 'value',
30673             store : new Roo.data.SimpleStore({
30674                 data : (function() {    
30675                     var days = [];
30676                     _this.fireEvent('days', _this, days);
30677                     return days;
30678                 })(),
30679                 fields : [ 'value' ]
30680             }),
30681             listeners : {
30682                 select : function (_self, record, index)
30683                 {
30684                     _this.setValue(_this.getValue());
30685                 }
30686             }
30687         });
30688
30689         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30690         
30691         this.monthField = new Roo.bootstrap.MonthField({
30692             after : '<i class=\"fa fa-calendar\"></i>',
30693             allowBlank : this.monthAllowBlank,
30694             placeholder : this.monthPlaceholder,
30695             readOnly : true,
30696             listeners : {
30697                 render : function (_self)
30698                 {
30699                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30700                         e.preventDefault();
30701                         _self.focus();
30702                     });
30703                 },
30704                 select : function (_self, oldvalue, newvalue)
30705                 {
30706                     _this.setValue(_this.getValue());
30707                 }
30708             }
30709         });
30710         
30711         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30712         
30713         this.yearField = new Roo.bootstrap.ComboBox({
30714             allowBlank : this.yearAllowBlank,
30715             alwaysQuery : true,
30716             displayField : 'value',
30717             editable : false,
30718             fieldLabel : '',
30719             forceSelection : true,
30720             mode : 'local',
30721             placeholder : this.yearPlaceholder,
30722             selectOnFocus : true,
30723             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30724             triggerAction : 'all',
30725             typeAhead : true,
30726             valueField : 'value',
30727             store : new Roo.data.SimpleStore({
30728                 data : (function() {
30729                     var years = [];
30730                     _this.fireEvent('years', _this, years);
30731                     return years;
30732                 })(),
30733                 fields : [ 'value' ]
30734             }),
30735             listeners : {
30736                 select : function (_self, record, index)
30737                 {
30738                     _this.setValue(_this.getValue());
30739                 }
30740             }
30741         });
30742
30743         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30744     },
30745     
30746     setValue : function(v, format)
30747     {
30748         this.inputEl.dom.value = v;
30749         
30750         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30751         
30752         var d = Date.parseDate(v, f);
30753         
30754         if(!d){
30755             this.validate();
30756             return;
30757         }
30758         
30759         this.setDay(d.format(this.dayFormat));
30760         this.setMonth(d.format(this.monthFormat));
30761         this.setYear(d.format(this.yearFormat));
30762         
30763         this.validate();
30764         
30765         return;
30766     },
30767     
30768     setDay : function(v)
30769     {
30770         this.dayField.setValue(v);
30771         this.inputEl.dom.value = this.getValue();
30772         this.validate();
30773         return;
30774     },
30775     
30776     setMonth : function(v)
30777     {
30778         this.monthField.setValue(v, true);
30779         this.inputEl.dom.value = this.getValue();
30780         this.validate();
30781         return;
30782     },
30783     
30784     setYear : function(v)
30785     {
30786         this.yearField.setValue(v);
30787         this.inputEl.dom.value = this.getValue();
30788         this.validate();
30789         return;
30790     },
30791     
30792     getDay : function()
30793     {
30794         return this.dayField.getValue();
30795     },
30796     
30797     getMonth : function()
30798     {
30799         return this.monthField.getValue();
30800     },
30801     
30802     getYear : function()
30803     {
30804         return this.yearField.getValue();
30805     },
30806     
30807     getValue : function()
30808     {
30809         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30810         
30811         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30812         
30813         return date;
30814     },
30815     
30816     reset : function()
30817     {
30818         this.setDay('');
30819         this.setMonth('');
30820         this.setYear('');
30821         this.inputEl.dom.value = '';
30822         this.validate();
30823         return;
30824     },
30825     
30826     validate : function()
30827     {
30828         var d = this.dayField.validate();
30829         var m = this.monthField.validate();
30830         var y = this.yearField.validate();
30831         
30832         var valid = true;
30833         
30834         if(
30835                 (!this.dayAllowBlank && !d) ||
30836                 (!this.monthAllowBlank && !m) ||
30837                 (!this.yearAllowBlank && !y)
30838         ){
30839             valid = false;
30840         }
30841         
30842         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30843             return valid;
30844         }
30845         
30846         if(valid){
30847             this.markValid();
30848             return valid;
30849         }
30850         
30851         this.markInvalid();
30852         
30853         return valid;
30854     },
30855     
30856     markValid : function()
30857     {
30858         
30859         var label = this.el.select('label', true).first();
30860         var icon = this.el.select('i.fa-star', true).first();
30861
30862         if(label && icon){
30863             icon.remove();
30864         }
30865         
30866         this.fireEvent('valid', this);
30867     },
30868     
30869      /**
30870      * Mark this field as invalid
30871      * @param {String} msg The validation message
30872      */
30873     markInvalid : function(msg)
30874     {
30875         
30876         var label = this.el.select('label', true).first();
30877         var icon = this.el.select('i.fa-star', true).first();
30878
30879         if(label && !icon){
30880             this.el.select('.roo-date-split-field-label', true).createChild({
30881                 tag : 'i',
30882                 cls : 'text-danger fa fa-lg fa-star',
30883                 tooltip : 'This field is required',
30884                 style : 'margin-right:5px;'
30885             }, label, true);
30886         }
30887         
30888         this.fireEvent('invalid', this, msg);
30889     },
30890     
30891     clearInvalid : function()
30892     {
30893         var label = this.el.select('label', true).first();
30894         var icon = this.el.select('i.fa-star', true).first();
30895
30896         if(label && icon){
30897             icon.remove();
30898         }
30899         
30900         this.fireEvent('valid', this);
30901     },
30902     
30903     getName: function()
30904     {
30905         return this.name;
30906     }
30907     
30908 });
30909
30910  /**
30911  *
30912  * This is based on 
30913  * http://masonry.desandro.com
30914  *
30915  * The idea is to render all the bricks based on vertical width...
30916  *
30917  * The original code extends 'outlayer' - we might need to use that....
30918  * 
30919  */
30920
30921
30922 /**
30923  * @class Roo.bootstrap.LayoutMasonry
30924  * @extends Roo.bootstrap.Component
30925  * Bootstrap Layout Masonry class
30926  * 
30927  * @constructor
30928  * Create a new Element
30929  * @param {Object} config The config object
30930  */
30931
30932 Roo.bootstrap.LayoutMasonry = function(config){
30933     
30934     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30935     
30936     this.bricks = [];
30937     
30938     Roo.bootstrap.LayoutMasonry.register(this);
30939     
30940     this.addEvents({
30941         // raw events
30942         /**
30943          * @event layout
30944          * Fire after layout the items
30945          * @param {Roo.bootstrap.LayoutMasonry} this
30946          * @param {Roo.EventObject} e
30947          */
30948         "layout" : true
30949     });
30950     
30951 };
30952
30953 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30954     
30955     /**
30956      * @cfg {Boolean} isLayoutInstant = no animation?
30957      */   
30958     isLayoutInstant : false, // needed?
30959    
30960     /**
30961      * @cfg {Number} boxWidth  width of the columns
30962      */   
30963     boxWidth : 450,
30964     
30965       /**
30966      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30967      */   
30968     boxHeight : 0,
30969     
30970     /**
30971      * @cfg {Number} padWidth padding below box..
30972      */   
30973     padWidth : 10, 
30974     
30975     /**
30976      * @cfg {Number} gutter gutter width..
30977      */   
30978     gutter : 10,
30979     
30980      /**
30981      * @cfg {Number} maxCols maximum number of columns
30982      */   
30983     
30984     maxCols: 0,
30985     
30986     /**
30987      * @cfg {Boolean} isAutoInitial defalut true
30988      */   
30989     isAutoInitial : true, 
30990     
30991     containerWidth: 0,
30992     
30993     /**
30994      * @cfg {Boolean} isHorizontal defalut false
30995      */   
30996     isHorizontal : false, 
30997
30998     currentSize : null,
30999     
31000     tag: 'div',
31001     
31002     cls: '',
31003     
31004     bricks: null, //CompositeElement
31005     
31006     cols : 1,
31007     
31008     _isLayoutInited : false,
31009     
31010 //    isAlternative : false, // only use for vertical layout...
31011     
31012     /**
31013      * @cfg {Number} alternativePadWidth padding below box..
31014      */   
31015     alternativePadWidth : 50,
31016     
31017     selectedBrick : [],
31018     
31019     getAutoCreate : function(){
31020         
31021         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31022         
31023         var cfg = {
31024             tag: this.tag,
31025             cls: 'blog-masonary-wrapper ' + this.cls,
31026             cn : {
31027                 cls : 'mas-boxes masonary'
31028             }
31029         };
31030         
31031         return cfg;
31032     },
31033     
31034     getChildContainer: function( )
31035     {
31036         if (this.boxesEl) {
31037             return this.boxesEl;
31038         }
31039         
31040         this.boxesEl = this.el.select('.mas-boxes').first();
31041         
31042         return this.boxesEl;
31043     },
31044     
31045     
31046     initEvents : function()
31047     {
31048         var _this = this;
31049         
31050         if(this.isAutoInitial){
31051             Roo.log('hook children rendered');
31052             this.on('childrenrendered', function() {
31053                 Roo.log('children rendered');
31054                 _this.initial();
31055             } ,this);
31056         }
31057     },
31058     
31059     initial : function()
31060     {
31061         this.selectedBrick = [];
31062         
31063         this.currentSize = this.el.getBox(true);
31064         
31065         Roo.EventManager.onWindowResize(this.resize, this); 
31066
31067         if(!this.isAutoInitial){
31068             this.layout();
31069             return;
31070         }
31071         
31072         this.layout();
31073         
31074         return;
31075         //this.layout.defer(500,this);
31076         
31077     },
31078     
31079     resize : function()
31080     {
31081         var cs = this.el.getBox(true);
31082         
31083         if (
31084                 this.currentSize.width == cs.width && 
31085                 this.currentSize.x == cs.x && 
31086                 this.currentSize.height == cs.height && 
31087                 this.currentSize.y == cs.y 
31088         ) {
31089             Roo.log("no change in with or X or Y");
31090             return;
31091         }
31092         
31093         this.currentSize = cs;
31094         
31095         this.layout();
31096         
31097     },
31098     
31099     layout : function()
31100     {   
31101         this._resetLayout();
31102         
31103         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31104         
31105         this.layoutItems( isInstant );
31106       
31107         this._isLayoutInited = true;
31108         
31109         this.fireEvent('layout', this);
31110         
31111     },
31112     
31113     _resetLayout : function()
31114     {
31115         if(this.isHorizontal){
31116             this.horizontalMeasureColumns();
31117             return;
31118         }
31119         
31120         this.verticalMeasureColumns();
31121         
31122     },
31123     
31124     verticalMeasureColumns : function()
31125     {
31126         this.getContainerWidth();
31127         
31128 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31129 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31130 //            return;
31131 //        }
31132         
31133         var boxWidth = this.boxWidth + this.padWidth;
31134         
31135         if(this.containerWidth < this.boxWidth){
31136             boxWidth = this.containerWidth
31137         }
31138         
31139         var containerWidth = this.containerWidth;
31140         
31141         var cols = Math.floor(containerWidth / boxWidth);
31142         
31143         this.cols = Math.max( cols, 1 );
31144         
31145         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31146         
31147         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31148         
31149         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31150         
31151         this.colWidth = boxWidth + avail - this.padWidth;
31152         
31153         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31154         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31155     },
31156     
31157     horizontalMeasureColumns : function()
31158     {
31159         this.getContainerWidth();
31160         
31161         var boxWidth = this.boxWidth;
31162         
31163         if(this.containerWidth < boxWidth){
31164             boxWidth = this.containerWidth;
31165         }
31166         
31167         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31168         
31169         this.el.setHeight(boxWidth);
31170         
31171     },
31172     
31173     getContainerWidth : function()
31174     {
31175         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31176     },
31177     
31178     layoutItems : function( isInstant )
31179     {
31180         Roo.log(this.bricks);
31181         
31182         var items = Roo.apply([], this.bricks);
31183         
31184         if(this.isHorizontal){
31185             this._horizontalLayoutItems( items , isInstant );
31186             return;
31187         }
31188         
31189 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31190 //            this._verticalAlternativeLayoutItems( items , isInstant );
31191 //            return;
31192 //        }
31193         
31194         this._verticalLayoutItems( items , isInstant );
31195         
31196     },
31197     
31198     _verticalLayoutItems : function ( items , isInstant)
31199     {
31200         if ( !items || !items.length ) {
31201             return;
31202         }
31203         
31204         var standard = [
31205             ['xs', 'xs', 'xs', 'tall'],
31206             ['xs', 'xs', 'tall'],
31207             ['xs', 'xs', 'sm'],
31208             ['xs', 'xs', 'xs'],
31209             ['xs', 'tall'],
31210             ['xs', 'sm'],
31211             ['xs', 'xs'],
31212             ['xs'],
31213             
31214             ['sm', 'xs', 'xs'],
31215             ['sm', 'xs'],
31216             ['sm'],
31217             
31218             ['tall', 'xs', 'xs', 'xs'],
31219             ['tall', 'xs', 'xs'],
31220             ['tall', 'xs'],
31221             ['tall']
31222             
31223         ];
31224         
31225         var queue = [];
31226         
31227         var boxes = [];
31228         
31229         var box = [];
31230         
31231         Roo.each(items, function(item, k){
31232             
31233             switch (item.size) {
31234                 // these layouts take up a full box,
31235                 case 'md' :
31236                 case 'md-left' :
31237                 case 'md-right' :
31238                 case 'wide' :
31239                     
31240                     if(box.length){
31241                         boxes.push(box);
31242                         box = [];
31243                     }
31244                     
31245                     boxes.push([item]);
31246                     
31247                     break;
31248                     
31249                 case 'xs' :
31250                 case 'sm' :
31251                 case 'tall' :
31252                     
31253                     box.push(item);
31254                     
31255                     break;
31256                 default :
31257                     break;
31258                     
31259             }
31260             
31261         }, this);
31262         
31263         if(box.length){
31264             boxes.push(box);
31265             box = [];
31266         }
31267         
31268         var filterPattern = function(box, length)
31269         {
31270             if(!box.length){
31271                 return;
31272             }
31273             
31274             var match = false;
31275             
31276             var pattern = box.slice(0, length);
31277             
31278             var format = [];
31279             
31280             Roo.each(pattern, function(i){
31281                 format.push(i.size);
31282             }, this);
31283             
31284             Roo.each(standard, function(s){
31285                 
31286                 if(String(s) != String(format)){
31287                     return;
31288                 }
31289                 
31290                 match = true;
31291                 return false;
31292                 
31293             }, this);
31294             
31295             if(!match && length == 1){
31296                 return;
31297             }
31298             
31299             if(!match){
31300                 filterPattern(box, length - 1);
31301                 return;
31302             }
31303                 
31304             queue.push(pattern);
31305
31306             box = box.slice(length, box.length);
31307
31308             filterPattern(box, 4);
31309
31310             return;
31311             
31312         }
31313         
31314         Roo.each(boxes, function(box, k){
31315             
31316             if(!box.length){
31317                 return;
31318             }
31319             
31320             if(box.length == 1){
31321                 queue.push(box);
31322                 return;
31323             }
31324             
31325             filterPattern(box, 4);
31326             
31327         }, this);
31328         
31329         this._processVerticalLayoutQueue( queue, isInstant );
31330         
31331     },
31332     
31333 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31334 //    {
31335 //        if ( !items || !items.length ) {
31336 //            return;
31337 //        }
31338 //
31339 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31340 //        
31341 //    },
31342     
31343     _horizontalLayoutItems : function ( items , isInstant)
31344     {
31345         if ( !items || !items.length || items.length < 3) {
31346             return;
31347         }
31348         
31349         items.reverse();
31350         
31351         var eItems = items.slice(0, 3);
31352         
31353         items = items.slice(3, items.length);
31354         
31355         var standard = [
31356             ['xs', 'xs', 'xs', 'wide'],
31357             ['xs', 'xs', 'wide'],
31358             ['xs', 'xs', 'sm'],
31359             ['xs', 'xs', 'xs'],
31360             ['xs', 'wide'],
31361             ['xs', 'sm'],
31362             ['xs', 'xs'],
31363             ['xs'],
31364             
31365             ['sm', 'xs', 'xs'],
31366             ['sm', 'xs'],
31367             ['sm'],
31368             
31369             ['wide', 'xs', 'xs', 'xs'],
31370             ['wide', 'xs', 'xs'],
31371             ['wide', 'xs'],
31372             ['wide'],
31373             
31374             ['wide-thin']
31375         ];
31376         
31377         var queue = [];
31378         
31379         var boxes = [];
31380         
31381         var box = [];
31382         
31383         Roo.each(items, function(item, k){
31384             
31385             switch (item.size) {
31386                 case 'md' :
31387                 case 'md-left' :
31388                 case 'md-right' :
31389                 case 'tall' :
31390                     
31391                     if(box.length){
31392                         boxes.push(box);
31393                         box = [];
31394                     }
31395                     
31396                     boxes.push([item]);
31397                     
31398                     break;
31399                     
31400                 case 'xs' :
31401                 case 'sm' :
31402                 case 'wide' :
31403                 case 'wide-thin' :
31404                     
31405                     box.push(item);
31406                     
31407                     break;
31408                 default :
31409                     break;
31410                     
31411             }
31412             
31413         }, this);
31414         
31415         if(box.length){
31416             boxes.push(box);
31417             box = [];
31418         }
31419         
31420         var filterPattern = function(box, length)
31421         {
31422             if(!box.length){
31423                 return;
31424             }
31425             
31426             var match = false;
31427             
31428             var pattern = box.slice(0, length);
31429             
31430             var format = [];
31431             
31432             Roo.each(pattern, function(i){
31433                 format.push(i.size);
31434             }, this);
31435             
31436             Roo.each(standard, function(s){
31437                 
31438                 if(String(s) != String(format)){
31439                     return;
31440                 }
31441                 
31442                 match = true;
31443                 return false;
31444                 
31445             }, this);
31446             
31447             if(!match && length == 1){
31448                 return;
31449             }
31450             
31451             if(!match){
31452                 filterPattern(box, length - 1);
31453                 return;
31454             }
31455                 
31456             queue.push(pattern);
31457
31458             box = box.slice(length, box.length);
31459
31460             filterPattern(box, 4);
31461
31462             return;
31463             
31464         }
31465         
31466         Roo.each(boxes, function(box, k){
31467             
31468             if(!box.length){
31469                 return;
31470             }
31471             
31472             if(box.length == 1){
31473                 queue.push(box);
31474                 return;
31475             }
31476             
31477             filterPattern(box, 4);
31478             
31479         }, this);
31480         
31481         
31482         var prune = [];
31483         
31484         var pos = this.el.getBox(true);
31485         
31486         var minX = pos.x;
31487         
31488         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31489         
31490         var hit_end = false;
31491         
31492         Roo.each(queue, function(box){
31493             
31494             if(hit_end){
31495                 
31496                 Roo.each(box, function(b){
31497                 
31498                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31499                     b.el.hide();
31500
31501                 }, this);
31502
31503                 return;
31504             }
31505             
31506             var mx = 0;
31507             
31508             Roo.each(box, function(b){
31509                 
31510                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31511                 b.el.show();
31512
31513                 mx = Math.max(mx, b.x);
31514                 
31515             }, this);
31516             
31517             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31518             
31519             if(maxX < minX){
31520                 
31521                 Roo.each(box, function(b){
31522                 
31523                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31524                     b.el.hide();
31525                     
31526                 }, this);
31527                 
31528                 hit_end = true;
31529                 
31530                 return;
31531             }
31532             
31533             prune.push(box);
31534             
31535         }, this);
31536         
31537         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31538     },
31539     
31540     /** Sets position of item in DOM
31541     * @param {Element} item
31542     * @param {Number} x - horizontal position
31543     * @param {Number} y - vertical position
31544     * @param {Boolean} isInstant - disables transitions
31545     */
31546     _processVerticalLayoutQueue : function( queue, isInstant )
31547     {
31548         var pos = this.el.getBox(true);
31549         var x = pos.x;
31550         var y = pos.y;
31551         var maxY = [];
31552         
31553         for (var i = 0; i < this.cols; i++){
31554             maxY[i] = pos.y;
31555         }
31556         
31557         Roo.each(queue, function(box, k){
31558             
31559             var col = k % this.cols;
31560             
31561             Roo.each(box, function(b,kk){
31562                 
31563                 b.el.position('absolute');
31564                 
31565                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31566                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31567                 
31568                 if(b.size == 'md-left' || b.size == 'md-right'){
31569                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31570                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31571                 }
31572                 
31573                 b.el.setWidth(width);
31574                 b.el.setHeight(height);
31575                 // iframe?
31576                 b.el.select('iframe',true).setSize(width,height);
31577                 
31578             }, this);
31579             
31580             for (var i = 0; i < this.cols; i++){
31581                 
31582                 if(maxY[i] < maxY[col]){
31583                     col = i;
31584                     continue;
31585                 }
31586                 
31587                 col = Math.min(col, i);
31588                 
31589             }
31590             
31591             x = pos.x + col * (this.colWidth + this.padWidth);
31592             
31593             y = maxY[col];
31594             
31595             var positions = [];
31596             
31597             switch (box.length){
31598                 case 1 :
31599                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31600                     break;
31601                 case 2 :
31602                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31603                     break;
31604                 case 3 :
31605                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31606                     break;
31607                 case 4 :
31608                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31609                     break;
31610                 default :
31611                     break;
31612             }
31613             
31614             Roo.each(box, function(b,kk){
31615                 
31616                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31617                 
31618                 var sz = b.el.getSize();
31619                 
31620                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31621                 
31622             }, this);
31623             
31624         }, this);
31625         
31626         var mY = 0;
31627         
31628         for (var i = 0; i < this.cols; i++){
31629             mY = Math.max(mY, maxY[i]);
31630         }
31631         
31632         this.el.setHeight(mY - pos.y);
31633         
31634     },
31635     
31636 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31637 //    {
31638 //        var pos = this.el.getBox(true);
31639 //        var x = pos.x;
31640 //        var y = pos.y;
31641 //        var maxX = pos.right;
31642 //        
31643 //        var maxHeight = 0;
31644 //        
31645 //        Roo.each(items, function(item, k){
31646 //            
31647 //            var c = k % 2;
31648 //            
31649 //            item.el.position('absolute');
31650 //                
31651 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31652 //
31653 //            item.el.setWidth(width);
31654 //
31655 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31656 //
31657 //            item.el.setHeight(height);
31658 //            
31659 //            if(c == 0){
31660 //                item.el.setXY([x, y], isInstant ? false : true);
31661 //            } else {
31662 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31663 //            }
31664 //            
31665 //            y = y + height + this.alternativePadWidth;
31666 //            
31667 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31668 //            
31669 //        }, this);
31670 //        
31671 //        this.el.setHeight(maxHeight);
31672 //        
31673 //    },
31674     
31675     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31676     {
31677         var pos = this.el.getBox(true);
31678         
31679         var minX = pos.x;
31680         var minY = pos.y;
31681         
31682         var maxX = pos.right;
31683         
31684         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31685         
31686         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31687         
31688         Roo.each(queue, function(box, k){
31689             
31690             Roo.each(box, function(b, kk){
31691                 
31692                 b.el.position('absolute');
31693                 
31694                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31695                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31696                 
31697                 if(b.size == 'md-left' || b.size == 'md-right'){
31698                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31699                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31700                 }
31701                 
31702                 b.el.setWidth(width);
31703                 b.el.setHeight(height);
31704                 
31705             }, this);
31706             
31707             if(!box.length){
31708                 return;
31709             }
31710             
31711             var positions = [];
31712             
31713             switch (box.length){
31714                 case 1 :
31715                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31716                     break;
31717                 case 2 :
31718                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31719                     break;
31720                 case 3 :
31721                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31722                     break;
31723                 case 4 :
31724                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31725                     break;
31726                 default :
31727                     break;
31728             }
31729             
31730             Roo.each(box, function(b,kk){
31731                 
31732                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31733                 
31734                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31735                 
31736             }, this);
31737             
31738         }, this);
31739         
31740     },
31741     
31742     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31743     {
31744         Roo.each(eItems, function(b,k){
31745             
31746             b.size = (k == 0) ? 'sm' : 'xs';
31747             b.x = (k == 0) ? 2 : 1;
31748             b.y = (k == 0) ? 2 : 1;
31749             
31750             b.el.position('absolute');
31751             
31752             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31753                 
31754             b.el.setWidth(width);
31755             
31756             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31757             
31758             b.el.setHeight(height);
31759             
31760         }, this);
31761
31762         var positions = [];
31763         
31764         positions.push({
31765             x : maxX - this.unitWidth * 2 - this.gutter,
31766             y : minY
31767         });
31768         
31769         positions.push({
31770             x : maxX - this.unitWidth,
31771             y : minY + (this.unitWidth + this.gutter) * 2
31772         });
31773         
31774         positions.push({
31775             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31776             y : minY
31777         });
31778         
31779         Roo.each(eItems, function(b,k){
31780             
31781             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31782
31783         }, this);
31784         
31785     },
31786     
31787     getVerticalOneBoxColPositions : function(x, y, box)
31788     {
31789         var pos = [];
31790         
31791         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31792         
31793         if(box[0].size == 'md-left'){
31794             rand = 0;
31795         }
31796         
31797         if(box[0].size == 'md-right'){
31798             rand = 1;
31799         }
31800         
31801         pos.push({
31802             x : x + (this.unitWidth + this.gutter) * rand,
31803             y : y
31804         });
31805         
31806         return pos;
31807     },
31808     
31809     getVerticalTwoBoxColPositions : function(x, y, box)
31810     {
31811         var pos = [];
31812         
31813         if(box[0].size == 'xs'){
31814             
31815             pos.push({
31816                 x : x,
31817                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31818             });
31819
31820             pos.push({
31821                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31822                 y : y
31823             });
31824             
31825             return pos;
31826             
31827         }
31828         
31829         pos.push({
31830             x : x,
31831             y : y
31832         });
31833
31834         pos.push({
31835             x : x + (this.unitWidth + this.gutter) * 2,
31836             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31837         });
31838         
31839         return pos;
31840         
31841     },
31842     
31843     getVerticalThreeBoxColPositions : function(x, y, box)
31844     {
31845         var pos = [];
31846         
31847         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31848             
31849             pos.push({
31850                 x : x,
31851                 y : y
31852             });
31853
31854             pos.push({
31855                 x : x + (this.unitWidth + this.gutter) * 1,
31856                 y : y
31857             });
31858             
31859             pos.push({
31860                 x : x + (this.unitWidth + this.gutter) * 2,
31861                 y : y
31862             });
31863             
31864             return pos;
31865             
31866         }
31867         
31868         if(box[0].size == 'xs' && box[1].size == 'xs'){
31869             
31870             pos.push({
31871                 x : x,
31872                 y : y
31873             });
31874
31875             pos.push({
31876                 x : x,
31877                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31878             });
31879             
31880             pos.push({
31881                 x : x + (this.unitWidth + this.gutter) * 1,
31882                 y : y
31883             });
31884             
31885             return pos;
31886             
31887         }
31888         
31889         pos.push({
31890             x : x,
31891             y : y
31892         });
31893
31894         pos.push({
31895             x : x + (this.unitWidth + this.gutter) * 2,
31896             y : y
31897         });
31898
31899         pos.push({
31900             x : x + (this.unitWidth + this.gutter) * 2,
31901             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31902         });
31903             
31904         return pos;
31905         
31906     },
31907     
31908     getVerticalFourBoxColPositions : function(x, y, box)
31909     {
31910         var pos = [];
31911         
31912         if(box[0].size == 'xs'){
31913             
31914             pos.push({
31915                 x : x,
31916                 y : y
31917             });
31918
31919             pos.push({
31920                 x : x,
31921                 y : y + (this.unitHeight + this.gutter) * 1
31922             });
31923             
31924             pos.push({
31925                 x : x,
31926                 y : y + (this.unitHeight + this.gutter) * 2
31927             });
31928             
31929             pos.push({
31930                 x : x + (this.unitWidth + this.gutter) * 1,
31931                 y : y
31932             });
31933             
31934             return pos;
31935             
31936         }
31937         
31938         pos.push({
31939             x : x,
31940             y : y
31941         });
31942
31943         pos.push({
31944             x : x + (this.unitWidth + this.gutter) * 2,
31945             y : y
31946         });
31947
31948         pos.push({
31949             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31950             y : y + (this.unitHeight + this.gutter) * 1
31951         });
31952
31953         pos.push({
31954             x : x + (this.unitWidth + this.gutter) * 2,
31955             y : y + (this.unitWidth + this.gutter) * 2
31956         });
31957
31958         return pos;
31959         
31960     },
31961     
31962     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31963     {
31964         var pos = [];
31965         
31966         if(box[0].size == 'md-left'){
31967             pos.push({
31968                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31969                 y : minY
31970             });
31971             
31972             return pos;
31973         }
31974         
31975         if(box[0].size == 'md-right'){
31976             pos.push({
31977                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31978                 y : minY + (this.unitWidth + this.gutter) * 1
31979             });
31980             
31981             return pos;
31982         }
31983         
31984         var rand = Math.floor(Math.random() * (4 - box[0].y));
31985         
31986         pos.push({
31987             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31988             y : minY + (this.unitWidth + this.gutter) * rand
31989         });
31990         
31991         return pos;
31992         
31993     },
31994     
31995     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31996     {
31997         var pos = [];
31998         
31999         if(box[0].size == 'xs'){
32000             
32001             pos.push({
32002                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32003                 y : minY
32004             });
32005
32006             pos.push({
32007                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32008                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32009             });
32010             
32011             return pos;
32012             
32013         }
32014         
32015         pos.push({
32016             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32017             y : minY
32018         });
32019
32020         pos.push({
32021             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32022             y : minY + (this.unitWidth + this.gutter) * 2
32023         });
32024         
32025         return pos;
32026         
32027     },
32028     
32029     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32030     {
32031         var pos = [];
32032         
32033         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32034             
32035             pos.push({
32036                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32037                 y : minY
32038             });
32039
32040             pos.push({
32041                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32042                 y : minY + (this.unitWidth + this.gutter) * 1
32043             });
32044             
32045             pos.push({
32046                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32047                 y : minY + (this.unitWidth + this.gutter) * 2
32048             });
32049             
32050             return pos;
32051             
32052         }
32053         
32054         if(box[0].size == 'xs' && box[1].size == 'xs'){
32055             
32056             pos.push({
32057                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32058                 y : minY
32059             });
32060
32061             pos.push({
32062                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32063                 y : minY
32064             });
32065             
32066             pos.push({
32067                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32068                 y : minY + (this.unitWidth + this.gutter) * 1
32069             });
32070             
32071             return pos;
32072             
32073         }
32074         
32075         pos.push({
32076             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32077             y : minY
32078         });
32079
32080         pos.push({
32081             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32082             y : minY + (this.unitWidth + this.gutter) * 2
32083         });
32084
32085         pos.push({
32086             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32087             y : minY + (this.unitWidth + this.gutter) * 2
32088         });
32089             
32090         return pos;
32091         
32092     },
32093     
32094     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32095     {
32096         var pos = [];
32097         
32098         if(box[0].size == 'xs'){
32099             
32100             pos.push({
32101                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32102                 y : minY
32103             });
32104
32105             pos.push({
32106                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32107                 y : minY
32108             });
32109             
32110             pos.push({
32111                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32112                 y : minY
32113             });
32114             
32115             pos.push({
32116                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32117                 y : minY + (this.unitWidth + this.gutter) * 1
32118             });
32119             
32120             return pos;
32121             
32122         }
32123         
32124         pos.push({
32125             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32126             y : minY
32127         });
32128         
32129         pos.push({
32130             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32131             y : minY + (this.unitWidth + this.gutter) * 2
32132         });
32133         
32134         pos.push({
32135             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32136             y : minY + (this.unitWidth + this.gutter) * 2
32137         });
32138         
32139         pos.push({
32140             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32141             y : minY + (this.unitWidth + this.gutter) * 2
32142         });
32143
32144         return pos;
32145         
32146     },
32147     
32148     /**
32149     * remove a Masonry Brick
32150     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32151     */
32152     removeBrick : function(brick_id)
32153     {
32154         if (!brick_id) {
32155             return;
32156         }
32157         
32158         for (var i = 0; i<this.bricks.length; i++) {
32159             if (this.bricks[i].id == brick_id) {
32160                 this.bricks.splice(i,1);
32161                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32162                 this.initial();
32163             }
32164         }
32165     },
32166     
32167     /**
32168     * adds a Masonry Brick
32169     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32170     */
32171     addBrick : function(cfg)
32172     {
32173         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32174         //this.register(cn);
32175         cn.parentId = this.id;
32176         cn.render(this.el);
32177         return cn;
32178     },
32179     
32180     /**
32181     * register a Masonry Brick
32182     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32183     */
32184     
32185     register : function(brick)
32186     {
32187         this.bricks.push(brick);
32188         brick.masonryId = this.id;
32189     },
32190     
32191     /**
32192     * clear all the Masonry Brick
32193     */
32194     clearAll : function()
32195     {
32196         this.bricks = [];
32197         //this.getChildContainer().dom.innerHTML = "";
32198         this.el.dom.innerHTML = '';
32199     },
32200     
32201     getSelected : function()
32202     {
32203         if (!this.selectedBrick) {
32204             return false;
32205         }
32206         
32207         return this.selectedBrick;
32208     }
32209 });
32210
32211 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32212     
32213     groups: {},
32214      /**
32215     * register a Masonry Layout
32216     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32217     */
32218     
32219     register : function(layout)
32220     {
32221         this.groups[layout.id] = layout;
32222     },
32223     /**
32224     * fetch a  Masonry Layout based on the masonry layout ID
32225     * @param {string} the masonry layout to add
32226     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32227     */
32228     
32229     get: function(layout_id) {
32230         if (typeof(this.groups[layout_id]) == 'undefined') {
32231             return false;
32232         }
32233         return this.groups[layout_id] ;
32234     }
32235     
32236     
32237     
32238 });
32239
32240  
32241
32242  /**
32243  *
32244  * This is based on 
32245  * http://masonry.desandro.com
32246  *
32247  * The idea is to render all the bricks based on vertical width...
32248  *
32249  * The original code extends 'outlayer' - we might need to use that....
32250  * 
32251  */
32252
32253
32254 /**
32255  * @class Roo.bootstrap.LayoutMasonryAuto
32256  * @extends Roo.bootstrap.Component
32257  * Bootstrap Layout Masonry class
32258  * 
32259  * @constructor
32260  * Create a new Element
32261  * @param {Object} config The config object
32262  */
32263
32264 Roo.bootstrap.LayoutMasonryAuto = function(config){
32265     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32266 };
32267
32268 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32269     
32270       /**
32271      * @cfg {Boolean} isFitWidth  - resize the width..
32272      */   
32273     isFitWidth : false,  // options..
32274     /**
32275      * @cfg {Boolean} isOriginLeft = left align?
32276      */   
32277     isOriginLeft : true,
32278     /**
32279      * @cfg {Boolean} isOriginTop = top align?
32280      */   
32281     isOriginTop : false,
32282     /**
32283      * @cfg {Boolean} isLayoutInstant = no animation?
32284      */   
32285     isLayoutInstant : false, // needed?
32286     /**
32287      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32288      */   
32289     isResizingContainer : true,
32290     /**
32291      * @cfg {Number} columnWidth  width of the columns 
32292      */   
32293     
32294     columnWidth : 0,
32295     
32296     /**
32297      * @cfg {Number} maxCols maximum number of columns
32298      */   
32299     
32300     maxCols: 0,
32301     /**
32302      * @cfg {Number} padHeight padding below box..
32303      */   
32304     
32305     padHeight : 10, 
32306     
32307     /**
32308      * @cfg {Boolean} isAutoInitial defalut true
32309      */   
32310     
32311     isAutoInitial : true, 
32312     
32313     // private?
32314     gutter : 0,
32315     
32316     containerWidth: 0,
32317     initialColumnWidth : 0,
32318     currentSize : null,
32319     
32320     colYs : null, // array.
32321     maxY : 0,
32322     padWidth: 10,
32323     
32324     
32325     tag: 'div',
32326     cls: '',
32327     bricks: null, //CompositeElement
32328     cols : 0, // array?
32329     // element : null, // wrapped now this.el
32330     _isLayoutInited : null, 
32331     
32332     
32333     getAutoCreate : function(){
32334         
32335         var cfg = {
32336             tag: this.tag,
32337             cls: 'blog-masonary-wrapper ' + this.cls,
32338             cn : {
32339                 cls : 'mas-boxes masonary'
32340             }
32341         };
32342         
32343         return cfg;
32344     },
32345     
32346     getChildContainer: function( )
32347     {
32348         if (this.boxesEl) {
32349             return this.boxesEl;
32350         }
32351         
32352         this.boxesEl = this.el.select('.mas-boxes').first();
32353         
32354         return this.boxesEl;
32355     },
32356     
32357     
32358     initEvents : function()
32359     {
32360         var _this = this;
32361         
32362         if(this.isAutoInitial){
32363             Roo.log('hook children rendered');
32364             this.on('childrenrendered', function() {
32365                 Roo.log('children rendered');
32366                 _this.initial();
32367             } ,this);
32368         }
32369         
32370     },
32371     
32372     initial : function()
32373     {
32374         this.reloadItems();
32375
32376         this.currentSize = this.el.getBox(true);
32377
32378         /// was window resize... - let's see if this works..
32379         Roo.EventManager.onWindowResize(this.resize, this); 
32380
32381         if(!this.isAutoInitial){
32382             this.layout();
32383             return;
32384         }
32385         
32386         this.layout.defer(500,this);
32387     },
32388     
32389     reloadItems: function()
32390     {
32391         this.bricks = this.el.select('.masonry-brick', true);
32392         
32393         this.bricks.each(function(b) {
32394             //Roo.log(b.getSize());
32395             if (!b.attr('originalwidth')) {
32396                 b.attr('originalwidth',  b.getSize().width);
32397             }
32398             
32399         });
32400         
32401         Roo.log(this.bricks.elements.length);
32402     },
32403     
32404     resize : function()
32405     {
32406         Roo.log('resize');
32407         var cs = this.el.getBox(true);
32408         
32409         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32410             Roo.log("no change in with or X");
32411             return;
32412         }
32413         this.currentSize = cs;
32414         this.layout();
32415     },
32416     
32417     layout : function()
32418     {
32419          Roo.log('layout');
32420         this._resetLayout();
32421         //this._manageStamps();
32422       
32423         // don't animate first layout
32424         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32425         this.layoutItems( isInstant );
32426       
32427         // flag for initalized
32428         this._isLayoutInited = true;
32429     },
32430     
32431     layoutItems : function( isInstant )
32432     {
32433         //var items = this._getItemsForLayout( this.items );
32434         // original code supports filtering layout items.. we just ignore it..
32435         
32436         this._layoutItems( this.bricks , isInstant );
32437       
32438         this._postLayout();
32439     },
32440     _layoutItems : function ( items , isInstant)
32441     {
32442        //this.fireEvent( 'layout', this, items );
32443     
32444
32445         if ( !items || !items.elements.length ) {
32446           // no items, emit event with empty array
32447             return;
32448         }
32449
32450         var queue = [];
32451         items.each(function(item) {
32452             Roo.log("layout item");
32453             Roo.log(item);
32454             // get x/y object from method
32455             var position = this._getItemLayoutPosition( item );
32456             // enqueue
32457             position.item = item;
32458             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32459             queue.push( position );
32460         }, this);
32461       
32462         this._processLayoutQueue( queue );
32463     },
32464     /** Sets position of item in DOM
32465     * @param {Element} item
32466     * @param {Number} x - horizontal position
32467     * @param {Number} y - vertical position
32468     * @param {Boolean} isInstant - disables transitions
32469     */
32470     _processLayoutQueue : function( queue )
32471     {
32472         for ( var i=0, len = queue.length; i < len; i++ ) {
32473             var obj = queue[i];
32474             obj.item.position('absolute');
32475             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32476         }
32477     },
32478       
32479     
32480     /**
32481     * Any logic you want to do after each layout,
32482     * i.e. size the container
32483     */
32484     _postLayout : function()
32485     {
32486         this.resizeContainer();
32487     },
32488     
32489     resizeContainer : function()
32490     {
32491         if ( !this.isResizingContainer ) {
32492             return;
32493         }
32494         var size = this._getContainerSize();
32495         if ( size ) {
32496             this.el.setSize(size.width,size.height);
32497             this.boxesEl.setSize(size.width,size.height);
32498         }
32499     },
32500     
32501     
32502     
32503     _resetLayout : function()
32504     {
32505         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32506         this.colWidth = this.el.getWidth();
32507         //this.gutter = this.el.getWidth(); 
32508         
32509         this.measureColumns();
32510
32511         // reset column Y
32512         var i = this.cols;
32513         this.colYs = [];
32514         while (i--) {
32515             this.colYs.push( 0 );
32516         }
32517     
32518         this.maxY = 0;
32519     },
32520
32521     measureColumns : function()
32522     {
32523         this.getContainerWidth();
32524       // if columnWidth is 0, default to outerWidth of first item
32525         if ( !this.columnWidth ) {
32526             var firstItem = this.bricks.first();
32527             Roo.log(firstItem);
32528             this.columnWidth  = this.containerWidth;
32529             if (firstItem && firstItem.attr('originalwidth') ) {
32530                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32531             }
32532             // columnWidth fall back to item of first element
32533             Roo.log("set column width?");
32534                         this.initialColumnWidth = this.columnWidth  ;
32535
32536             // if first elem has no width, default to size of container
32537             
32538         }
32539         
32540         
32541         if (this.initialColumnWidth) {
32542             this.columnWidth = this.initialColumnWidth;
32543         }
32544         
32545         
32546             
32547         // column width is fixed at the top - however if container width get's smaller we should
32548         // reduce it...
32549         
32550         // this bit calcs how man columns..
32551             
32552         var columnWidth = this.columnWidth += this.gutter;
32553       
32554         // calculate columns
32555         var containerWidth = this.containerWidth + this.gutter;
32556         
32557         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32558         // fix rounding errors, typically with gutters
32559         var excess = columnWidth - containerWidth % columnWidth;
32560         
32561         
32562         // if overshoot is less than a pixel, round up, otherwise floor it
32563         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32564         cols = Math[ mathMethod ]( cols );
32565         this.cols = Math.max( cols, 1 );
32566         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32567         
32568          // padding positioning..
32569         var totalColWidth = this.cols * this.columnWidth;
32570         var padavail = this.containerWidth - totalColWidth;
32571         // so for 2 columns - we need 3 'pads'
32572         
32573         var padNeeded = (1+this.cols) * this.padWidth;
32574         
32575         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32576         
32577         this.columnWidth += padExtra
32578         //this.padWidth = Math.floor(padavail /  ( this.cols));
32579         
32580         // adjust colum width so that padding is fixed??
32581         
32582         // we have 3 columns ... total = width * 3
32583         // we have X left over... that should be used by 
32584         
32585         //if (this.expandC) {
32586             
32587         //}
32588         
32589         
32590         
32591     },
32592     
32593     getContainerWidth : function()
32594     {
32595        /* // container is parent if fit width
32596         var container = this.isFitWidth ? this.element.parentNode : this.element;
32597         // check that this.size and size are there
32598         // IE8 triggers resize on body size change, so they might not be
32599         
32600         var size = getSize( container );  //FIXME
32601         this.containerWidth = size && size.innerWidth; //FIXME
32602         */
32603          
32604         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32605         
32606     },
32607     
32608     _getItemLayoutPosition : function( item )  // what is item?
32609     {
32610         // we resize the item to our columnWidth..
32611       
32612         item.setWidth(this.columnWidth);
32613         item.autoBoxAdjust  = false;
32614         
32615         var sz = item.getSize();
32616  
32617         // how many columns does this brick span
32618         var remainder = this.containerWidth % this.columnWidth;
32619         
32620         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32621         // round if off by 1 pixel, otherwise use ceil
32622         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32623         colSpan = Math.min( colSpan, this.cols );
32624         
32625         // normally this should be '1' as we dont' currently allow multi width columns..
32626         
32627         var colGroup = this._getColGroup( colSpan );
32628         // get the minimum Y value from the columns
32629         var minimumY = Math.min.apply( Math, colGroup );
32630         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32631         
32632         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32633          
32634         // position the brick
32635         var position = {
32636             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32637             y: this.currentSize.y + minimumY + this.padHeight
32638         };
32639         
32640         Roo.log(position);
32641         // apply setHeight to necessary columns
32642         var setHeight = minimumY + sz.height + this.padHeight;
32643         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32644         
32645         var setSpan = this.cols + 1 - colGroup.length;
32646         for ( var i = 0; i < setSpan; i++ ) {
32647           this.colYs[ shortColIndex + i ] = setHeight ;
32648         }
32649       
32650         return position;
32651     },
32652     
32653     /**
32654      * @param {Number} colSpan - number of columns the element spans
32655      * @returns {Array} colGroup
32656      */
32657     _getColGroup : function( colSpan )
32658     {
32659         if ( colSpan < 2 ) {
32660           // if brick spans only one column, use all the column Ys
32661           return this.colYs;
32662         }
32663       
32664         var colGroup = [];
32665         // how many different places could this brick fit horizontally
32666         var groupCount = this.cols + 1 - colSpan;
32667         // for each group potential horizontal position
32668         for ( var i = 0; i < groupCount; i++ ) {
32669           // make an array of colY values for that one group
32670           var groupColYs = this.colYs.slice( i, i + colSpan );
32671           // and get the max value of the array
32672           colGroup[i] = Math.max.apply( Math, groupColYs );
32673         }
32674         return colGroup;
32675     },
32676     /*
32677     _manageStamp : function( stamp )
32678     {
32679         var stampSize =  stamp.getSize();
32680         var offset = stamp.getBox();
32681         // get the columns that this stamp affects
32682         var firstX = this.isOriginLeft ? offset.x : offset.right;
32683         var lastX = firstX + stampSize.width;
32684         var firstCol = Math.floor( firstX / this.columnWidth );
32685         firstCol = Math.max( 0, firstCol );
32686         
32687         var lastCol = Math.floor( lastX / this.columnWidth );
32688         // lastCol should not go over if multiple of columnWidth #425
32689         lastCol -= lastX % this.columnWidth ? 0 : 1;
32690         lastCol = Math.min( this.cols - 1, lastCol );
32691         
32692         // set colYs to bottom of the stamp
32693         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32694             stampSize.height;
32695             
32696         for ( var i = firstCol; i <= lastCol; i++ ) {
32697           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32698         }
32699     },
32700     */
32701     
32702     _getContainerSize : function()
32703     {
32704         this.maxY = Math.max.apply( Math, this.colYs );
32705         var size = {
32706             height: this.maxY
32707         };
32708       
32709         if ( this.isFitWidth ) {
32710             size.width = this._getContainerFitWidth();
32711         }
32712       
32713         return size;
32714     },
32715     
32716     _getContainerFitWidth : function()
32717     {
32718         var unusedCols = 0;
32719         // count unused columns
32720         var i = this.cols;
32721         while ( --i ) {
32722           if ( this.colYs[i] !== 0 ) {
32723             break;
32724           }
32725           unusedCols++;
32726         }
32727         // fit container to columns that have been used
32728         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32729     },
32730     
32731     needsResizeLayout : function()
32732     {
32733         var previousWidth = this.containerWidth;
32734         this.getContainerWidth();
32735         return previousWidth !== this.containerWidth;
32736     }
32737  
32738 });
32739
32740  
32741
32742  /*
32743  * - LGPL
32744  *
32745  * element
32746  * 
32747  */
32748
32749 /**
32750  * @class Roo.bootstrap.MasonryBrick
32751  * @extends Roo.bootstrap.Component
32752  * Bootstrap MasonryBrick class
32753  * 
32754  * @constructor
32755  * Create a new MasonryBrick
32756  * @param {Object} config The config object
32757  */
32758
32759 Roo.bootstrap.MasonryBrick = function(config){
32760     
32761     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32762     
32763     Roo.bootstrap.MasonryBrick.register(this);
32764     
32765     this.addEvents({
32766         // raw events
32767         /**
32768          * @event click
32769          * When a MasonryBrick is clcik
32770          * @param {Roo.bootstrap.MasonryBrick} this
32771          * @param {Roo.EventObject} e
32772          */
32773         "click" : true
32774     });
32775 };
32776
32777 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32778     
32779     /**
32780      * @cfg {String} title
32781      */   
32782     title : '',
32783     /**
32784      * @cfg {String} html
32785      */   
32786     html : '',
32787     /**
32788      * @cfg {String} bgimage
32789      */   
32790     bgimage : '',
32791     /**
32792      * @cfg {String} videourl
32793      */   
32794     videourl : '',
32795     /**
32796      * @cfg {String} cls
32797      */   
32798     cls : '',
32799     /**
32800      * @cfg {String} href
32801      */   
32802     href : '',
32803     /**
32804      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32805      */   
32806     size : 'xs',
32807     
32808     /**
32809      * @cfg {String} placetitle (center|bottom)
32810      */   
32811     placetitle : '',
32812     
32813     /**
32814      * @cfg {Boolean} isFitContainer defalut true
32815      */   
32816     isFitContainer : true, 
32817     
32818     /**
32819      * @cfg {Boolean} preventDefault defalut false
32820      */   
32821     preventDefault : false, 
32822     
32823     /**
32824      * @cfg {Boolean} inverse defalut false
32825      */   
32826     maskInverse : false, 
32827     
32828     getAutoCreate : function()
32829     {
32830         if(!this.isFitContainer){
32831             return this.getSplitAutoCreate();
32832         }
32833         
32834         var cls = 'masonry-brick masonry-brick-full';
32835         
32836         if(this.href.length){
32837             cls += ' masonry-brick-link';
32838         }
32839         
32840         if(this.bgimage.length){
32841             cls += ' masonry-brick-image';
32842         }
32843         
32844         if(this.maskInverse){
32845             cls += ' mask-inverse';
32846         }
32847         
32848         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32849             cls += ' enable-mask';
32850         }
32851         
32852         if(this.size){
32853             cls += ' masonry-' + this.size + '-brick';
32854         }
32855         
32856         if(this.placetitle.length){
32857             
32858             switch (this.placetitle) {
32859                 case 'center' :
32860                     cls += ' masonry-center-title';
32861                     break;
32862                 case 'bottom' :
32863                     cls += ' masonry-bottom-title';
32864                     break;
32865                 default:
32866                     break;
32867             }
32868             
32869         } else {
32870             if(!this.html.length && !this.bgimage.length){
32871                 cls += ' masonry-center-title';
32872             }
32873
32874             if(!this.html.length && this.bgimage.length){
32875                 cls += ' masonry-bottom-title';
32876             }
32877         }
32878         
32879         if(this.cls){
32880             cls += ' ' + this.cls;
32881         }
32882         
32883         var cfg = {
32884             tag: (this.href.length) ? 'a' : 'div',
32885             cls: cls,
32886             cn: [
32887                 {
32888                     tag: 'div',
32889                     cls: 'masonry-brick-mask'
32890                 },
32891                 {
32892                     tag: 'div',
32893                     cls: 'masonry-brick-paragraph',
32894                     cn: []
32895                 }
32896             ]
32897         };
32898         
32899         if(this.href.length){
32900             cfg.href = this.href;
32901         }
32902         
32903         var cn = cfg.cn[1].cn;
32904         
32905         if(this.title.length){
32906             cn.push({
32907                 tag: 'h4',
32908                 cls: 'masonry-brick-title',
32909                 html: this.title
32910             });
32911         }
32912         
32913         if(this.html.length){
32914             cn.push({
32915                 tag: 'p',
32916                 cls: 'masonry-brick-text',
32917                 html: this.html
32918             });
32919         }
32920         
32921         if (!this.title.length && !this.html.length) {
32922             cfg.cn[1].cls += ' hide';
32923         }
32924         
32925         if(this.bgimage.length){
32926             cfg.cn.push({
32927                 tag: 'img',
32928                 cls: 'masonry-brick-image-view',
32929                 src: this.bgimage
32930             });
32931         }
32932         
32933         if(this.videourl.length){
32934             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32935             // youtube support only?
32936             cfg.cn.push({
32937                 tag: 'iframe',
32938                 cls: 'masonry-brick-image-view',
32939                 src: vurl,
32940                 frameborder : 0,
32941                 allowfullscreen : true
32942             });
32943         }
32944         
32945         return cfg;
32946         
32947     },
32948     
32949     getSplitAutoCreate : function()
32950     {
32951         var cls = 'masonry-brick masonry-brick-split';
32952         
32953         if(this.href.length){
32954             cls += ' masonry-brick-link';
32955         }
32956         
32957         if(this.bgimage.length){
32958             cls += ' masonry-brick-image';
32959         }
32960         
32961         if(this.size){
32962             cls += ' masonry-' + this.size + '-brick';
32963         }
32964         
32965         switch (this.placetitle) {
32966             case 'center' :
32967                 cls += ' masonry-center-title';
32968                 break;
32969             case 'bottom' :
32970                 cls += ' masonry-bottom-title';
32971                 break;
32972             default:
32973                 if(!this.bgimage.length){
32974                     cls += ' masonry-center-title';
32975                 }
32976
32977                 if(this.bgimage.length){
32978                     cls += ' masonry-bottom-title';
32979                 }
32980                 break;
32981         }
32982         
32983         if(this.cls){
32984             cls += ' ' + this.cls;
32985         }
32986         
32987         var cfg = {
32988             tag: (this.href.length) ? 'a' : 'div',
32989             cls: cls,
32990             cn: [
32991                 {
32992                     tag: 'div',
32993                     cls: 'masonry-brick-split-head',
32994                     cn: [
32995                         {
32996                             tag: 'div',
32997                             cls: 'masonry-brick-paragraph',
32998                             cn: []
32999                         }
33000                     ]
33001                 },
33002                 {
33003                     tag: 'div',
33004                     cls: 'masonry-brick-split-body',
33005                     cn: []
33006                 }
33007             ]
33008         };
33009         
33010         if(this.href.length){
33011             cfg.href = this.href;
33012         }
33013         
33014         if(this.title.length){
33015             cfg.cn[0].cn[0].cn.push({
33016                 tag: 'h4',
33017                 cls: 'masonry-brick-title',
33018                 html: this.title
33019             });
33020         }
33021         
33022         if(this.html.length){
33023             cfg.cn[1].cn.push({
33024                 tag: 'p',
33025                 cls: 'masonry-brick-text',
33026                 html: this.html
33027             });
33028         }
33029
33030         if(this.bgimage.length){
33031             cfg.cn[0].cn.push({
33032                 tag: 'img',
33033                 cls: 'masonry-brick-image-view',
33034                 src: this.bgimage
33035             });
33036         }
33037         
33038         if(this.videourl.length){
33039             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33040             // youtube support only?
33041             cfg.cn[0].cn.cn.push({
33042                 tag: 'iframe',
33043                 cls: 'masonry-brick-image-view',
33044                 src: vurl,
33045                 frameborder : 0,
33046                 allowfullscreen : true
33047             });
33048         }
33049         
33050         return cfg;
33051     },
33052     
33053     initEvents: function() 
33054     {
33055         switch (this.size) {
33056             case 'xs' :
33057                 this.x = 1;
33058                 this.y = 1;
33059                 break;
33060             case 'sm' :
33061                 this.x = 2;
33062                 this.y = 2;
33063                 break;
33064             case 'md' :
33065             case 'md-left' :
33066             case 'md-right' :
33067                 this.x = 3;
33068                 this.y = 3;
33069                 break;
33070             case 'tall' :
33071                 this.x = 2;
33072                 this.y = 3;
33073                 break;
33074             case 'wide' :
33075                 this.x = 3;
33076                 this.y = 2;
33077                 break;
33078             case 'wide-thin' :
33079                 this.x = 3;
33080                 this.y = 1;
33081                 break;
33082                         
33083             default :
33084                 break;
33085         }
33086         
33087         if(Roo.isTouch){
33088             this.el.on('touchstart', this.onTouchStart, this);
33089             this.el.on('touchmove', this.onTouchMove, this);
33090             this.el.on('touchend', this.onTouchEnd, this);
33091             this.el.on('contextmenu', this.onContextMenu, this);
33092         } else {
33093             this.el.on('mouseenter'  ,this.enter, this);
33094             this.el.on('mouseleave', this.leave, this);
33095             this.el.on('click', this.onClick, this);
33096         }
33097         
33098         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33099             this.parent().bricks.push(this);   
33100         }
33101         
33102     },
33103     
33104     onClick: function(e, el)
33105     {
33106         var time = this.endTimer - this.startTimer;
33107         // Roo.log(e.preventDefault());
33108         if(Roo.isTouch){
33109             if(time > 1000){
33110                 e.preventDefault();
33111                 return;
33112             }
33113         }
33114         
33115         if(!this.preventDefault){
33116             return;
33117         }
33118         
33119         e.preventDefault();
33120         
33121         if (this.activeClass != '') {
33122             this.selectBrick();
33123         }
33124         
33125         this.fireEvent('click', this, e);
33126     },
33127     
33128     enter: function(e, el)
33129     {
33130         e.preventDefault();
33131         
33132         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33133             return;
33134         }
33135         
33136         if(this.bgimage.length && this.html.length){
33137             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33138         }
33139     },
33140     
33141     leave: function(e, el)
33142     {
33143         e.preventDefault();
33144         
33145         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33146             return;
33147         }
33148         
33149         if(this.bgimage.length && this.html.length){
33150             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33151         }
33152     },
33153     
33154     onTouchStart: function(e, el)
33155     {
33156 //        e.preventDefault();
33157         
33158         this.touchmoved = false;
33159         
33160         if(!this.isFitContainer){
33161             return;
33162         }
33163         
33164         if(!this.bgimage.length || !this.html.length){
33165             return;
33166         }
33167         
33168         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33169         
33170         this.timer = new Date().getTime();
33171         
33172     },
33173     
33174     onTouchMove: function(e, el)
33175     {
33176         this.touchmoved = true;
33177     },
33178     
33179     onContextMenu : function(e,el)
33180     {
33181         e.preventDefault();
33182         e.stopPropagation();
33183         return false;
33184     },
33185     
33186     onTouchEnd: function(e, el)
33187     {
33188 //        e.preventDefault();
33189         
33190         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33191         
33192             this.leave(e,el);
33193             
33194             return;
33195         }
33196         
33197         if(!this.bgimage.length || !this.html.length){
33198             
33199             if(this.href.length){
33200                 window.location.href = this.href;
33201             }
33202             
33203             return;
33204         }
33205         
33206         if(!this.isFitContainer){
33207             return;
33208         }
33209         
33210         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33211         
33212         window.location.href = this.href;
33213     },
33214     
33215     //selection on single brick only
33216     selectBrick : function() {
33217         
33218         if (!this.parentId) {
33219             return;
33220         }
33221         
33222         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33223         var index = m.selectedBrick.indexOf(this.id);
33224         
33225         if ( index > -1) {
33226             m.selectedBrick.splice(index,1);
33227             this.el.removeClass(this.activeClass);
33228             return;
33229         }
33230         
33231         for(var i = 0; i < m.selectedBrick.length; i++) {
33232             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33233             b.el.removeClass(b.activeClass);
33234         }
33235         
33236         m.selectedBrick = [];
33237         
33238         m.selectedBrick.push(this.id);
33239         this.el.addClass(this.activeClass);
33240         return;
33241     },
33242     
33243     isSelected : function(){
33244         return this.el.hasClass(this.activeClass);
33245         
33246     }
33247 });
33248
33249 Roo.apply(Roo.bootstrap.MasonryBrick, {
33250     
33251     //groups: {},
33252     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33253      /**
33254     * register a Masonry Brick
33255     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33256     */
33257     
33258     register : function(brick)
33259     {
33260         //this.groups[brick.id] = brick;
33261         this.groups.add(brick.id, brick);
33262     },
33263     /**
33264     * fetch a  masonry brick based on the masonry brick ID
33265     * @param {string} the masonry brick to add
33266     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33267     */
33268     
33269     get: function(brick_id) 
33270     {
33271         // if (typeof(this.groups[brick_id]) == 'undefined') {
33272         //     return false;
33273         // }
33274         // return this.groups[brick_id] ;
33275         
33276         if(this.groups.key(brick_id)) {
33277             return this.groups.key(brick_id);
33278         }
33279         
33280         return false;
33281     }
33282     
33283     
33284     
33285 });
33286
33287  /*
33288  * - LGPL
33289  *
33290  * element
33291  * 
33292  */
33293
33294 /**
33295  * @class Roo.bootstrap.Brick
33296  * @extends Roo.bootstrap.Component
33297  * Bootstrap Brick class
33298  * 
33299  * @constructor
33300  * Create a new Brick
33301  * @param {Object} config The config object
33302  */
33303
33304 Roo.bootstrap.Brick = function(config){
33305     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33306     
33307     this.addEvents({
33308         // raw events
33309         /**
33310          * @event click
33311          * When a Brick is click
33312          * @param {Roo.bootstrap.Brick} this
33313          * @param {Roo.EventObject} e
33314          */
33315         "click" : true
33316     });
33317 };
33318
33319 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33320     
33321     /**
33322      * @cfg {String} title
33323      */   
33324     title : '',
33325     /**
33326      * @cfg {String} html
33327      */   
33328     html : '',
33329     /**
33330      * @cfg {String} bgimage
33331      */   
33332     bgimage : '',
33333     /**
33334      * @cfg {String} cls
33335      */   
33336     cls : '',
33337     /**
33338      * @cfg {String} href
33339      */   
33340     href : '',
33341     /**
33342      * @cfg {String} video
33343      */   
33344     video : '',
33345     /**
33346      * @cfg {Boolean} square
33347      */   
33348     square : true,
33349     
33350     getAutoCreate : function()
33351     {
33352         var cls = 'roo-brick';
33353         
33354         if(this.href.length){
33355             cls += ' roo-brick-link';
33356         }
33357         
33358         if(this.bgimage.length){
33359             cls += ' roo-brick-image';
33360         }
33361         
33362         if(!this.html.length && !this.bgimage.length){
33363             cls += ' roo-brick-center-title';
33364         }
33365         
33366         if(!this.html.length && this.bgimage.length){
33367             cls += ' roo-brick-bottom-title';
33368         }
33369         
33370         if(this.cls){
33371             cls += ' ' + this.cls;
33372         }
33373         
33374         var cfg = {
33375             tag: (this.href.length) ? 'a' : 'div',
33376             cls: cls,
33377             cn: [
33378                 {
33379                     tag: 'div',
33380                     cls: 'roo-brick-paragraph',
33381                     cn: []
33382                 }
33383             ]
33384         };
33385         
33386         if(this.href.length){
33387             cfg.href = this.href;
33388         }
33389         
33390         var cn = cfg.cn[0].cn;
33391         
33392         if(this.title.length){
33393             cn.push({
33394                 tag: 'h4',
33395                 cls: 'roo-brick-title',
33396                 html: this.title
33397             });
33398         }
33399         
33400         if(this.html.length){
33401             cn.push({
33402                 tag: 'p',
33403                 cls: 'roo-brick-text',
33404                 html: this.html
33405             });
33406         } else {
33407             cn.cls += ' hide';
33408         }
33409         
33410         if(this.bgimage.length){
33411             cfg.cn.push({
33412                 tag: 'img',
33413                 cls: 'roo-brick-image-view',
33414                 src: this.bgimage
33415             });
33416         }
33417         
33418         return cfg;
33419     },
33420     
33421     initEvents: function() 
33422     {
33423         if(this.title.length || this.html.length){
33424             this.el.on('mouseenter'  ,this.enter, this);
33425             this.el.on('mouseleave', this.leave, this);
33426         }
33427         
33428         Roo.EventManager.onWindowResize(this.resize, this); 
33429         
33430         if(this.bgimage.length){
33431             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33432             this.imageEl.on('load', this.onImageLoad, this);
33433             return;
33434         }
33435         
33436         this.resize();
33437     },
33438     
33439     onImageLoad : function()
33440     {
33441         this.resize();
33442     },
33443     
33444     resize : function()
33445     {
33446         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33447         
33448         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33449         
33450         if(this.bgimage.length){
33451             var image = this.el.select('.roo-brick-image-view', true).first();
33452             
33453             image.setWidth(paragraph.getWidth());
33454             
33455             if(this.square){
33456                 image.setHeight(paragraph.getWidth());
33457             }
33458             
33459             this.el.setHeight(image.getHeight());
33460             paragraph.setHeight(image.getHeight());
33461             
33462         }
33463         
33464     },
33465     
33466     enter: function(e, el)
33467     {
33468         e.preventDefault();
33469         
33470         if(this.bgimage.length){
33471             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33472             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33473         }
33474     },
33475     
33476     leave: function(e, el)
33477     {
33478         e.preventDefault();
33479         
33480         if(this.bgimage.length){
33481             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33482             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33483         }
33484     }
33485     
33486 });
33487
33488  
33489
33490  /*
33491  * - LGPL
33492  *
33493  * Number field 
33494  */
33495
33496 /**
33497  * @class Roo.bootstrap.NumberField
33498  * @extends Roo.bootstrap.Input
33499  * Bootstrap NumberField class
33500  * 
33501  * 
33502  * 
33503  * 
33504  * @constructor
33505  * Create a new NumberField
33506  * @param {Object} config The config object
33507  */
33508
33509 Roo.bootstrap.NumberField = function(config){
33510     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33511 };
33512
33513 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33514     
33515     /**
33516      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33517      */
33518     allowDecimals : true,
33519     /**
33520      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33521      */
33522     decimalSeparator : ".",
33523     /**
33524      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33525      */
33526     decimalPrecision : 2,
33527     /**
33528      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33529      */
33530     allowNegative : true,
33531     
33532     /**
33533      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33534      */
33535     allowZero: true,
33536     /**
33537      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33538      */
33539     minValue : Number.NEGATIVE_INFINITY,
33540     /**
33541      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33542      */
33543     maxValue : Number.MAX_VALUE,
33544     /**
33545      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33546      */
33547     minText : "The minimum value for this field is {0}",
33548     /**
33549      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33550      */
33551     maxText : "The maximum value for this field is {0}",
33552     /**
33553      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33554      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33555      */
33556     nanText : "{0} is not a valid number",
33557     /**
33558      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33559      */
33560     thousandsDelimiter : false,
33561     /**
33562      * @cfg {String} valueAlign alignment of value
33563      */
33564     valueAlign : "left",
33565
33566     getAutoCreate : function()
33567     {
33568         var hiddenInput = {
33569             tag: 'input',
33570             type: 'hidden',
33571             id: Roo.id(),
33572             cls: 'hidden-number-input'
33573         };
33574         
33575         if (this.name) {
33576             hiddenInput.name = this.name;
33577         }
33578         
33579         this.name = '';
33580         
33581         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33582         
33583         this.name = hiddenInput.name;
33584         
33585         if(cfg.cn.length > 0) {
33586             cfg.cn.push(hiddenInput);
33587         }
33588         
33589         return cfg;
33590     },
33591
33592     // private
33593     initEvents : function()
33594     {   
33595         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33596         
33597         var allowed = "0123456789";
33598         
33599         if(this.allowDecimals){
33600             allowed += this.decimalSeparator;
33601         }
33602         
33603         if(this.allowNegative){
33604             allowed += "-";
33605         }
33606         
33607         if(this.thousandsDelimiter) {
33608             allowed += ",";
33609         }
33610         
33611         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33612         
33613         var keyPress = function(e){
33614             
33615             var k = e.getKey();
33616             
33617             var c = e.getCharCode();
33618             
33619             if(
33620                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33621                     allowed.indexOf(String.fromCharCode(c)) === -1
33622             ){
33623                 e.stopEvent();
33624                 return;
33625             }
33626             
33627             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33628                 return;
33629             }
33630             
33631             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33632                 e.stopEvent();
33633             }
33634         };
33635         
33636         this.el.on("keypress", keyPress, this);
33637     },
33638     
33639     validateValue : function(value)
33640     {
33641         
33642         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33643             return false;
33644         }
33645         
33646         var num = this.parseValue(value);
33647         
33648         if(isNaN(num)){
33649             this.markInvalid(String.format(this.nanText, value));
33650             return false;
33651         }
33652         
33653         if(num < this.minValue){
33654             this.markInvalid(String.format(this.minText, this.minValue));
33655             return false;
33656         }
33657         
33658         if(num > this.maxValue){
33659             this.markInvalid(String.format(this.maxText, this.maxValue));
33660             return false;
33661         }
33662         
33663         return true;
33664     },
33665
33666     getValue : function()
33667     {
33668         var v = this.hiddenEl().getValue();
33669         
33670         return this.fixPrecision(this.parseValue(v));
33671     },
33672
33673     parseValue : function(value)
33674     {
33675         if(this.thousandsDelimiter) {
33676             value += "";
33677             r = new RegExp(",", "g");
33678             value = value.replace(r, "");
33679         }
33680         
33681         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33682         return isNaN(value) ? '' : value;
33683     },
33684
33685     fixPrecision : function(value)
33686     {
33687         if(this.thousandsDelimiter) {
33688             value += "";
33689             r = new RegExp(",", "g");
33690             value = value.replace(r, "");
33691         }
33692         
33693         var nan = isNaN(value);
33694         
33695         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33696             return nan ? '' : value;
33697         }
33698         return parseFloat(value).toFixed(this.decimalPrecision);
33699     },
33700
33701     setValue : function(v)
33702     {
33703         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33704         
33705         this.value = v;
33706         
33707         if(this.rendered){
33708             
33709             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33710             
33711             this.inputEl().dom.value = (v == '') ? '' :
33712                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33713             
33714             if(!this.allowZero && v === '0') {
33715                 this.hiddenEl().dom.value = '';
33716                 this.inputEl().dom.value = '';
33717             }
33718             
33719             this.validate();
33720         }
33721     },
33722
33723     decimalPrecisionFcn : function(v)
33724     {
33725         return Math.floor(v);
33726     },
33727
33728     beforeBlur : function()
33729     {
33730         var v = this.parseValue(this.getRawValue());
33731         
33732         if(v || v === 0 || v === ''){
33733             this.setValue(v);
33734         }
33735     },
33736     
33737     hiddenEl : function()
33738     {
33739         return this.el.select('input.hidden-number-input',true).first();
33740     }
33741     
33742 });
33743
33744  
33745
33746 /*
33747 * Licence: LGPL
33748 */
33749
33750 /**
33751  * @class Roo.bootstrap.DocumentSlider
33752  * @extends Roo.bootstrap.Component
33753  * Bootstrap DocumentSlider class
33754  * 
33755  * @constructor
33756  * Create a new DocumentViewer
33757  * @param {Object} config The config object
33758  */
33759
33760 Roo.bootstrap.DocumentSlider = function(config){
33761     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33762     
33763     this.files = [];
33764     
33765     this.addEvents({
33766         /**
33767          * @event initial
33768          * Fire after initEvent
33769          * @param {Roo.bootstrap.DocumentSlider} this
33770          */
33771         "initial" : true,
33772         /**
33773          * @event update
33774          * Fire after update
33775          * @param {Roo.bootstrap.DocumentSlider} this
33776          */
33777         "update" : true,
33778         /**
33779          * @event click
33780          * Fire after click
33781          * @param {Roo.bootstrap.DocumentSlider} this
33782          */
33783         "click" : true
33784     });
33785 };
33786
33787 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33788     
33789     files : false,
33790     
33791     indicator : 0,
33792     
33793     getAutoCreate : function()
33794     {
33795         var cfg = {
33796             tag : 'div',
33797             cls : 'roo-document-slider',
33798             cn : [
33799                 {
33800                     tag : 'div',
33801                     cls : 'roo-document-slider-header',
33802                     cn : [
33803                         {
33804                             tag : 'div',
33805                             cls : 'roo-document-slider-header-title'
33806                         }
33807                     ]
33808                 },
33809                 {
33810                     tag : 'div',
33811                     cls : 'roo-document-slider-body',
33812                     cn : [
33813                         {
33814                             tag : 'div',
33815                             cls : 'roo-document-slider-prev',
33816                             cn : [
33817                                 {
33818                                     tag : 'i',
33819                                     cls : 'fa fa-chevron-left'
33820                                 }
33821                             ]
33822                         },
33823                         {
33824                             tag : 'div',
33825                             cls : 'roo-document-slider-thumb',
33826                             cn : [
33827                                 {
33828                                     tag : 'img',
33829                                     cls : 'roo-document-slider-image'
33830                                 }
33831                             ]
33832                         },
33833                         {
33834                             tag : 'div',
33835                             cls : 'roo-document-slider-next',
33836                             cn : [
33837                                 {
33838                                     tag : 'i',
33839                                     cls : 'fa fa-chevron-right'
33840                                 }
33841                             ]
33842                         }
33843                     ]
33844                 }
33845             ]
33846         };
33847         
33848         return cfg;
33849     },
33850     
33851     initEvents : function()
33852     {
33853         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33854         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33855         
33856         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33857         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33858         
33859         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33860         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33861         
33862         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33863         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33864         
33865         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33866         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33867         
33868         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33869         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33870         
33871         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33872         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33873         
33874         this.thumbEl.on('click', this.onClick, this);
33875         
33876         this.prevIndicator.on('click', this.prev, this);
33877         
33878         this.nextIndicator.on('click', this.next, this);
33879         
33880     },
33881     
33882     initial : function()
33883     {
33884         if(this.files.length){
33885             this.indicator = 1;
33886             this.update()
33887         }
33888         
33889         this.fireEvent('initial', this);
33890     },
33891     
33892     update : function()
33893     {
33894         this.imageEl.attr('src', this.files[this.indicator - 1]);
33895         
33896         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33897         
33898         this.prevIndicator.show();
33899         
33900         if(this.indicator == 1){
33901             this.prevIndicator.hide();
33902         }
33903         
33904         this.nextIndicator.show();
33905         
33906         if(this.indicator == this.files.length){
33907             this.nextIndicator.hide();
33908         }
33909         
33910         this.thumbEl.scrollTo('top');
33911         
33912         this.fireEvent('update', this);
33913     },
33914     
33915     onClick : function(e)
33916     {
33917         e.preventDefault();
33918         
33919         this.fireEvent('click', this);
33920     },
33921     
33922     prev : function(e)
33923     {
33924         e.preventDefault();
33925         
33926         this.indicator = Math.max(1, this.indicator - 1);
33927         
33928         this.update();
33929     },
33930     
33931     next : function(e)
33932     {
33933         e.preventDefault();
33934         
33935         this.indicator = Math.min(this.files.length, this.indicator + 1);
33936         
33937         this.update();
33938     }
33939 });
33940 /*
33941  * - LGPL
33942  *
33943  * RadioSet
33944  *
33945  *
33946  */
33947
33948 /**
33949  * @class Roo.bootstrap.RadioSet
33950  * @extends Roo.bootstrap.Input
33951  * Bootstrap RadioSet class
33952  * @cfg {String} indicatorpos (left|right) default left
33953  * @cfg {Boolean} inline (true|false) inline the element (default true)
33954  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33955  * @constructor
33956  * Create a new RadioSet
33957  * @param {Object} config The config object
33958  */
33959
33960 Roo.bootstrap.RadioSet = function(config){
33961     
33962     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33963     
33964     this.radioes = [];
33965     
33966     Roo.bootstrap.RadioSet.register(this);
33967     
33968     this.addEvents({
33969         /**
33970         * @event check
33971         * Fires when the element is checked or unchecked.
33972         * @param {Roo.bootstrap.RadioSet} this This radio
33973         * @param {Roo.bootstrap.Radio} item The checked item
33974         */
33975        check : true,
33976        /**
33977         * @event click
33978         * Fires when the element is click.
33979         * @param {Roo.bootstrap.RadioSet} this This radio set
33980         * @param {Roo.bootstrap.Radio} item The checked item
33981         * @param {Roo.EventObject} e The event object
33982         */
33983        click : true
33984     });
33985     
33986 };
33987
33988 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33989
33990     radioes : false,
33991     
33992     inline : true,
33993     
33994     weight : '',
33995     
33996     indicatorpos : 'left',
33997     
33998     getAutoCreate : function()
33999     {
34000         var label = {
34001             tag : 'label',
34002             cls : 'roo-radio-set-label',
34003             cn : [
34004                 {
34005                     tag : 'span',
34006                     html : this.fieldLabel
34007                 }
34008             ]
34009         };
34010         
34011         if(this.indicatorpos == 'left'){
34012             label.cn.unshift({
34013                 tag : 'i',
34014                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34015                 tooltip : 'This field is required'
34016             });
34017         } else {
34018             label.cn.push({
34019                 tag : 'i',
34020                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34021                 tooltip : 'This field is required'
34022             });
34023         }
34024         
34025         var items = {
34026             tag : 'div',
34027             cls : 'roo-radio-set-items'
34028         };
34029         
34030         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34031         
34032         if (align === 'left' && this.fieldLabel.length) {
34033             
34034             items = {
34035                 cls : "roo-radio-set-right", 
34036                 cn: [
34037                     items
34038                 ]
34039             };
34040             
34041             if(this.labelWidth > 12){
34042                 label.style = "width: " + this.labelWidth + 'px';
34043             }
34044             
34045             if(this.labelWidth < 13 && this.labelmd == 0){
34046                 this.labelmd = this.labelWidth;
34047             }
34048             
34049             if(this.labellg > 0){
34050                 label.cls += ' col-lg-' + this.labellg;
34051                 items.cls += ' col-lg-' + (12 - this.labellg);
34052             }
34053             
34054             if(this.labelmd > 0){
34055                 label.cls += ' col-md-' + this.labelmd;
34056                 items.cls += ' col-md-' + (12 - this.labelmd);
34057             }
34058             
34059             if(this.labelsm > 0){
34060                 label.cls += ' col-sm-' + this.labelsm;
34061                 items.cls += ' col-sm-' + (12 - this.labelsm);
34062             }
34063             
34064             if(this.labelxs > 0){
34065                 label.cls += ' col-xs-' + this.labelxs;
34066                 items.cls += ' col-xs-' + (12 - this.labelxs);
34067             }
34068         }
34069         
34070         var cfg = {
34071             tag : 'div',
34072             cls : 'roo-radio-set',
34073             cn : [
34074                 {
34075                     tag : 'input',
34076                     cls : 'roo-radio-set-input',
34077                     type : 'hidden',
34078                     name : this.name,
34079                     value : this.value ? this.value :  ''
34080                 },
34081                 label,
34082                 items
34083             ]
34084         };
34085         
34086         if(this.weight.length){
34087             cfg.cls += ' roo-radio-' + this.weight;
34088         }
34089         
34090         if(this.inline) {
34091             cfg.cls += ' roo-radio-set-inline';
34092         }
34093         
34094         var settings=this;
34095         ['xs','sm','md','lg'].map(function(size){
34096             if (settings[size]) {
34097                 cfg.cls += ' col-' + size + '-' + settings[size];
34098             }
34099         });
34100         
34101         return cfg;
34102         
34103     },
34104
34105     initEvents : function()
34106     {
34107         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34108         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34109         
34110         if(!this.fieldLabel.length){
34111             this.labelEl.hide();
34112         }
34113         
34114         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34115         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34116         
34117         this.indicator = this.indicatorEl();
34118         
34119         if(this.indicator){
34120             this.indicator.addClass('invisible');
34121         }
34122         
34123         this.originalValue = this.getValue();
34124         
34125     },
34126     
34127     inputEl: function ()
34128     {
34129         return this.el.select('.roo-radio-set-input', true).first();
34130     },
34131     
34132     getChildContainer : function()
34133     {
34134         return this.itemsEl;
34135     },
34136     
34137     register : function(item)
34138     {
34139         this.radioes.push(item);
34140         
34141     },
34142     
34143     validate : function()
34144     {   
34145         if(this.getVisibilityEl().hasClass('hidden')){
34146             return true;
34147         }
34148         
34149         var valid = false;
34150         
34151         Roo.each(this.radioes, function(i){
34152             if(!i.checked){
34153                 return;
34154             }
34155             
34156             valid = true;
34157             return false;
34158         });
34159         
34160         if(this.allowBlank) {
34161             return true;
34162         }
34163         
34164         if(this.disabled || valid){
34165             this.markValid();
34166             return true;
34167         }
34168         
34169         this.markInvalid();
34170         return false;
34171         
34172     },
34173     
34174     markValid : function()
34175     {
34176         if(this.labelEl.isVisible(true)){
34177             this.indicatorEl().removeClass('visible');
34178             this.indicatorEl().addClass('invisible');
34179         }
34180         
34181         this.el.removeClass([this.invalidClass, this.validClass]);
34182         this.el.addClass(this.validClass);
34183         
34184         this.fireEvent('valid', this);
34185     },
34186     
34187     markInvalid : function(msg)
34188     {
34189         if(this.allowBlank || this.disabled){
34190             return;
34191         }
34192         
34193         if(this.labelEl.isVisible(true)){
34194             this.indicatorEl().removeClass('invisible');
34195             this.indicatorEl().addClass('visible');
34196         }
34197         
34198         this.el.removeClass([this.invalidClass, this.validClass]);
34199         this.el.addClass(this.invalidClass);
34200         
34201         this.fireEvent('invalid', this, msg);
34202         
34203     },
34204     
34205     setValue : function(v, suppressEvent)
34206     {   
34207         if(this.value === v){
34208             return;
34209         }
34210         
34211         this.value = v;
34212         
34213         if(this.rendered){
34214             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34215         }
34216         
34217         Roo.each(this.radioes, function(i){
34218             i.checked = false;
34219             i.el.removeClass('checked');
34220         });
34221         
34222         Roo.each(this.radioes, function(i){
34223             
34224             if(i.value === v || i.value.toString() === v.toString()){
34225                 i.checked = true;
34226                 i.el.addClass('checked');
34227                 
34228                 if(suppressEvent !== true){
34229                     this.fireEvent('check', this, i);
34230                 }
34231                 
34232                 return false;
34233             }
34234             
34235         }, this);
34236         
34237         this.validate();
34238     },
34239     
34240     clearInvalid : function(){
34241         
34242         if(!this.el || this.preventMark){
34243             return;
34244         }
34245         
34246         this.el.removeClass([this.invalidClass]);
34247         
34248         this.fireEvent('valid', this);
34249     }
34250     
34251 });
34252
34253 Roo.apply(Roo.bootstrap.RadioSet, {
34254     
34255     groups: {},
34256     
34257     register : function(set)
34258     {
34259         this.groups[set.name] = set;
34260     },
34261     
34262     get: function(name) 
34263     {
34264         if (typeof(this.groups[name]) == 'undefined') {
34265             return false;
34266         }
34267         
34268         return this.groups[name] ;
34269     }
34270     
34271 });
34272 /*
34273  * Based on:
34274  * Ext JS Library 1.1.1
34275  * Copyright(c) 2006-2007, Ext JS, LLC.
34276  *
34277  * Originally Released Under LGPL - original licence link has changed is not relivant.
34278  *
34279  * Fork - LGPL
34280  * <script type="text/javascript">
34281  */
34282
34283
34284 /**
34285  * @class Roo.bootstrap.SplitBar
34286  * @extends Roo.util.Observable
34287  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34288  * <br><br>
34289  * Usage:
34290  * <pre><code>
34291 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34292                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34293 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34294 split.minSize = 100;
34295 split.maxSize = 600;
34296 split.animate = true;
34297 split.on('moved', splitterMoved);
34298 </code></pre>
34299  * @constructor
34300  * Create a new SplitBar
34301  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34302  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34303  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34304  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34305                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34306                         position of the SplitBar).
34307  */
34308 Roo.bootstrap.SplitBar = function(cfg){
34309     
34310     /** @private */
34311     
34312     //{
34313     //  dragElement : elm
34314     //  resizingElement: el,
34315         // optional..
34316     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34317     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34318         // existingProxy ???
34319     //}
34320     
34321     this.el = Roo.get(cfg.dragElement, true);
34322     this.el.dom.unselectable = "on";
34323     /** @private */
34324     this.resizingEl = Roo.get(cfg.resizingElement, true);
34325
34326     /**
34327      * @private
34328      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34329      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34330      * @type Number
34331      */
34332     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34333     
34334     /**
34335      * The minimum size of the resizing element. (Defaults to 0)
34336      * @type Number
34337      */
34338     this.minSize = 0;
34339     
34340     /**
34341      * The maximum size of the resizing element. (Defaults to 2000)
34342      * @type Number
34343      */
34344     this.maxSize = 2000;
34345     
34346     /**
34347      * Whether to animate the transition to the new size
34348      * @type Boolean
34349      */
34350     this.animate = false;
34351     
34352     /**
34353      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34354      * @type Boolean
34355      */
34356     this.useShim = false;
34357     
34358     /** @private */
34359     this.shim = null;
34360     
34361     if(!cfg.existingProxy){
34362         /** @private */
34363         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34364     }else{
34365         this.proxy = Roo.get(cfg.existingProxy).dom;
34366     }
34367     /** @private */
34368     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34369     
34370     /** @private */
34371     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34372     
34373     /** @private */
34374     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34375     
34376     /** @private */
34377     this.dragSpecs = {};
34378     
34379     /**
34380      * @private The adapter to use to positon and resize elements
34381      */
34382     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34383     this.adapter.init(this);
34384     
34385     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34386         /** @private */
34387         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34388         this.el.addClass("roo-splitbar-h");
34389     }else{
34390         /** @private */
34391         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34392         this.el.addClass("roo-splitbar-v");
34393     }
34394     
34395     this.addEvents({
34396         /**
34397          * @event resize
34398          * Fires when the splitter is moved (alias for {@link #event-moved})
34399          * @param {Roo.bootstrap.SplitBar} this
34400          * @param {Number} newSize the new width or height
34401          */
34402         "resize" : true,
34403         /**
34404          * @event moved
34405          * Fires when the splitter is moved
34406          * @param {Roo.bootstrap.SplitBar} this
34407          * @param {Number} newSize the new width or height
34408          */
34409         "moved" : true,
34410         /**
34411          * @event beforeresize
34412          * Fires before the splitter is dragged
34413          * @param {Roo.bootstrap.SplitBar} this
34414          */
34415         "beforeresize" : true,
34416
34417         "beforeapply" : true
34418     });
34419
34420     Roo.util.Observable.call(this);
34421 };
34422
34423 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34424     onStartProxyDrag : function(x, y){
34425         this.fireEvent("beforeresize", this);
34426         if(!this.overlay){
34427             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34428             o.unselectable();
34429             o.enableDisplayMode("block");
34430             // all splitbars share the same overlay
34431             Roo.bootstrap.SplitBar.prototype.overlay = o;
34432         }
34433         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34434         this.overlay.show();
34435         Roo.get(this.proxy).setDisplayed("block");
34436         var size = this.adapter.getElementSize(this);
34437         this.activeMinSize = this.getMinimumSize();;
34438         this.activeMaxSize = this.getMaximumSize();;
34439         var c1 = size - this.activeMinSize;
34440         var c2 = Math.max(this.activeMaxSize - size, 0);
34441         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34442             this.dd.resetConstraints();
34443             this.dd.setXConstraint(
34444                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34445                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34446             );
34447             this.dd.setYConstraint(0, 0);
34448         }else{
34449             this.dd.resetConstraints();
34450             this.dd.setXConstraint(0, 0);
34451             this.dd.setYConstraint(
34452                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34453                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34454             );
34455          }
34456         this.dragSpecs.startSize = size;
34457         this.dragSpecs.startPoint = [x, y];
34458         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34459     },
34460     
34461     /** 
34462      * @private Called after the drag operation by the DDProxy
34463      */
34464     onEndProxyDrag : function(e){
34465         Roo.get(this.proxy).setDisplayed(false);
34466         var endPoint = Roo.lib.Event.getXY(e);
34467         if(this.overlay){
34468             this.overlay.hide();
34469         }
34470         var newSize;
34471         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34472             newSize = this.dragSpecs.startSize + 
34473                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34474                     endPoint[0] - this.dragSpecs.startPoint[0] :
34475                     this.dragSpecs.startPoint[0] - endPoint[0]
34476                 );
34477         }else{
34478             newSize = this.dragSpecs.startSize + 
34479                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34480                     endPoint[1] - this.dragSpecs.startPoint[1] :
34481                     this.dragSpecs.startPoint[1] - endPoint[1]
34482                 );
34483         }
34484         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34485         if(newSize != this.dragSpecs.startSize){
34486             if(this.fireEvent('beforeapply', this, newSize) !== false){
34487                 this.adapter.setElementSize(this, newSize);
34488                 this.fireEvent("moved", this, newSize);
34489                 this.fireEvent("resize", this, newSize);
34490             }
34491         }
34492     },
34493     
34494     /**
34495      * Get the adapter this SplitBar uses
34496      * @return The adapter object
34497      */
34498     getAdapter : function(){
34499         return this.adapter;
34500     },
34501     
34502     /**
34503      * Set the adapter this SplitBar uses
34504      * @param {Object} adapter A SplitBar adapter object
34505      */
34506     setAdapter : function(adapter){
34507         this.adapter = adapter;
34508         this.adapter.init(this);
34509     },
34510     
34511     /**
34512      * Gets the minimum size for the resizing element
34513      * @return {Number} The minimum size
34514      */
34515     getMinimumSize : function(){
34516         return this.minSize;
34517     },
34518     
34519     /**
34520      * Sets the minimum size for the resizing element
34521      * @param {Number} minSize The minimum size
34522      */
34523     setMinimumSize : function(minSize){
34524         this.minSize = minSize;
34525     },
34526     
34527     /**
34528      * Gets the maximum size for the resizing element
34529      * @return {Number} The maximum size
34530      */
34531     getMaximumSize : function(){
34532         return this.maxSize;
34533     },
34534     
34535     /**
34536      * Sets the maximum size for the resizing element
34537      * @param {Number} maxSize The maximum size
34538      */
34539     setMaximumSize : function(maxSize){
34540         this.maxSize = maxSize;
34541     },
34542     
34543     /**
34544      * Sets the initialize size for the resizing element
34545      * @param {Number} size The initial size
34546      */
34547     setCurrentSize : function(size){
34548         var oldAnimate = this.animate;
34549         this.animate = false;
34550         this.adapter.setElementSize(this, size);
34551         this.animate = oldAnimate;
34552     },
34553     
34554     /**
34555      * Destroy this splitbar. 
34556      * @param {Boolean} removeEl True to remove the element
34557      */
34558     destroy : function(removeEl){
34559         if(this.shim){
34560             this.shim.remove();
34561         }
34562         this.dd.unreg();
34563         this.proxy.parentNode.removeChild(this.proxy);
34564         if(removeEl){
34565             this.el.remove();
34566         }
34567     }
34568 });
34569
34570 /**
34571  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
34572  */
34573 Roo.bootstrap.SplitBar.createProxy = function(dir){
34574     var proxy = new Roo.Element(document.createElement("div"));
34575     proxy.unselectable();
34576     var cls = 'roo-splitbar-proxy';
34577     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34578     document.body.appendChild(proxy.dom);
34579     return proxy.dom;
34580 };
34581
34582 /** 
34583  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34584  * Default Adapter. It assumes the splitter and resizing element are not positioned
34585  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34586  */
34587 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34588 };
34589
34590 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34591     // do nothing for now
34592     init : function(s){
34593     
34594     },
34595     /**
34596      * Called before drag operations to get the current size of the resizing element. 
34597      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34598      */
34599      getElementSize : function(s){
34600         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34601             return s.resizingEl.getWidth();
34602         }else{
34603             return s.resizingEl.getHeight();
34604         }
34605     },
34606     
34607     /**
34608      * Called after drag operations to set the size of the resizing element.
34609      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34610      * @param {Number} newSize The new size to set
34611      * @param {Function} onComplete A function to be invoked when resizing is complete
34612      */
34613     setElementSize : function(s, newSize, onComplete){
34614         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34615             if(!s.animate){
34616                 s.resizingEl.setWidth(newSize);
34617                 if(onComplete){
34618                     onComplete(s, newSize);
34619                 }
34620             }else{
34621                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34622             }
34623         }else{
34624             
34625             if(!s.animate){
34626                 s.resizingEl.setHeight(newSize);
34627                 if(onComplete){
34628                     onComplete(s, newSize);
34629                 }
34630             }else{
34631                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34632             }
34633         }
34634     }
34635 };
34636
34637 /** 
34638  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34639  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34640  * Adapter that  moves the splitter element to align with the resized sizing element. 
34641  * Used with an absolute positioned SplitBar.
34642  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34643  * document.body, make sure you assign an id to the body element.
34644  */
34645 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34646     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34647     this.container = Roo.get(container);
34648 };
34649
34650 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34651     init : function(s){
34652         this.basic.init(s);
34653     },
34654     
34655     getElementSize : function(s){
34656         return this.basic.getElementSize(s);
34657     },
34658     
34659     setElementSize : function(s, newSize, onComplete){
34660         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34661     },
34662     
34663     moveSplitter : function(s){
34664         var yes = Roo.bootstrap.SplitBar;
34665         switch(s.placement){
34666             case yes.LEFT:
34667                 s.el.setX(s.resizingEl.getRight());
34668                 break;
34669             case yes.RIGHT:
34670                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34671                 break;
34672             case yes.TOP:
34673                 s.el.setY(s.resizingEl.getBottom());
34674                 break;
34675             case yes.BOTTOM:
34676                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34677                 break;
34678         }
34679     }
34680 };
34681
34682 /**
34683  * Orientation constant - Create a vertical SplitBar
34684  * @static
34685  * @type Number
34686  */
34687 Roo.bootstrap.SplitBar.VERTICAL = 1;
34688
34689 /**
34690  * Orientation constant - Create a horizontal SplitBar
34691  * @static
34692  * @type Number
34693  */
34694 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34695
34696 /**
34697  * Placement constant - The resizing element is to the left of the splitter element
34698  * @static
34699  * @type Number
34700  */
34701 Roo.bootstrap.SplitBar.LEFT = 1;
34702
34703 /**
34704  * Placement constant - The resizing element is to the right of the splitter element
34705  * @static
34706  * @type Number
34707  */
34708 Roo.bootstrap.SplitBar.RIGHT = 2;
34709
34710 /**
34711  * Placement constant - The resizing element is positioned above the splitter element
34712  * @static
34713  * @type Number
34714  */
34715 Roo.bootstrap.SplitBar.TOP = 3;
34716
34717 /**
34718  * Placement constant - The resizing element is positioned under splitter element
34719  * @static
34720  * @type Number
34721  */
34722 Roo.bootstrap.SplitBar.BOTTOM = 4;
34723 Roo.namespace("Roo.bootstrap.layout");/*
34724  * Based on:
34725  * Ext JS Library 1.1.1
34726  * Copyright(c) 2006-2007, Ext JS, LLC.
34727  *
34728  * Originally Released Under LGPL - original licence link has changed is not relivant.
34729  *
34730  * Fork - LGPL
34731  * <script type="text/javascript">
34732  */
34733
34734 /**
34735  * @class Roo.bootstrap.layout.Manager
34736  * @extends Roo.bootstrap.Component
34737  * Base class for layout managers.
34738  */
34739 Roo.bootstrap.layout.Manager = function(config)
34740 {
34741     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34742
34743
34744
34745
34746
34747     /** false to disable window resize monitoring @type Boolean */
34748     this.monitorWindowResize = true;
34749     this.regions = {};
34750     this.addEvents({
34751         /**
34752          * @event layout
34753          * Fires when a layout is performed.
34754          * @param {Roo.LayoutManager} this
34755          */
34756         "layout" : true,
34757         /**
34758          * @event regionresized
34759          * Fires when the user resizes a region.
34760          * @param {Roo.LayoutRegion} region The resized region
34761          * @param {Number} newSize The new size (width for east/west, height for north/south)
34762          */
34763         "regionresized" : true,
34764         /**
34765          * @event regioncollapsed
34766          * Fires when a region is collapsed.
34767          * @param {Roo.LayoutRegion} region The collapsed region
34768          */
34769         "regioncollapsed" : true,
34770         /**
34771          * @event regionexpanded
34772          * Fires when a region is expanded.
34773          * @param {Roo.LayoutRegion} region The expanded region
34774          */
34775         "regionexpanded" : true
34776     });
34777     this.updating = false;
34778
34779     if (config.el) {
34780         this.el = Roo.get(config.el);
34781         this.initEvents();
34782     }
34783
34784 };
34785
34786 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34787
34788
34789     regions : null,
34790
34791     monitorWindowResize : true,
34792
34793
34794     updating : false,
34795
34796
34797     onRender : function(ct, position)
34798     {
34799         if(!this.el){
34800             this.el = Roo.get(ct);
34801             this.initEvents();
34802         }
34803         //this.fireEvent('render',this);
34804     },
34805
34806
34807     initEvents: function()
34808     {
34809
34810
34811         // ie scrollbar fix
34812         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34813             document.body.scroll = "no";
34814         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34815             this.el.position('relative');
34816         }
34817         this.id = this.el.id;
34818         this.el.addClass("roo-layout-container");
34819         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34820         if(this.el.dom != document.body ) {
34821             this.el.on('resize', this.layout,this);
34822             this.el.on('show', this.layout,this);
34823         }
34824
34825     },
34826
34827     /**
34828      * Returns true if this layout is currently being updated
34829      * @return {Boolean}
34830      */
34831     isUpdating : function(){
34832         return this.updating;
34833     },
34834
34835     /**
34836      * Suspend the LayoutManager from doing auto-layouts while
34837      * making multiple add or remove calls
34838      */
34839     beginUpdate : function(){
34840         this.updating = true;
34841     },
34842
34843     /**
34844      * Restore auto-layouts and optionally disable the manager from performing a layout
34845      * @param {Boolean} noLayout true to disable a layout update
34846      */
34847     endUpdate : function(noLayout){
34848         this.updating = false;
34849         if(!noLayout){
34850             this.layout();
34851         }
34852     },
34853
34854     layout: function(){
34855         // abstract...
34856     },
34857
34858     onRegionResized : function(region, newSize){
34859         this.fireEvent("regionresized", region, newSize);
34860         this.layout();
34861     },
34862
34863     onRegionCollapsed : function(region){
34864         this.fireEvent("regioncollapsed", region);
34865     },
34866
34867     onRegionExpanded : function(region){
34868         this.fireEvent("regionexpanded", region);
34869     },
34870
34871     /**
34872      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34873      * performs box-model adjustments.
34874      * @return {Object} The size as an object {width: (the width), height: (the height)}
34875      */
34876     getViewSize : function()
34877     {
34878         var size;
34879         if(this.el.dom != document.body){
34880             size = this.el.getSize();
34881         }else{
34882             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34883         }
34884         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34885         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34886         return size;
34887     },
34888
34889     /**
34890      * Returns the Element this layout is bound to.
34891      * @return {Roo.Element}
34892      */
34893     getEl : function(){
34894         return this.el;
34895     },
34896
34897     /**
34898      * Returns the specified region.
34899      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34900      * @return {Roo.LayoutRegion}
34901      */
34902     getRegion : function(target){
34903         return this.regions[target.toLowerCase()];
34904     },
34905
34906     onWindowResize : function(){
34907         if(this.monitorWindowResize){
34908             this.layout();
34909         }
34910     }
34911 });
34912 /*
34913  * Based on:
34914  * Ext JS Library 1.1.1
34915  * Copyright(c) 2006-2007, Ext JS, LLC.
34916  *
34917  * Originally Released Under LGPL - original licence link has changed is not relivant.
34918  *
34919  * Fork - LGPL
34920  * <script type="text/javascript">
34921  */
34922 /**
34923  * @class Roo.bootstrap.layout.Border
34924  * @extends Roo.bootstrap.layout.Manager
34925  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34926  * please see: examples/bootstrap/nested.html<br><br>
34927  
34928 <b>The container the layout is rendered into can be either the body element or any other element.
34929 If it is not the body element, the container needs to either be an absolute positioned element,
34930 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34931 the container size if it is not the body element.</b>
34932
34933 * @constructor
34934 * Create a new Border
34935 * @param {Object} config Configuration options
34936  */
34937 Roo.bootstrap.layout.Border = function(config){
34938     config = config || {};
34939     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34940     
34941     
34942     
34943     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34944         if(config[region]){
34945             config[region].region = region;
34946             this.addRegion(config[region]);
34947         }
34948     },this);
34949     
34950 };
34951
34952 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34953
34954 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34955     /**
34956      * Creates and adds a new region if it doesn't already exist.
34957      * @param {String} target The target region key (north, south, east, west or center).
34958      * @param {Object} config The regions config object
34959      * @return {BorderLayoutRegion} The new region
34960      */
34961     addRegion : function(config)
34962     {
34963         if(!this.regions[config.region]){
34964             var r = this.factory(config);
34965             this.bindRegion(r);
34966         }
34967         return this.regions[config.region];
34968     },
34969
34970     // private (kinda)
34971     bindRegion : function(r){
34972         this.regions[r.config.region] = r;
34973         
34974         r.on("visibilitychange",    this.layout, this);
34975         r.on("paneladded",          this.layout, this);
34976         r.on("panelremoved",        this.layout, this);
34977         r.on("invalidated",         this.layout, this);
34978         r.on("resized",             this.onRegionResized, this);
34979         r.on("collapsed",           this.onRegionCollapsed, this);
34980         r.on("expanded",            this.onRegionExpanded, this);
34981     },
34982
34983     /**
34984      * Performs a layout update.
34985      */
34986     layout : function()
34987     {
34988         if(this.updating) {
34989             return;
34990         }
34991         
34992         // render all the rebions if they have not been done alreayd?
34993         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34994             if(this.regions[region] && !this.regions[region].bodyEl){
34995                 this.regions[region].onRender(this.el)
34996             }
34997         },this);
34998         
34999         var size = this.getViewSize();
35000         var w = size.width;
35001         var h = size.height;
35002         var centerW = w;
35003         var centerH = h;
35004         var centerY = 0;
35005         var centerX = 0;
35006         //var x = 0, y = 0;
35007
35008         var rs = this.regions;
35009         var north = rs["north"];
35010         var south = rs["south"]; 
35011         var west = rs["west"];
35012         var east = rs["east"];
35013         var center = rs["center"];
35014         //if(this.hideOnLayout){ // not supported anymore
35015             //c.el.setStyle("display", "none");
35016         //}
35017         if(north && north.isVisible()){
35018             var b = north.getBox();
35019             var m = north.getMargins();
35020             b.width = w - (m.left+m.right);
35021             b.x = m.left;
35022             b.y = m.top;
35023             centerY = b.height + b.y + m.bottom;
35024             centerH -= centerY;
35025             north.updateBox(this.safeBox(b));
35026         }
35027         if(south && south.isVisible()){
35028             var b = south.getBox();
35029             var m = south.getMargins();
35030             b.width = w - (m.left+m.right);
35031             b.x = m.left;
35032             var totalHeight = (b.height + m.top + m.bottom);
35033             b.y = h - totalHeight + m.top;
35034             centerH -= totalHeight;
35035             south.updateBox(this.safeBox(b));
35036         }
35037         if(west && west.isVisible()){
35038             var b = west.getBox();
35039             var m = west.getMargins();
35040             b.height = centerH - (m.top+m.bottom);
35041             b.x = m.left;
35042             b.y = centerY + m.top;
35043             var totalWidth = (b.width + m.left + m.right);
35044             centerX += totalWidth;
35045             centerW -= totalWidth;
35046             west.updateBox(this.safeBox(b));
35047         }
35048         if(east && east.isVisible()){
35049             var b = east.getBox();
35050             var m = east.getMargins();
35051             b.height = centerH - (m.top+m.bottom);
35052             var totalWidth = (b.width + m.left + m.right);
35053             b.x = w - totalWidth + m.left;
35054             b.y = centerY + m.top;
35055             centerW -= totalWidth;
35056             east.updateBox(this.safeBox(b));
35057         }
35058         if(center){
35059             var m = center.getMargins();
35060             var centerBox = {
35061                 x: centerX + m.left,
35062                 y: centerY + m.top,
35063                 width: centerW - (m.left+m.right),
35064                 height: centerH - (m.top+m.bottom)
35065             };
35066             //if(this.hideOnLayout){
35067                 //center.el.setStyle("display", "block");
35068             //}
35069             center.updateBox(this.safeBox(centerBox));
35070         }
35071         this.el.repaint();
35072         this.fireEvent("layout", this);
35073     },
35074
35075     // private
35076     safeBox : function(box){
35077         box.width = Math.max(0, box.width);
35078         box.height = Math.max(0, box.height);
35079         return box;
35080     },
35081
35082     /**
35083      * Adds a ContentPanel (or subclass) to this layout.
35084      * @param {String} target The target region key (north, south, east, west or center).
35085      * @param {Roo.ContentPanel} panel The panel to add
35086      * @return {Roo.ContentPanel} The added panel
35087      */
35088     add : function(target, panel){
35089          
35090         target = target.toLowerCase();
35091         return this.regions[target].add(panel);
35092     },
35093
35094     /**
35095      * Remove a ContentPanel (or subclass) to this layout.
35096      * @param {String} target The target region key (north, south, east, west or center).
35097      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35098      * @return {Roo.ContentPanel} The removed panel
35099      */
35100     remove : function(target, panel){
35101         target = target.toLowerCase();
35102         return this.regions[target].remove(panel);
35103     },
35104
35105     /**
35106      * Searches all regions for a panel with the specified id
35107      * @param {String} panelId
35108      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35109      */
35110     findPanel : function(panelId){
35111         var rs = this.regions;
35112         for(var target in rs){
35113             if(typeof rs[target] != "function"){
35114                 var p = rs[target].getPanel(panelId);
35115                 if(p){
35116                     return p;
35117                 }
35118             }
35119         }
35120         return null;
35121     },
35122
35123     /**
35124      * Searches all regions for a panel with the specified id and activates (shows) it.
35125      * @param {String/ContentPanel} panelId The panels id or the panel itself
35126      * @return {Roo.ContentPanel} The shown panel or null
35127      */
35128     showPanel : function(panelId) {
35129       var rs = this.regions;
35130       for(var target in rs){
35131          var r = rs[target];
35132          if(typeof r != "function"){
35133             if(r.hasPanel(panelId)){
35134                return r.showPanel(panelId);
35135             }
35136          }
35137       }
35138       return null;
35139    },
35140
35141    /**
35142      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35143      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35144      */
35145    /*
35146     restoreState : function(provider){
35147         if(!provider){
35148             provider = Roo.state.Manager;
35149         }
35150         var sm = new Roo.LayoutStateManager();
35151         sm.init(this, provider);
35152     },
35153 */
35154  
35155  
35156     /**
35157      * Adds a xtype elements to the layout.
35158      * <pre><code>
35159
35160 layout.addxtype({
35161        xtype : 'ContentPanel',
35162        region: 'west',
35163        items: [ .... ]
35164    }
35165 );
35166
35167 layout.addxtype({
35168         xtype : 'NestedLayoutPanel',
35169         region: 'west',
35170         layout: {
35171            center: { },
35172            west: { }   
35173         },
35174         items : [ ... list of content panels or nested layout panels.. ]
35175    }
35176 );
35177 </code></pre>
35178      * @param {Object} cfg Xtype definition of item to add.
35179      */
35180     addxtype : function(cfg)
35181     {
35182         // basically accepts a pannel...
35183         // can accept a layout region..!?!?
35184         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35185         
35186         
35187         // theory?  children can only be panels??
35188         
35189         //if (!cfg.xtype.match(/Panel$/)) {
35190         //    return false;
35191         //}
35192         var ret = false;
35193         
35194         if (typeof(cfg.region) == 'undefined') {
35195             Roo.log("Failed to add Panel, region was not set");
35196             Roo.log(cfg);
35197             return false;
35198         }
35199         var region = cfg.region;
35200         delete cfg.region;
35201         
35202           
35203         var xitems = [];
35204         if (cfg.items) {
35205             xitems = cfg.items;
35206             delete cfg.items;
35207         }
35208         var nb = false;
35209         
35210         switch(cfg.xtype) 
35211         {
35212             case 'Content':  // ContentPanel (el, cfg)
35213             case 'Scroll':  // ContentPanel (el, cfg)
35214             case 'View': 
35215                 cfg.autoCreate = true;
35216                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35217                 //} else {
35218                 //    var el = this.el.createChild();
35219                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35220                 //}
35221                 
35222                 this.add(region, ret);
35223                 break;
35224             
35225             /*
35226             case 'TreePanel': // our new panel!
35227                 cfg.el = this.el.createChild();
35228                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35229                 this.add(region, ret);
35230                 break;
35231             */
35232             
35233             case 'Nest': 
35234                 // create a new Layout (which is  a Border Layout...
35235                 
35236                 var clayout = cfg.layout;
35237                 clayout.el  = this.el.createChild();
35238                 clayout.items   = clayout.items  || [];
35239                 
35240                 delete cfg.layout;
35241                 
35242                 // replace this exitems with the clayout ones..
35243                 xitems = clayout.items;
35244                  
35245                 // force background off if it's in center...
35246                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35247                     cfg.background = false;
35248                 }
35249                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35250                 
35251                 
35252                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35253                 //console.log('adding nested layout panel '  + cfg.toSource());
35254                 this.add(region, ret);
35255                 nb = {}; /// find first...
35256                 break;
35257             
35258             case 'Grid':
35259                 
35260                 // needs grid and region
35261                 
35262                 //var el = this.getRegion(region).el.createChild();
35263                 /*
35264                  *var el = this.el.createChild();
35265                 // create the grid first...
35266                 cfg.grid.container = el;
35267                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35268                 */
35269                 
35270                 if (region == 'center' && this.active ) {
35271                     cfg.background = false;
35272                 }
35273                 
35274                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35275                 
35276                 this.add(region, ret);
35277                 /*
35278                 if (cfg.background) {
35279                     // render grid on panel activation (if panel background)
35280                     ret.on('activate', function(gp) {
35281                         if (!gp.grid.rendered) {
35282                     //        gp.grid.render(el);
35283                         }
35284                     });
35285                 } else {
35286                   //  cfg.grid.render(el);
35287                 }
35288                 */
35289                 break;
35290            
35291            
35292             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35293                 // it was the old xcomponent building that caused this before.
35294                 // espeically if border is the top element in the tree.
35295                 ret = this;
35296                 break; 
35297                 
35298                     
35299                 
35300                 
35301                 
35302             default:
35303                 /*
35304                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35305                     
35306                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35307                     this.add(region, ret);
35308                 } else {
35309                 */
35310                     Roo.log(cfg);
35311                     throw "Can not add '" + cfg.xtype + "' to Border";
35312                     return null;
35313              
35314                                 
35315              
35316         }
35317         this.beginUpdate();
35318         // add children..
35319         var region = '';
35320         var abn = {};
35321         Roo.each(xitems, function(i)  {
35322             region = nb && i.region ? i.region : false;
35323             
35324             var add = ret.addxtype(i);
35325            
35326             if (region) {
35327                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35328                 if (!i.background) {
35329                     abn[region] = nb[region] ;
35330                 }
35331             }
35332             
35333         });
35334         this.endUpdate();
35335
35336         // make the last non-background panel active..
35337         //if (nb) { Roo.log(abn); }
35338         if (nb) {
35339             
35340             for(var r in abn) {
35341                 region = this.getRegion(r);
35342                 if (region) {
35343                     // tried using nb[r], but it does not work..
35344                      
35345                     region.showPanel(abn[r]);
35346                    
35347                 }
35348             }
35349         }
35350         return ret;
35351         
35352     },
35353     
35354     
35355 // private
35356     factory : function(cfg)
35357     {
35358         
35359         var validRegions = Roo.bootstrap.layout.Border.regions;
35360
35361         var target = cfg.region;
35362         cfg.mgr = this;
35363         
35364         var r = Roo.bootstrap.layout;
35365         Roo.log(target);
35366         switch(target){
35367             case "north":
35368                 return new r.North(cfg);
35369             case "south":
35370                 return new r.South(cfg);
35371             case "east":
35372                 return new r.East(cfg);
35373             case "west":
35374                 return new r.West(cfg);
35375             case "center":
35376                 return new r.Center(cfg);
35377         }
35378         throw 'Layout region "'+target+'" not supported.';
35379     }
35380     
35381     
35382 });
35383  /*
35384  * Based on:
35385  * Ext JS Library 1.1.1
35386  * Copyright(c) 2006-2007, Ext JS, LLC.
35387  *
35388  * Originally Released Under LGPL - original licence link has changed is not relivant.
35389  *
35390  * Fork - LGPL
35391  * <script type="text/javascript">
35392  */
35393  
35394 /**
35395  * @class Roo.bootstrap.layout.Basic
35396  * @extends Roo.util.Observable
35397  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35398  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35399  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35400  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35401  * @cfg {string}   region  the region that it inhabits..
35402  * @cfg {bool}   skipConfig skip config?
35403  * 
35404
35405  */
35406 Roo.bootstrap.layout.Basic = function(config){
35407     
35408     this.mgr = config.mgr;
35409     
35410     this.position = config.region;
35411     
35412     var skipConfig = config.skipConfig;
35413     
35414     this.events = {
35415         /**
35416          * @scope Roo.BasicLayoutRegion
35417          */
35418         
35419         /**
35420          * @event beforeremove
35421          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35422          * @param {Roo.LayoutRegion} this
35423          * @param {Roo.ContentPanel} panel The panel
35424          * @param {Object} e The cancel event object
35425          */
35426         "beforeremove" : true,
35427         /**
35428          * @event invalidated
35429          * Fires when the layout for this region is changed.
35430          * @param {Roo.LayoutRegion} this
35431          */
35432         "invalidated" : true,
35433         /**
35434          * @event visibilitychange
35435          * Fires when this region is shown or hidden 
35436          * @param {Roo.LayoutRegion} this
35437          * @param {Boolean} visibility true or false
35438          */
35439         "visibilitychange" : true,
35440         /**
35441          * @event paneladded
35442          * Fires when a panel is added. 
35443          * @param {Roo.LayoutRegion} this
35444          * @param {Roo.ContentPanel} panel The panel
35445          */
35446         "paneladded" : true,
35447         /**
35448          * @event panelremoved
35449          * Fires when a panel is removed. 
35450          * @param {Roo.LayoutRegion} this
35451          * @param {Roo.ContentPanel} panel The panel
35452          */
35453         "panelremoved" : true,
35454         /**
35455          * @event beforecollapse
35456          * Fires when this region before collapse.
35457          * @param {Roo.LayoutRegion} this
35458          */
35459         "beforecollapse" : true,
35460         /**
35461          * @event collapsed
35462          * Fires when this region is collapsed.
35463          * @param {Roo.LayoutRegion} this
35464          */
35465         "collapsed" : true,
35466         /**
35467          * @event expanded
35468          * Fires when this region is expanded.
35469          * @param {Roo.LayoutRegion} this
35470          */
35471         "expanded" : true,
35472         /**
35473          * @event slideshow
35474          * Fires when this region is slid into view.
35475          * @param {Roo.LayoutRegion} this
35476          */
35477         "slideshow" : true,
35478         /**
35479          * @event slidehide
35480          * Fires when this region slides out of view. 
35481          * @param {Roo.LayoutRegion} this
35482          */
35483         "slidehide" : true,
35484         /**
35485          * @event panelactivated
35486          * Fires when a panel is activated. 
35487          * @param {Roo.LayoutRegion} this
35488          * @param {Roo.ContentPanel} panel The activated panel
35489          */
35490         "panelactivated" : true,
35491         /**
35492          * @event resized
35493          * Fires when the user resizes this region. 
35494          * @param {Roo.LayoutRegion} this
35495          * @param {Number} newSize The new size (width for east/west, height for north/south)
35496          */
35497         "resized" : true
35498     };
35499     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35500     this.panels = new Roo.util.MixedCollection();
35501     this.panels.getKey = this.getPanelId.createDelegate(this);
35502     this.box = null;
35503     this.activePanel = null;
35504     // ensure listeners are added...
35505     
35506     if (config.listeners || config.events) {
35507         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35508             listeners : config.listeners || {},
35509             events : config.events || {}
35510         });
35511     }
35512     
35513     if(skipConfig !== true){
35514         this.applyConfig(config);
35515     }
35516 };
35517
35518 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35519 {
35520     getPanelId : function(p){
35521         return p.getId();
35522     },
35523     
35524     applyConfig : function(config){
35525         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35526         this.config = config;
35527         
35528     },
35529     
35530     /**
35531      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35532      * the width, for horizontal (north, south) the height.
35533      * @param {Number} newSize The new width or height
35534      */
35535     resizeTo : function(newSize){
35536         var el = this.el ? this.el :
35537                  (this.activePanel ? this.activePanel.getEl() : null);
35538         if(el){
35539             switch(this.position){
35540                 case "east":
35541                 case "west":
35542                     el.setWidth(newSize);
35543                     this.fireEvent("resized", this, newSize);
35544                 break;
35545                 case "north":
35546                 case "south":
35547                     el.setHeight(newSize);
35548                     this.fireEvent("resized", this, newSize);
35549                 break;                
35550             }
35551         }
35552     },
35553     
35554     getBox : function(){
35555         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35556     },
35557     
35558     getMargins : function(){
35559         return this.margins;
35560     },
35561     
35562     updateBox : function(box){
35563         this.box = box;
35564         var el = this.activePanel.getEl();
35565         el.dom.style.left = box.x + "px";
35566         el.dom.style.top = box.y + "px";
35567         this.activePanel.setSize(box.width, box.height);
35568     },
35569     
35570     /**
35571      * Returns the container element for this region.
35572      * @return {Roo.Element}
35573      */
35574     getEl : function(){
35575         return this.activePanel;
35576     },
35577     
35578     /**
35579      * Returns true if this region is currently visible.
35580      * @return {Boolean}
35581      */
35582     isVisible : function(){
35583         return this.activePanel ? true : false;
35584     },
35585     
35586     setActivePanel : function(panel){
35587         panel = this.getPanel(panel);
35588         if(this.activePanel && this.activePanel != panel){
35589             this.activePanel.setActiveState(false);
35590             this.activePanel.getEl().setLeftTop(-10000,-10000);
35591         }
35592         this.activePanel = panel;
35593         panel.setActiveState(true);
35594         if(this.box){
35595             panel.setSize(this.box.width, this.box.height);
35596         }
35597         this.fireEvent("panelactivated", this, panel);
35598         this.fireEvent("invalidated");
35599     },
35600     
35601     /**
35602      * Show the specified panel.
35603      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35604      * @return {Roo.ContentPanel} The shown panel or null
35605      */
35606     showPanel : function(panel){
35607         panel = this.getPanel(panel);
35608         if(panel){
35609             this.setActivePanel(panel);
35610         }
35611         return panel;
35612     },
35613     
35614     /**
35615      * Get the active panel for this region.
35616      * @return {Roo.ContentPanel} The active panel or null
35617      */
35618     getActivePanel : function(){
35619         return this.activePanel;
35620     },
35621     
35622     /**
35623      * Add the passed ContentPanel(s)
35624      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35625      * @return {Roo.ContentPanel} The panel added (if only one was added)
35626      */
35627     add : function(panel){
35628         if(arguments.length > 1){
35629             for(var i = 0, len = arguments.length; i < len; i++) {
35630                 this.add(arguments[i]);
35631             }
35632             return null;
35633         }
35634         if(this.hasPanel(panel)){
35635             this.showPanel(panel);
35636             return panel;
35637         }
35638         var el = panel.getEl();
35639         if(el.dom.parentNode != this.mgr.el.dom){
35640             this.mgr.el.dom.appendChild(el.dom);
35641         }
35642         if(panel.setRegion){
35643             panel.setRegion(this);
35644         }
35645         this.panels.add(panel);
35646         el.setStyle("position", "absolute");
35647         if(!panel.background){
35648             this.setActivePanel(panel);
35649             if(this.config.initialSize && this.panels.getCount()==1){
35650                 this.resizeTo(this.config.initialSize);
35651             }
35652         }
35653         this.fireEvent("paneladded", this, panel);
35654         return panel;
35655     },
35656     
35657     /**
35658      * Returns true if the panel is in this region.
35659      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35660      * @return {Boolean}
35661      */
35662     hasPanel : function(panel){
35663         if(typeof panel == "object"){ // must be panel obj
35664             panel = panel.getId();
35665         }
35666         return this.getPanel(panel) ? true : false;
35667     },
35668     
35669     /**
35670      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35671      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35672      * @param {Boolean} preservePanel Overrides the config preservePanel option
35673      * @return {Roo.ContentPanel} The panel that was removed
35674      */
35675     remove : function(panel, preservePanel){
35676         panel = this.getPanel(panel);
35677         if(!panel){
35678             return null;
35679         }
35680         var e = {};
35681         this.fireEvent("beforeremove", this, panel, e);
35682         if(e.cancel === true){
35683             return null;
35684         }
35685         var panelId = panel.getId();
35686         this.panels.removeKey(panelId);
35687         return panel;
35688     },
35689     
35690     /**
35691      * Returns the panel specified or null if it's not in this region.
35692      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35693      * @return {Roo.ContentPanel}
35694      */
35695     getPanel : function(id){
35696         if(typeof id == "object"){ // must be panel obj
35697             return id;
35698         }
35699         return this.panels.get(id);
35700     },
35701     
35702     /**
35703      * Returns this regions position (north/south/east/west/center).
35704      * @return {String} 
35705      */
35706     getPosition: function(){
35707         return this.position;    
35708     }
35709 });/*
35710  * Based on:
35711  * Ext JS Library 1.1.1
35712  * Copyright(c) 2006-2007, Ext JS, LLC.
35713  *
35714  * Originally Released Under LGPL - original licence link has changed is not relivant.
35715  *
35716  * Fork - LGPL
35717  * <script type="text/javascript">
35718  */
35719  
35720 /**
35721  * @class Roo.bootstrap.layout.Region
35722  * @extends Roo.bootstrap.layout.Basic
35723  * This class represents a region in a layout manager.
35724  
35725  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35726  * @cfg {Object}    cmargins        Margins for the element when collapsed (defaults to: north/south {top: 2, left: 0, right:0, bottom: 2} or east/west {top: 0, left: 2, right:2, bottom: 0})
35727  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35728  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35729  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35730  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35731  * @cfg {String}    title           The title for the region (overrides panel titles)
35732  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35733  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35734  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35735  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35736  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35737  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35738  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35739  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35740  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35741  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35742
35743  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35744  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35745  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35746  * @cfg {Number}    width           For East/West panels
35747  * @cfg {Number}    height          For North/South panels
35748  * @cfg {Boolean}   split           To show the splitter
35749  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35750  * 
35751  * @cfg {string}   cls             Extra CSS classes to add to region
35752  * 
35753  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35754  * @cfg {string}   region  the region that it inhabits..
35755  *
35756
35757  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35758  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35759
35760  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35761  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35762  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35763  */
35764 Roo.bootstrap.layout.Region = function(config)
35765 {
35766     this.applyConfig(config);
35767
35768     var mgr = config.mgr;
35769     var pos = config.region;
35770     config.skipConfig = true;
35771     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35772     
35773     if (mgr.el) {
35774         this.onRender(mgr.el);   
35775     }
35776      
35777     this.visible = true;
35778     this.collapsed = false;
35779     this.unrendered_panels = [];
35780 };
35781
35782 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35783
35784     position: '', // set by wrapper (eg. north/south etc..)
35785     unrendered_panels : null,  // unrendered panels.
35786     createBody : function(){
35787         /** This region's body element 
35788         * @type Roo.Element */
35789         this.bodyEl = this.el.createChild({
35790                 tag: "div",
35791                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35792         });
35793     },
35794
35795     onRender: function(ctr, pos)
35796     {
35797         var dh = Roo.DomHelper;
35798         /** This region's container element 
35799         * @type Roo.Element */
35800         this.el = dh.append(ctr.dom, {
35801                 tag: "div",
35802                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35803             }, true);
35804         /** This region's title element 
35805         * @type Roo.Element */
35806     
35807         this.titleEl = dh.append(this.el.dom,
35808             {
35809                     tag: "div",
35810                     unselectable: "on",
35811                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35812                     children:[
35813                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35814                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35815                     ]}, true);
35816         
35817         this.titleEl.enableDisplayMode();
35818         /** This region's title text element 
35819         * @type HTMLElement */
35820         this.titleTextEl = this.titleEl.dom.firstChild;
35821         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35822         /*
35823         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35824         this.closeBtn.enableDisplayMode();
35825         this.closeBtn.on("click", this.closeClicked, this);
35826         this.closeBtn.hide();
35827     */
35828         this.createBody(this.config);
35829         if(this.config.hideWhenEmpty){
35830             this.hide();
35831             this.on("paneladded", this.validateVisibility, this);
35832             this.on("panelremoved", this.validateVisibility, this);
35833         }
35834         if(this.autoScroll){
35835             this.bodyEl.setStyle("overflow", "auto");
35836         }else{
35837             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35838         }
35839         //if(c.titlebar !== false){
35840             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35841                 this.titleEl.hide();
35842             }else{
35843                 this.titleEl.show();
35844                 if(this.config.title){
35845                     this.titleTextEl.innerHTML = this.config.title;
35846                 }
35847             }
35848         //}
35849         if(this.config.collapsed){
35850             this.collapse(true);
35851         }
35852         if(this.config.hidden){
35853             this.hide();
35854         }
35855         
35856         if (this.unrendered_panels && this.unrendered_panels.length) {
35857             for (var i =0;i< this.unrendered_panels.length; i++) {
35858                 this.add(this.unrendered_panels[i]);
35859             }
35860             this.unrendered_panels = null;
35861             
35862         }
35863         
35864     },
35865     
35866     applyConfig : function(c)
35867     {
35868         /*
35869          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35870             var dh = Roo.DomHelper;
35871             if(c.titlebar !== false){
35872                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35873                 this.collapseBtn.on("click", this.collapse, this);
35874                 this.collapseBtn.enableDisplayMode();
35875                 /*
35876                 if(c.showPin === true || this.showPin){
35877                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35878                     this.stickBtn.enableDisplayMode();
35879                     this.stickBtn.on("click", this.expand, this);
35880                     this.stickBtn.hide();
35881                 }
35882                 
35883             }
35884             */
35885             /** This region's collapsed element
35886             * @type Roo.Element */
35887             /*
35888              *
35889             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35890                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35891             ]}, true);
35892             
35893             if(c.floatable !== false){
35894                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35895                this.collapsedEl.on("click", this.collapseClick, this);
35896             }
35897
35898             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35899                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35900                    id: "message", unselectable: "on", style:{"float":"left"}});
35901                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35902              }
35903             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35904             this.expandBtn.on("click", this.expand, this);
35905             
35906         }
35907         
35908         if(this.collapseBtn){
35909             this.collapseBtn.setVisible(c.collapsible == true);
35910         }
35911         
35912         this.cmargins = c.cmargins || this.cmargins ||
35913                          (this.position == "west" || this.position == "east" ?
35914                              {top: 0, left: 2, right:2, bottom: 0} :
35915                              {top: 2, left: 0, right:0, bottom: 2});
35916         */
35917         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35918         
35919         
35920         this.bottomTabs = c.tabPosition != "top";
35921         
35922         this.autoScroll = c.autoScroll || false;
35923         
35924         
35925        
35926         
35927         this.duration = c.duration || .30;
35928         this.slideDuration = c.slideDuration || .45;
35929         this.config = c;
35930        
35931     },
35932     /**
35933      * Returns true if this region is currently visible.
35934      * @return {Boolean}
35935      */
35936     isVisible : function(){
35937         return this.visible;
35938     },
35939
35940     /**
35941      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35942      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35943      */
35944     //setCollapsedTitle : function(title){
35945     //    title = title || "&#160;";
35946      //   if(this.collapsedTitleTextEl){
35947       //      this.collapsedTitleTextEl.innerHTML = title;
35948        // }
35949     //},
35950
35951     getBox : function(){
35952         var b;
35953       //  if(!this.collapsed){
35954             b = this.el.getBox(false, true);
35955        // }else{
35956           //  b = this.collapsedEl.getBox(false, true);
35957         //}
35958         return b;
35959     },
35960
35961     getMargins : function(){
35962         return this.margins;
35963         //return this.collapsed ? this.cmargins : this.margins;
35964     },
35965 /*
35966     highlight : function(){
35967         this.el.addClass("x-layout-panel-dragover");
35968     },
35969
35970     unhighlight : function(){
35971         this.el.removeClass("x-layout-panel-dragover");
35972     },
35973 */
35974     updateBox : function(box)
35975     {
35976         if (!this.bodyEl) {
35977             return; // not rendered yet..
35978         }
35979         
35980         this.box = box;
35981         if(!this.collapsed){
35982             this.el.dom.style.left = box.x + "px";
35983             this.el.dom.style.top = box.y + "px";
35984             this.updateBody(box.width, box.height);
35985         }else{
35986             this.collapsedEl.dom.style.left = box.x + "px";
35987             this.collapsedEl.dom.style.top = box.y + "px";
35988             this.collapsedEl.setSize(box.width, box.height);
35989         }
35990         if(this.tabs){
35991             this.tabs.autoSizeTabs();
35992         }
35993     },
35994
35995     updateBody : function(w, h)
35996     {
35997         if(w !== null){
35998             this.el.setWidth(w);
35999             w -= this.el.getBorderWidth("rl");
36000             if(this.config.adjustments){
36001                 w += this.config.adjustments[0];
36002             }
36003         }
36004         if(h !== null && h > 0){
36005             this.el.setHeight(h);
36006             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36007             h -= this.el.getBorderWidth("tb");
36008             if(this.config.adjustments){
36009                 h += this.config.adjustments[1];
36010             }
36011             this.bodyEl.setHeight(h);
36012             if(this.tabs){
36013                 h = this.tabs.syncHeight(h);
36014             }
36015         }
36016         if(this.panelSize){
36017             w = w !== null ? w : this.panelSize.width;
36018             h = h !== null ? h : this.panelSize.height;
36019         }
36020         if(this.activePanel){
36021             var el = this.activePanel.getEl();
36022             w = w !== null ? w : el.getWidth();
36023             h = h !== null ? h : el.getHeight();
36024             this.panelSize = {width: w, height: h};
36025             this.activePanel.setSize(w, h);
36026         }
36027         if(Roo.isIE && this.tabs){
36028             this.tabs.el.repaint();
36029         }
36030     },
36031
36032     /**
36033      * Returns the container element for this region.
36034      * @return {Roo.Element}
36035      */
36036     getEl : function(){
36037         return this.el;
36038     },
36039
36040     /**
36041      * Hides this region.
36042      */
36043     hide : function(){
36044         //if(!this.collapsed){
36045             this.el.dom.style.left = "-2000px";
36046             this.el.hide();
36047         //}else{
36048          //   this.collapsedEl.dom.style.left = "-2000px";
36049          //   this.collapsedEl.hide();
36050        // }
36051         this.visible = false;
36052         this.fireEvent("visibilitychange", this, false);
36053     },
36054
36055     /**
36056      * Shows this region if it was previously hidden.
36057      */
36058     show : function(){
36059         //if(!this.collapsed){
36060             this.el.show();
36061         //}else{
36062         //    this.collapsedEl.show();
36063        // }
36064         this.visible = true;
36065         this.fireEvent("visibilitychange", this, true);
36066     },
36067 /*
36068     closeClicked : function(){
36069         if(this.activePanel){
36070             this.remove(this.activePanel);
36071         }
36072     },
36073
36074     collapseClick : function(e){
36075         if(this.isSlid){
36076            e.stopPropagation();
36077            this.slideIn();
36078         }else{
36079            e.stopPropagation();
36080            this.slideOut();
36081         }
36082     },
36083 */
36084     /**
36085      * Collapses this region.
36086      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36087      */
36088     /*
36089     collapse : function(skipAnim, skipCheck = false){
36090         if(this.collapsed) {
36091             return;
36092         }
36093         
36094         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36095             
36096             this.collapsed = true;
36097             if(this.split){
36098                 this.split.el.hide();
36099             }
36100             if(this.config.animate && skipAnim !== true){
36101                 this.fireEvent("invalidated", this);
36102                 this.animateCollapse();
36103             }else{
36104                 this.el.setLocation(-20000,-20000);
36105                 this.el.hide();
36106                 this.collapsedEl.show();
36107                 this.fireEvent("collapsed", this);
36108                 this.fireEvent("invalidated", this);
36109             }
36110         }
36111         
36112     },
36113 */
36114     animateCollapse : function(){
36115         // overridden
36116     },
36117
36118     /**
36119      * Expands this region if it was previously collapsed.
36120      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36121      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36122      */
36123     /*
36124     expand : function(e, skipAnim){
36125         if(e) {
36126             e.stopPropagation();
36127         }
36128         if(!this.collapsed || this.el.hasActiveFx()) {
36129             return;
36130         }
36131         if(this.isSlid){
36132             this.afterSlideIn();
36133             skipAnim = true;
36134         }
36135         this.collapsed = false;
36136         if(this.config.animate && skipAnim !== true){
36137             this.animateExpand();
36138         }else{
36139             this.el.show();
36140             if(this.split){
36141                 this.split.el.show();
36142             }
36143             this.collapsedEl.setLocation(-2000,-2000);
36144             this.collapsedEl.hide();
36145             this.fireEvent("invalidated", this);
36146             this.fireEvent("expanded", this);
36147         }
36148     },
36149 */
36150     animateExpand : function(){
36151         // overridden
36152     },
36153
36154     initTabs : function()
36155     {
36156         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36157         
36158         var ts = new Roo.bootstrap.panel.Tabs({
36159                 el: this.bodyEl.dom,
36160                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36161                 disableTooltips: this.config.disableTabTips,
36162                 toolbar : this.config.toolbar
36163             });
36164         
36165         if(this.config.hideTabs){
36166             ts.stripWrap.setDisplayed(false);
36167         }
36168         this.tabs = ts;
36169         ts.resizeTabs = this.config.resizeTabs === true;
36170         ts.minTabWidth = this.config.minTabWidth || 40;
36171         ts.maxTabWidth = this.config.maxTabWidth || 250;
36172         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36173         ts.monitorResize = false;
36174         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36175         ts.bodyEl.addClass('roo-layout-tabs-body');
36176         this.panels.each(this.initPanelAsTab, this);
36177     },
36178
36179     initPanelAsTab : function(panel){
36180         var ti = this.tabs.addTab(
36181             panel.getEl().id,
36182             panel.getTitle(),
36183             null,
36184             this.config.closeOnTab && panel.isClosable(),
36185             panel.tpl
36186         );
36187         if(panel.tabTip !== undefined){
36188             ti.setTooltip(panel.tabTip);
36189         }
36190         ti.on("activate", function(){
36191               this.setActivePanel(panel);
36192         }, this);
36193         
36194         if(this.config.closeOnTab){
36195             ti.on("beforeclose", function(t, e){
36196                 e.cancel = true;
36197                 this.remove(panel);
36198             }, this);
36199         }
36200         
36201         panel.tabItem = ti;
36202         
36203         return ti;
36204     },
36205
36206     updatePanelTitle : function(panel, title)
36207     {
36208         if(this.activePanel == panel){
36209             this.updateTitle(title);
36210         }
36211         if(this.tabs){
36212             var ti = this.tabs.getTab(panel.getEl().id);
36213             ti.setText(title);
36214             if(panel.tabTip !== undefined){
36215                 ti.setTooltip(panel.tabTip);
36216             }
36217         }
36218     },
36219
36220     updateTitle : function(title){
36221         if(this.titleTextEl && !this.config.title){
36222             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36223         }
36224     },
36225
36226     setActivePanel : function(panel)
36227     {
36228         panel = this.getPanel(panel);
36229         if(this.activePanel && this.activePanel != panel){
36230             if(this.activePanel.setActiveState(false) === false){
36231                 return;
36232             }
36233         }
36234         this.activePanel = panel;
36235         panel.setActiveState(true);
36236         if(this.panelSize){
36237             panel.setSize(this.panelSize.width, this.panelSize.height);
36238         }
36239         if(this.closeBtn){
36240             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36241         }
36242         this.updateTitle(panel.getTitle());
36243         if(this.tabs){
36244             this.fireEvent("invalidated", this);
36245         }
36246         this.fireEvent("panelactivated", this, panel);
36247     },
36248
36249     /**
36250      * Shows the specified panel.
36251      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36252      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36253      */
36254     showPanel : function(panel)
36255     {
36256         panel = this.getPanel(panel);
36257         if(panel){
36258             if(this.tabs){
36259                 var tab = this.tabs.getTab(panel.getEl().id);
36260                 if(tab.isHidden()){
36261                     this.tabs.unhideTab(tab.id);
36262                 }
36263                 tab.activate();
36264             }else{
36265                 this.setActivePanel(panel);
36266             }
36267         }
36268         return panel;
36269     },
36270
36271     /**
36272      * Get the active panel for this region.
36273      * @return {Roo.ContentPanel} The active panel or null
36274      */
36275     getActivePanel : function(){
36276         return this.activePanel;
36277     },
36278
36279     validateVisibility : function(){
36280         if(this.panels.getCount() < 1){
36281             this.updateTitle("&#160;");
36282             this.closeBtn.hide();
36283             this.hide();
36284         }else{
36285             if(!this.isVisible()){
36286                 this.show();
36287             }
36288         }
36289     },
36290
36291     /**
36292      * Adds the passed ContentPanel(s) to this region.
36293      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36294      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36295      */
36296     add : function(panel)
36297     {
36298         if(arguments.length > 1){
36299             for(var i = 0, len = arguments.length; i < len; i++) {
36300                 this.add(arguments[i]);
36301             }
36302             return null;
36303         }
36304         
36305         // if we have not been rendered yet, then we can not really do much of this..
36306         if (!this.bodyEl) {
36307             this.unrendered_panels.push(panel);
36308             return panel;
36309         }
36310         
36311         
36312         
36313         
36314         if(this.hasPanel(panel)){
36315             this.showPanel(panel);
36316             return panel;
36317         }
36318         panel.setRegion(this);
36319         this.panels.add(panel);
36320        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36321             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36322             // and hide them... ???
36323             this.bodyEl.dom.appendChild(panel.getEl().dom);
36324             if(panel.background !== true){
36325                 this.setActivePanel(panel);
36326             }
36327             this.fireEvent("paneladded", this, panel);
36328             return panel;
36329         }
36330         */
36331         if(!this.tabs){
36332             this.initTabs();
36333         }else{
36334             this.initPanelAsTab(panel);
36335         }
36336         
36337         
36338         if(panel.background !== true){
36339             this.tabs.activate(panel.getEl().id);
36340         }
36341         this.fireEvent("paneladded", this, panel);
36342         return panel;
36343     },
36344
36345     /**
36346      * Hides the tab for the specified panel.
36347      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36348      */
36349     hidePanel : function(panel){
36350         if(this.tabs && (panel = this.getPanel(panel))){
36351             this.tabs.hideTab(panel.getEl().id);
36352         }
36353     },
36354
36355     /**
36356      * Unhides the tab for a previously hidden panel.
36357      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36358      */
36359     unhidePanel : function(panel){
36360         if(this.tabs && (panel = this.getPanel(panel))){
36361             this.tabs.unhideTab(panel.getEl().id);
36362         }
36363     },
36364
36365     clearPanels : function(){
36366         while(this.panels.getCount() > 0){
36367              this.remove(this.panels.first());
36368         }
36369     },
36370
36371     /**
36372      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36373      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36374      * @param {Boolean} preservePanel Overrides the config preservePanel option
36375      * @return {Roo.ContentPanel} The panel that was removed
36376      */
36377     remove : function(panel, preservePanel)
36378     {
36379         panel = this.getPanel(panel);
36380         if(!panel){
36381             return null;
36382         }
36383         var e = {};
36384         this.fireEvent("beforeremove", this, panel, e);
36385         if(e.cancel === true){
36386             return null;
36387         }
36388         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36389         var panelId = panel.getId();
36390         this.panels.removeKey(panelId);
36391         if(preservePanel){
36392             document.body.appendChild(panel.getEl().dom);
36393         }
36394         if(this.tabs){
36395             this.tabs.removeTab(panel.getEl().id);
36396         }else if (!preservePanel){
36397             this.bodyEl.dom.removeChild(panel.getEl().dom);
36398         }
36399         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36400             var p = this.panels.first();
36401             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36402             tempEl.appendChild(p.getEl().dom);
36403             this.bodyEl.update("");
36404             this.bodyEl.dom.appendChild(p.getEl().dom);
36405             tempEl = null;
36406             this.updateTitle(p.getTitle());
36407             this.tabs = null;
36408             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36409             this.setActivePanel(p);
36410         }
36411         panel.setRegion(null);
36412         if(this.activePanel == panel){
36413             this.activePanel = null;
36414         }
36415         if(this.config.autoDestroy !== false && preservePanel !== true){
36416             try{panel.destroy();}catch(e){}
36417         }
36418         this.fireEvent("panelremoved", this, panel);
36419         return panel;
36420     },
36421
36422     /**
36423      * Returns the TabPanel component used by this region
36424      * @return {Roo.TabPanel}
36425      */
36426     getTabs : function(){
36427         return this.tabs;
36428     },
36429
36430     createTool : function(parentEl, className){
36431         var btn = Roo.DomHelper.append(parentEl, {
36432             tag: "div",
36433             cls: "x-layout-tools-button",
36434             children: [ {
36435                 tag: "div",
36436                 cls: "roo-layout-tools-button-inner " + className,
36437                 html: "&#160;"
36438             }]
36439         }, true);
36440         btn.addClassOnOver("roo-layout-tools-button-over");
36441         return btn;
36442     }
36443 });/*
36444  * Based on:
36445  * Ext JS Library 1.1.1
36446  * Copyright(c) 2006-2007, Ext JS, LLC.
36447  *
36448  * Originally Released Under LGPL - original licence link has changed is not relivant.
36449  *
36450  * Fork - LGPL
36451  * <script type="text/javascript">
36452  */
36453  
36454
36455
36456 /**
36457  * @class Roo.SplitLayoutRegion
36458  * @extends Roo.LayoutRegion
36459  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36460  */
36461 Roo.bootstrap.layout.Split = function(config){
36462     this.cursor = config.cursor;
36463     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36464 };
36465
36466 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36467 {
36468     splitTip : "Drag to resize.",
36469     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36470     useSplitTips : false,
36471
36472     applyConfig : function(config){
36473         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36474     },
36475     
36476     onRender : function(ctr,pos) {
36477         
36478         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36479         if(!this.config.split){
36480             return;
36481         }
36482         if(!this.split){
36483             
36484             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36485                             tag: "div",
36486                             id: this.el.id + "-split",
36487                             cls: "roo-layout-split roo-layout-split-"+this.position,
36488                             html: "&#160;"
36489             });
36490             /** The SplitBar for this region 
36491             * @type Roo.SplitBar */
36492             // does not exist yet...
36493             Roo.log([this.position, this.orientation]);
36494             
36495             this.split = new Roo.bootstrap.SplitBar({
36496                 dragElement : splitEl,
36497                 resizingElement: this.el,
36498                 orientation : this.orientation
36499             });
36500             
36501             this.split.on("moved", this.onSplitMove, this);
36502             this.split.useShim = this.config.useShim === true;
36503             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36504             if(this.useSplitTips){
36505                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36506             }
36507             //if(config.collapsible){
36508             //    this.split.el.on("dblclick", this.collapse,  this);
36509             //}
36510         }
36511         if(typeof this.config.minSize != "undefined"){
36512             this.split.minSize = this.config.minSize;
36513         }
36514         if(typeof this.config.maxSize != "undefined"){
36515             this.split.maxSize = this.config.maxSize;
36516         }
36517         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36518             this.hideSplitter();
36519         }
36520         
36521     },
36522
36523     getHMaxSize : function(){
36524          var cmax = this.config.maxSize || 10000;
36525          var center = this.mgr.getRegion("center");
36526          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36527     },
36528
36529     getVMaxSize : function(){
36530          var cmax = this.config.maxSize || 10000;
36531          var center = this.mgr.getRegion("center");
36532          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36533     },
36534
36535     onSplitMove : function(split, newSize){
36536         this.fireEvent("resized", this, newSize);
36537     },
36538     
36539     /** 
36540      * Returns the {@link Roo.SplitBar} for this region.
36541      * @return {Roo.SplitBar}
36542      */
36543     getSplitBar : function(){
36544         return this.split;
36545     },
36546     
36547     hide : function(){
36548         this.hideSplitter();
36549         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36550     },
36551
36552     hideSplitter : function(){
36553         if(this.split){
36554             this.split.el.setLocation(-2000,-2000);
36555             this.split.el.hide();
36556         }
36557     },
36558
36559     show : function(){
36560         if(this.split){
36561             this.split.el.show();
36562         }
36563         Roo.bootstrap.layout.Split.superclass.show.call(this);
36564     },
36565     
36566     beforeSlide: function(){
36567         if(Roo.isGecko){// firefox overflow auto bug workaround
36568             this.bodyEl.clip();
36569             if(this.tabs) {
36570                 this.tabs.bodyEl.clip();
36571             }
36572             if(this.activePanel){
36573                 this.activePanel.getEl().clip();
36574                 
36575                 if(this.activePanel.beforeSlide){
36576                     this.activePanel.beforeSlide();
36577                 }
36578             }
36579         }
36580     },
36581     
36582     afterSlide : function(){
36583         if(Roo.isGecko){// firefox overflow auto bug workaround
36584             this.bodyEl.unclip();
36585             if(this.tabs) {
36586                 this.tabs.bodyEl.unclip();
36587             }
36588             if(this.activePanel){
36589                 this.activePanel.getEl().unclip();
36590                 if(this.activePanel.afterSlide){
36591                     this.activePanel.afterSlide();
36592                 }
36593             }
36594         }
36595     },
36596
36597     initAutoHide : function(){
36598         if(this.autoHide !== false){
36599             if(!this.autoHideHd){
36600                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36601                 this.autoHideHd = {
36602                     "mouseout": function(e){
36603                         if(!e.within(this.el, true)){
36604                             st.delay(500);
36605                         }
36606                     },
36607                     "mouseover" : function(e){
36608                         st.cancel();
36609                     },
36610                     scope : this
36611                 };
36612             }
36613             this.el.on(this.autoHideHd);
36614         }
36615     },
36616
36617     clearAutoHide : function(){
36618         if(this.autoHide !== false){
36619             this.el.un("mouseout", this.autoHideHd.mouseout);
36620             this.el.un("mouseover", this.autoHideHd.mouseover);
36621         }
36622     },
36623
36624     clearMonitor : function(){
36625         Roo.get(document).un("click", this.slideInIf, this);
36626     },
36627
36628     // these names are backwards but not changed for compat
36629     slideOut : function(){
36630         if(this.isSlid || this.el.hasActiveFx()){
36631             return;
36632         }
36633         this.isSlid = true;
36634         if(this.collapseBtn){
36635             this.collapseBtn.hide();
36636         }
36637         this.closeBtnState = this.closeBtn.getStyle('display');
36638         this.closeBtn.hide();
36639         if(this.stickBtn){
36640             this.stickBtn.show();
36641         }
36642         this.el.show();
36643         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36644         this.beforeSlide();
36645         this.el.setStyle("z-index", 10001);
36646         this.el.slideIn(this.getSlideAnchor(), {
36647             callback: function(){
36648                 this.afterSlide();
36649                 this.initAutoHide();
36650                 Roo.get(document).on("click", this.slideInIf, this);
36651                 this.fireEvent("slideshow", this);
36652             },
36653             scope: this,
36654             block: true
36655         });
36656     },
36657
36658     afterSlideIn : function(){
36659         this.clearAutoHide();
36660         this.isSlid = false;
36661         this.clearMonitor();
36662         this.el.setStyle("z-index", "");
36663         if(this.collapseBtn){
36664             this.collapseBtn.show();
36665         }
36666         this.closeBtn.setStyle('display', this.closeBtnState);
36667         if(this.stickBtn){
36668             this.stickBtn.hide();
36669         }
36670         this.fireEvent("slidehide", this);
36671     },
36672
36673     slideIn : function(cb){
36674         if(!this.isSlid || this.el.hasActiveFx()){
36675             Roo.callback(cb);
36676             return;
36677         }
36678         this.isSlid = false;
36679         this.beforeSlide();
36680         this.el.slideOut(this.getSlideAnchor(), {
36681             callback: function(){
36682                 this.el.setLeftTop(-10000, -10000);
36683                 this.afterSlide();
36684                 this.afterSlideIn();
36685                 Roo.callback(cb);
36686             },
36687             scope: this,
36688             block: true
36689         });
36690     },
36691     
36692     slideInIf : function(e){
36693         if(!e.within(this.el)){
36694             this.slideIn();
36695         }
36696     },
36697
36698     animateCollapse : function(){
36699         this.beforeSlide();
36700         this.el.setStyle("z-index", 20000);
36701         var anchor = this.getSlideAnchor();
36702         this.el.slideOut(anchor, {
36703             callback : function(){
36704                 this.el.setStyle("z-index", "");
36705                 this.collapsedEl.slideIn(anchor, {duration:.3});
36706                 this.afterSlide();
36707                 this.el.setLocation(-10000,-10000);
36708                 this.el.hide();
36709                 this.fireEvent("collapsed", this);
36710             },
36711             scope: this,
36712             block: true
36713         });
36714     },
36715
36716     animateExpand : function(){
36717         this.beforeSlide();
36718         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36719         this.el.setStyle("z-index", 20000);
36720         this.collapsedEl.hide({
36721             duration:.1
36722         });
36723         this.el.slideIn(this.getSlideAnchor(), {
36724             callback : function(){
36725                 this.el.setStyle("z-index", "");
36726                 this.afterSlide();
36727                 if(this.split){
36728                     this.split.el.show();
36729                 }
36730                 this.fireEvent("invalidated", this);
36731                 this.fireEvent("expanded", this);
36732             },
36733             scope: this,
36734             block: true
36735         });
36736     },
36737
36738     anchors : {
36739         "west" : "left",
36740         "east" : "right",
36741         "north" : "top",
36742         "south" : "bottom"
36743     },
36744
36745     sanchors : {
36746         "west" : "l",
36747         "east" : "r",
36748         "north" : "t",
36749         "south" : "b"
36750     },
36751
36752     canchors : {
36753         "west" : "tl-tr",
36754         "east" : "tr-tl",
36755         "north" : "tl-bl",
36756         "south" : "bl-tl"
36757     },
36758
36759     getAnchor : function(){
36760         return this.anchors[this.position];
36761     },
36762
36763     getCollapseAnchor : function(){
36764         return this.canchors[this.position];
36765     },
36766
36767     getSlideAnchor : function(){
36768         return this.sanchors[this.position];
36769     },
36770
36771     getAlignAdj : function(){
36772         var cm = this.cmargins;
36773         switch(this.position){
36774             case "west":
36775                 return [0, 0];
36776             break;
36777             case "east":
36778                 return [0, 0];
36779             break;
36780             case "north":
36781                 return [0, 0];
36782             break;
36783             case "south":
36784                 return [0, 0];
36785             break;
36786         }
36787     },
36788
36789     getExpandAdj : function(){
36790         var c = this.collapsedEl, cm = this.cmargins;
36791         switch(this.position){
36792             case "west":
36793                 return [-(cm.right+c.getWidth()+cm.left), 0];
36794             break;
36795             case "east":
36796                 return [cm.right+c.getWidth()+cm.left, 0];
36797             break;
36798             case "north":
36799                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36800             break;
36801             case "south":
36802                 return [0, cm.top+cm.bottom+c.getHeight()];
36803             break;
36804         }
36805     }
36806 });/*
36807  * Based on:
36808  * Ext JS Library 1.1.1
36809  * Copyright(c) 2006-2007, Ext JS, LLC.
36810  *
36811  * Originally Released Under LGPL - original licence link has changed is not relivant.
36812  *
36813  * Fork - LGPL
36814  * <script type="text/javascript">
36815  */
36816 /*
36817  * These classes are private internal classes
36818  */
36819 Roo.bootstrap.layout.Center = function(config){
36820     config.region = "center";
36821     Roo.bootstrap.layout.Region.call(this, config);
36822     this.visible = true;
36823     this.minWidth = config.minWidth || 20;
36824     this.minHeight = config.minHeight || 20;
36825 };
36826
36827 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36828     hide : function(){
36829         // center panel can't be hidden
36830     },
36831     
36832     show : function(){
36833         // center panel can't be hidden
36834     },
36835     
36836     getMinWidth: function(){
36837         return this.minWidth;
36838     },
36839     
36840     getMinHeight: function(){
36841         return this.minHeight;
36842     }
36843 });
36844
36845
36846
36847
36848  
36849
36850
36851
36852
36853
36854 Roo.bootstrap.layout.North = function(config)
36855 {
36856     config.region = 'north';
36857     config.cursor = 'n-resize';
36858     
36859     Roo.bootstrap.layout.Split.call(this, config);
36860     
36861     
36862     if(this.split){
36863         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36864         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36865         this.split.el.addClass("roo-layout-split-v");
36866     }
36867     var size = config.initialSize || config.height;
36868     if(typeof size != "undefined"){
36869         this.el.setHeight(size);
36870     }
36871 };
36872 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36873 {
36874     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36875     
36876     
36877     
36878     getBox : function(){
36879         if(this.collapsed){
36880             return this.collapsedEl.getBox();
36881         }
36882         var box = this.el.getBox();
36883         if(this.split){
36884             box.height += this.split.el.getHeight();
36885         }
36886         return box;
36887     },
36888     
36889     updateBox : function(box){
36890         if(this.split && !this.collapsed){
36891             box.height -= this.split.el.getHeight();
36892             this.split.el.setLeft(box.x);
36893             this.split.el.setTop(box.y+box.height);
36894             this.split.el.setWidth(box.width);
36895         }
36896         if(this.collapsed){
36897             this.updateBody(box.width, null);
36898         }
36899         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36900     }
36901 });
36902
36903
36904
36905
36906
36907 Roo.bootstrap.layout.South = function(config){
36908     config.region = 'south';
36909     config.cursor = 's-resize';
36910     Roo.bootstrap.layout.Split.call(this, config);
36911     if(this.split){
36912         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36913         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36914         this.split.el.addClass("roo-layout-split-v");
36915     }
36916     var size = config.initialSize || config.height;
36917     if(typeof size != "undefined"){
36918         this.el.setHeight(size);
36919     }
36920 };
36921
36922 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36923     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36924     getBox : function(){
36925         if(this.collapsed){
36926             return this.collapsedEl.getBox();
36927         }
36928         var box = this.el.getBox();
36929         if(this.split){
36930             var sh = this.split.el.getHeight();
36931             box.height += sh;
36932             box.y -= sh;
36933         }
36934         return box;
36935     },
36936     
36937     updateBox : function(box){
36938         if(this.split && !this.collapsed){
36939             var sh = this.split.el.getHeight();
36940             box.height -= sh;
36941             box.y += sh;
36942             this.split.el.setLeft(box.x);
36943             this.split.el.setTop(box.y-sh);
36944             this.split.el.setWidth(box.width);
36945         }
36946         if(this.collapsed){
36947             this.updateBody(box.width, null);
36948         }
36949         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36950     }
36951 });
36952
36953 Roo.bootstrap.layout.East = function(config){
36954     config.region = "east";
36955     config.cursor = "e-resize";
36956     Roo.bootstrap.layout.Split.call(this, config);
36957     if(this.split){
36958         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36959         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36960         this.split.el.addClass("roo-layout-split-h");
36961     }
36962     var size = config.initialSize || config.width;
36963     if(typeof size != "undefined"){
36964         this.el.setWidth(size);
36965     }
36966 };
36967 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36968     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36969     getBox : function(){
36970         if(this.collapsed){
36971             return this.collapsedEl.getBox();
36972         }
36973         var box = this.el.getBox();
36974         if(this.split){
36975             var sw = this.split.el.getWidth();
36976             box.width += sw;
36977             box.x -= sw;
36978         }
36979         return box;
36980     },
36981
36982     updateBox : function(box){
36983         if(this.split && !this.collapsed){
36984             var sw = this.split.el.getWidth();
36985             box.width -= sw;
36986             this.split.el.setLeft(box.x);
36987             this.split.el.setTop(box.y);
36988             this.split.el.setHeight(box.height);
36989             box.x += sw;
36990         }
36991         if(this.collapsed){
36992             this.updateBody(null, box.height);
36993         }
36994         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36995     }
36996 });
36997
36998 Roo.bootstrap.layout.West = function(config){
36999     config.region = "west";
37000     config.cursor = "w-resize";
37001     
37002     Roo.bootstrap.layout.Split.call(this, config);
37003     if(this.split){
37004         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37005         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37006         this.split.el.addClass("roo-layout-split-h");
37007     }
37008     
37009 };
37010 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37011     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37012     
37013     onRender: function(ctr, pos)
37014     {
37015         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37016         var size = this.config.initialSize || this.config.width;
37017         if(typeof size != "undefined"){
37018             this.el.setWidth(size);
37019         }
37020     },
37021     
37022     getBox : function(){
37023         if(this.collapsed){
37024             return this.collapsedEl.getBox();
37025         }
37026         var box = this.el.getBox();
37027         if(this.split){
37028             box.width += this.split.el.getWidth();
37029         }
37030         return box;
37031     },
37032     
37033     updateBox : function(box){
37034         if(this.split && !this.collapsed){
37035             var sw = this.split.el.getWidth();
37036             box.width -= sw;
37037             this.split.el.setLeft(box.x+box.width);
37038             this.split.el.setTop(box.y);
37039             this.split.el.setHeight(box.height);
37040         }
37041         if(this.collapsed){
37042             this.updateBody(null, box.height);
37043         }
37044         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37045     }
37046 });
37047 Roo.namespace("Roo.bootstrap.panel");/*
37048  * Based on:
37049  * Ext JS Library 1.1.1
37050  * Copyright(c) 2006-2007, Ext JS, LLC.
37051  *
37052  * Originally Released Under LGPL - original licence link has changed is not relivant.
37053  *
37054  * Fork - LGPL
37055  * <script type="text/javascript">
37056  */
37057 /**
37058  * @class Roo.ContentPanel
37059  * @extends Roo.util.Observable
37060  * A basic ContentPanel element.
37061  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37062  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37063  * @cfg {Boolean/Object} autoCreate True to auto generate the DOM element for this panel, or a {@link Roo.DomHelper} config of the element to create
37064  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37065  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37066  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37067  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37068  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37069  * @cfg {String} title          The title for this panel
37070  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37071  * @cfg {String} url            Calls {@link #setUrl} with this value
37072  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37073  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37074  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37075  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37076  * @cfg {Boolean} badges render the badges
37077
37078  * @constructor
37079  * Create a new ContentPanel.
37080  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37081  * @param {String/Object} config A string to set only the title or a config object
37082  * @param {String} content (optional) Set the HTML content for this panel
37083  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37084  */
37085 Roo.bootstrap.panel.Content = function( config){
37086     
37087     this.tpl = config.tpl || false;
37088     
37089     var el = config.el;
37090     var content = config.content;
37091
37092     if(config.autoCreate){ // xtype is available if this is called from factory
37093         el = Roo.id();
37094     }
37095     this.el = Roo.get(el);
37096     if(!this.el && config && config.autoCreate){
37097         if(typeof config.autoCreate == "object"){
37098             if(!config.autoCreate.id){
37099                 config.autoCreate.id = config.id||el;
37100             }
37101             this.el = Roo.DomHelper.append(document.body,
37102                         config.autoCreate, true);
37103         }else{
37104             var elcfg =  {   tag: "div",
37105                             cls: "roo-layout-inactive-content",
37106                             id: config.id||el
37107                             };
37108             if (config.html) {
37109                 elcfg.html = config.html;
37110                 
37111             }
37112                         
37113             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37114         }
37115     } 
37116     this.closable = false;
37117     this.loaded = false;
37118     this.active = false;
37119    
37120       
37121     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37122         
37123         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37124         
37125         this.wrapEl = this.el; //this.el.wrap();
37126         var ti = [];
37127         if (config.toolbar.items) {
37128             ti = config.toolbar.items ;
37129             delete config.toolbar.items ;
37130         }
37131         
37132         var nitems = [];
37133         this.toolbar.render(this.wrapEl, 'before');
37134         for(var i =0;i < ti.length;i++) {
37135           //  Roo.log(['add child', items[i]]);
37136             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37137         }
37138         this.toolbar.items = nitems;
37139         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37140         delete config.toolbar;
37141         
37142     }
37143     /*
37144     // xtype created footer. - not sure if will work as we normally have to render first..
37145     if (this.footer && !this.footer.el && this.footer.xtype) {
37146         if (!this.wrapEl) {
37147             this.wrapEl = this.el.wrap();
37148         }
37149     
37150         this.footer.container = this.wrapEl.createChild();
37151          
37152         this.footer = Roo.factory(this.footer, Roo);
37153         
37154     }
37155     */
37156     
37157      if(typeof config == "string"){
37158         this.title = config;
37159     }else{
37160         Roo.apply(this, config);
37161     }
37162     
37163     if(this.resizeEl){
37164         this.resizeEl = Roo.get(this.resizeEl, true);
37165     }else{
37166         this.resizeEl = this.el;
37167     }
37168     // handle view.xtype
37169     
37170  
37171     
37172     
37173     this.addEvents({
37174         /**
37175          * @event activate
37176          * Fires when this panel is activated. 
37177          * @param {Roo.ContentPanel} this
37178          */
37179         "activate" : true,
37180         /**
37181          * @event deactivate
37182          * Fires when this panel is activated. 
37183          * @param {Roo.ContentPanel} this
37184          */
37185         "deactivate" : true,
37186
37187         /**
37188          * @event resize
37189          * Fires when this panel is resized if fitToFrame is true.
37190          * @param {Roo.ContentPanel} this
37191          * @param {Number} width The width after any component adjustments
37192          * @param {Number} height The height after any component adjustments
37193          */
37194         "resize" : true,
37195         
37196          /**
37197          * @event render
37198          * Fires when this tab is created
37199          * @param {Roo.ContentPanel} this
37200          */
37201         "render" : true
37202         
37203         
37204         
37205     });
37206     
37207
37208     
37209     
37210     if(this.autoScroll){
37211         this.resizeEl.setStyle("overflow", "auto");
37212     } else {
37213         // fix randome scrolling
37214         //this.el.on('scroll', function() {
37215         //    Roo.log('fix random scolling');
37216         //    this.scrollTo('top',0); 
37217         //});
37218     }
37219     content = content || this.content;
37220     if(content){
37221         this.setContent(content);
37222     }
37223     if(config && config.url){
37224         this.setUrl(this.url, this.params, this.loadOnce);
37225     }
37226     
37227     
37228     
37229     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37230     
37231     if (this.view && typeof(this.view.xtype) != 'undefined') {
37232         this.view.el = this.el.appendChild(document.createElement("div"));
37233         this.view = Roo.factory(this.view); 
37234         this.view.render  &&  this.view.render(false, '');  
37235     }
37236     
37237     
37238     this.fireEvent('render', this);
37239 };
37240
37241 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37242     
37243     tabTip : '',
37244     
37245     setRegion : function(region){
37246         this.region = region;
37247         this.setActiveClass(region && !this.background);
37248     },
37249     
37250     
37251     setActiveClass: function(state)
37252     {
37253         if(state){
37254            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37255            this.el.setStyle('position','relative');
37256         }else{
37257            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37258            this.el.setStyle('position', 'absolute');
37259         } 
37260     },
37261     
37262     /**
37263      * Returns the toolbar for this Panel if one was configured. 
37264      * @return {Roo.Toolbar} 
37265      */
37266     getToolbar : function(){
37267         return this.toolbar;
37268     },
37269     
37270     setActiveState : function(active)
37271     {
37272         this.active = active;
37273         this.setActiveClass(active);
37274         if(!active){
37275             if(this.fireEvent("deactivate", this) === false){
37276                 return false;
37277             }
37278             return true;
37279         }
37280         this.fireEvent("activate", this);
37281         return true;
37282     },
37283     /**
37284      * Updates this panel's element
37285      * @param {String} content The new content
37286      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37287     */
37288     setContent : function(content, loadScripts){
37289         this.el.update(content, loadScripts);
37290     },
37291
37292     ignoreResize : function(w, h){
37293         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37294             return true;
37295         }else{
37296             this.lastSize = {width: w, height: h};
37297             return false;
37298         }
37299     },
37300     /**
37301      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37302      * @return {Roo.UpdateManager} The UpdateManager
37303      */
37304     getUpdateManager : function(){
37305         return this.el.getUpdateManager();
37306     },
37307      /**
37308      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37309      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
37310 <pre><code>
37311 panel.load({
37312     url: "your-url.php",
37313     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37314     callback: yourFunction,
37315     scope: yourObject, //(optional scope)
37316     discardUrl: false,
37317     nocache: false,
37318     text: "Loading...",
37319     timeout: 30,
37320     scripts: false
37321 });
37322 </code></pre>
37323      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37324      * are shorthand for <i>disableCaching</i>, <i>indicatorText</i> and <i>loadScripts</i> and are used to set their associated property on this panel UpdateManager instance.
37325      * @param {String/Object} params (optional) The parameters to pass as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
37326      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37327      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
37328      * @return {Roo.ContentPanel} this
37329      */
37330     load : function(){
37331         var um = this.el.getUpdateManager();
37332         um.update.apply(um, arguments);
37333         return this;
37334     },
37335
37336
37337     /**
37338      * Set a URL to be used to load the content for this panel. When this panel is activated, the content will be loaded from that URL.
37339      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37340      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
37341      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this panel is activated. (Defaults to false)
37342      * @return {Roo.UpdateManager} The UpdateManager
37343      */
37344     setUrl : function(url, params, loadOnce){
37345         if(this.refreshDelegate){
37346             this.removeListener("activate", this.refreshDelegate);
37347         }
37348         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37349         this.on("activate", this.refreshDelegate);
37350         return this.el.getUpdateManager();
37351     },
37352     
37353     _handleRefresh : function(url, params, loadOnce){
37354         if(!loadOnce || !this.loaded){
37355             var updater = this.el.getUpdateManager();
37356             updater.update(url, params, this._setLoaded.createDelegate(this));
37357         }
37358     },
37359     
37360     _setLoaded : function(){
37361         this.loaded = true;
37362     }, 
37363     
37364     /**
37365      * Returns this panel's id
37366      * @return {String} 
37367      */
37368     getId : function(){
37369         return this.el.id;
37370     },
37371     
37372     /** 
37373      * Returns this panel's element - used by regiosn to add.
37374      * @return {Roo.Element} 
37375      */
37376     getEl : function(){
37377         return this.wrapEl || this.el;
37378     },
37379     
37380    
37381     
37382     adjustForComponents : function(width, height)
37383     {
37384         //Roo.log('adjustForComponents ');
37385         if(this.resizeEl != this.el){
37386             width -= this.el.getFrameWidth('lr');
37387             height -= this.el.getFrameWidth('tb');
37388         }
37389         if(this.toolbar){
37390             var te = this.toolbar.getEl();
37391             te.setWidth(width);
37392             height -= te.getHeight();
37393         }
37394         if(this.footer){
37395             var te = this.footer.getEl();
37396             te.setWidth(width);
37397             height -= te.getHeight();
37398         }
37399         
37400         
37401         if(this.adjustments){
37402             width += this.adjustments[0];
37403             height += this.adjustments[1];
37404         }
37405         return {"width": width, "height": height};
37406     },
37407     
37408     setSize : function(width, height){
37409         if(this.fitToFrame && !this.ignoreResize(width, height)){
37410             if(this.fitContainer && this.resizeEl != this.el){
37411                 this.el.setSize(width, height);
37412             }
37413             var size = this.adjustForComponents(width, height);
37414             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37415             this.fireEvent('resize', this, size.width, size.height);
37416         }
37417     },
37418     
37419     /**
37420      * Returns this panel's title
37421      * @return {String} 
37422      */
37423     getTitle : function(){
37424         
37425         if (typeof(this.title) != 'object') {
37426             return this.title;
37427         }
37428         
37429         var t = '';
37430         for (var k in this.title) {
37431             if (!this.title.hasOwnProperty(k)) {
37432                 continue;
37433             }
37434             
37435             if (k.indexOf('-') >= 0) {
37436                 var s = k.split('-');
37437                 for (var i = 0; i<s.length; i++) {
37438                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37439                 }
37440             } else {
37441                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37442             }
37443         }
37444         return t;
37445     },
37446     
37447     /**
37448      * Set this panel's title
37449      * @param {String} title
37450      */
37451     setTitle : function(title){
37452         this.title = title;
37453         if(this.region){
37454             this.region.updatePanelTitle(this, title);
37455         }
37456     },
37457     
37458     /**
37459      * Returns true is this panel was configured to be closable
37460      * @return {Boolean} 
37461      */
37462     isClosable : function(){
37463         return this.closable;
37464     },
37465     
37466     beforeSlide : function(){
37467         this.el.clip();
37468         this.resizeEl.clip();
37469     },
37470     
37471     afterSlide : function(){
37472         this.el.unclip();
37473         this.resizeEl.unclip();
37474     },
37475     
37476     /**
37477      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37478      *   Will fail silently if the {@link #setUrl} method has not been called.
37479      *   This does not activate the panel, just updates its content.
37480      */
37481     refresh : function(){
37482         if(this.refreshDelegate){
37483            this.loaded = false;
37484            this.refreshDelegate();
37485         }
37486     },
37487     
37488     /**
37489      * Destroys this panel
37490      */
37491     destroy : function(){
37492         this.el.removeAllListeners();
37493         var tempEl = document.createElement("span");
37494         tempEl.appendChild(this.el.dom);
37495         tempEl.innerHTML = "";
37496         this.el.remove();
37497         this.el = null;
37498     },
37499     
37500     /**
37501      * form - if the content panel contains a form - this is a reference to it.
37502      * @type {Roo.form.Form}
37503      */
37504     form : false,
37505     /**
37506      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37507      *    This contains a reference to it.
37508      * @type {Roo.View}
37509      */
37510     view : false,
37511     
37512       /**
37513      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37514      * <pre><code>
37515
37516 layout.addxtype({
37517        xtype : 'Form',
37518        items: [ .... ]
37519    }
37520 );
37521
37522 </code></pre>
37523      * @param {Object} cfg Xtype definition of item to add.
37524      */
37525     
37526     
37527     getChildContainer: function () {
37528         return this.getEl();
37529     }
37530     
37531     
37532     /*
37533         var  ret = new Roo.factory(cfg);
37534         return ret;
37535         
37536         
37537         // add form..
37538         if (cfg.xtype.match(/^Form$/)) {
37539             
37540             var el;
37541             //if (this.footer) {
37542             //    el = this.footer.container.insertSibling(false, 'before');
37543             //} else {
37544                 el = this.el.createChild();
37545             //}
37546
37547             this.form = new  Roo.form.Form(cfg);
37548             
37549             
37550             if ( this.form.allItems.length) {
37551                 this.form.render(el.dom);
37552             }
37553             return this.form;
37554         }
37555         // should only have one of theses..
37556         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37557             // views.. should not be just added - used named prop 'view''
37558             
37559             cfg.el = this.el.appendChild(document.createElement("div"));
37560             // factory?
37561             
37562             var ret = new Roo.factory(cfg);
37563              
37564              ret.render && ret.render(false, ''); // render blank..
37565             this.view = ret;
37566             return ret;
37567         }
37568         return false;
37569     }
37570     \*/
37571 });
37572  
37573 /**
37574  * @class Roo.bootstrap.panel.Grid
37575  * @extends Roo.bootstrap.panel.Content
37576  * @constructor
37577  * Create a new GridPanel.
37578  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37579  * @param {Object} config A the config object
37580   
37581  */
37582
37583
37584
37585 Roo.bootstrap.panel.Grid = function(config)
37586 {
37587     
37588       
37589     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37590         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37591
37592     config.el = this.wrapper;
37593     //this.el = this.wrapper;
37594     
37595       if (config.container) {
37596         // ctor'ed from a Border/panel.grid
37597         
37598         
37599         this.wrapper.setStyle("overflow", "hidden");
37600         this.wrapper.addClass('roo-grid-container');
37601
37602     }
37603     
37604     
37605     if(config.toolbar){
37606         var tool_el = this.wrapper.createChild();    
37607         this.toolbar = Roo.factory(config.toolbar);
37608         var ti = [];
37609         if (config.toolbar.items) {
37610             ti = config.toolbar.items ;
37611             delete config.toolbar.items ;
37612         }
37613         
37614         var nitems = [];
37615         this.toolbar.render(tool_el);
37616         for(var i =0;i < ti.length;i++) {
37617           //  Roo.log(['add child', items[i]]);
37618             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37619         }
37620         this.toolbar.items = nitems;
37621         
37622         delete config.toolbar;
37623     }
37624     
37625     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37626     config.grid.scrollBody = true;;
37627     config.grid.monitorWindowResize = false; // turn off autosizing
37628     config.grid.autoHeight = false;
37629     config.grid.autoWidth = false;
37630     
37631     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37632     
37633     if (config.background) {
37634         // render grid on panel activation (if panel background)
37635         this.on('activate', function(gp) {
37636             if (!gp.grid.rendered) {
37637                 gp.grid.render(this.wrapper);
37638                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37639             }
37640         });
37641             
37642     } else {
37643         this.grid.render(this.wrapper);
37644         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37645
37646     }
37647     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37648     // ??? needed ??? config.el = this.wrapper;
37649     
37650     
37651     
37652   
37653     // xtype created footer. - not sure if will work as we normally have to render first..
37654     if (this.footer && !this.footer.el && this.footer.xtype) {
37655         
37656         var ctr = this.grid.getView().getFooterPanel(true);
37657         this.footer.dataSource = this.grid.dataSource;
37658         this.footer = Roo.factory(this.footer, Roo);
37659         this.footer.render(ctr);
37660         
37661     }
37662     
37663     
37664     
37665     
37666      
37667 };
37668
37669 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37670     getId : function(){
37671         return this.grid.id;
37672     },
37673     
37674     /**
37675      * Returns the grid for this panel
37676      * @return {Roo.bootstrap.Table} 
37677      */
37678     getGrid : function(){
37679         return this.grid;    
37680     },
37681     
37682     setSize : function(width, height){
37683         if(!this.ignoreResize(width, height)){
37684             var grid = this.grid;
37685             var size = this.adjustForComponents(width, height);
37686             var gridel = grid.getGridEl();
37687             gridel.setSize(size.width, size.height);
37688             /*
37689             var thd = grid.getGridEl().select('thead',true).first();
37690             var tbd = grid.getGridEl().select('tbody', true).first();
37691             if (tbd) {
37692                 tbd.setSize(width, height - thd.getHeight());
37693             }
37694             */
37695             grid.autoSize();
37696         }
37697     },
37698      
37699     
37700     
37701     beforeSlide : function(){
37702         this.grid.getView().scroller.clip();
37703     },
37704     
37705     afterSlide : function(){
37706         this.grid.getView().scroller.unclip();
37707     },
37708     
37709     destroy : function(){
37710         this.grid.destroy();
37711         delete this.grid;
37712         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37713     }
37714 });
37715
37716 /**
37717  * @class Roo.bootstrap.panel.Nest
37718  * @extends Roo.bootstrap.panel.Content
37719  * @constructor
37720  * Create a new Panel, that can contain a layout.Border.
37721  * 
37722  * 
37723  * @param {Roo.BorderLayout} layout The layout for this panel
37724  * @param {String/Object} config A string to set only the title or a config object
37725  */
37726 Roo.bootstrap.panel.Nest = function(config)
37727 {
37728     // construct with only one argument..
37729     /* FIXME - implement nicer consturctors
37730     if (layout.layout) {
37731         config = layout;
37732         layout = config.layout;
37733         delete config.layout;
37734     }
37735     if (layout.xtype && !layout.getEl) {
37736         // then layout needs constructing..
37737         layout = Roo.factory(layout, Roo);
37738     }
37739     */
37740     
37741     config.el =  config.layout.getEl();
37742     
37743     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37744     
37745     config.layout.monitorWindowResize = false; // turn off autosizing
37746     this.layout = config.layout;
37747     this.layout.getEl().addClass("roo-layout-nested-layout");
37748     
37749     
37750     
37751     
37752 };
37753
37754 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37755
37756     setSize : function(width, height){
37757         if(!this.ignoreResize(width, height)){
37758             var size = this.adjustForComponents(width, height);
37759             var el = this.layout.getEl();
37760             if (size.height < 1) {
37761                 el.setWidth(size.width);   
37762             } else {
37763                 el.setSize(size.width, size.height);
37764             }
37765             var touch = el.dom.offsetWidth;
37766             this.layout.layout();
37767             // ie requires a double layout on the first pass
37768             if(Roo.isIE && !this.initialized){
37769                 this.initialized = true;
37770                 this.layout.layout();
37771             }
37772         }
37773     },
37774     
37775     // activate all subpanels if not currently active..
37776     
37777     setActiveState : function(active){
37778         this.active = active;
37779         this.setActiveClass(active);
37780         
37781         if(!active){
37782             this.fireEvent("deactivate", this);
37783             return;
37784         }
37785         
37786         this.fireEvent("activate", this);
37787         // not sure if this should happen before or after..
37788         if (!this.layout) {
37789             return; // should not happen..
37790         }
37791         var reg = false;
37792         for (var r in this.layout.regions) {
37793             reg = this.layout.getRegion(r);
37794             if (reg.getActivePanel()) {
37795                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37796                 reg.setActivePanel(reg.getActivePanel());
37797                 continue;
37798             }
37799             if (!reg.panels.length) {
37800                 continue;
37801             }
37802             reg.showPanel(reg.getPanel(0));
37803         }
37804         
37805         
37806         
37807         
37808     },
37809     
37810     /**
37811      * Returns the nested BorderLayout for this panel
37812      * @return {Roo.BorderLayout} 
37813      */
37814     getLayout : function(){
37815         return this.layout;
37816     },
37817     
37818      /**
37819      * Adds a xtype elements to the layout of the nested panel
37820      * <pre><code>
37821
37822 panel.addxtype({
37823        xtype : 'ContentPanel',
37824        region: 'west',
37825        items: [ .... ]
37826    }
37827 );
37828
37829 panel.addxtype({
37830         xtype : 'NestedLayoutPanel',
37831         region: 'west',
37832         layout: {
37833            center: { },
37834            west: { }   
37835         },
37836         items : [ ... list of content panels or nested layout panels.. ]
37837    }
37838 );
37839 </code></pre>
37840      * @param {Object} cfg Xtype definition of item to add.
37841      */
37842     addxtype : function(cfg) {
37843         return this.layout.addxtype(cfg);
37844     
37845     }
37846 });        /*
37847  * Based on:
37848  * Ext JS Library 1.1.1
37849  * Copyright(c) 2006-2007, Ext JS, LLC.
37850  *
37851  * Originally Released Under LGPL - original licence link has changed is not relivant.
37852  *
37853  * Fork - LGPL
37854  * <script type="text/javascript">
37855  */
37856 /**
37857  * @class Roo.TabPanel
37858  * @extends Roo.util.Observable
37859  * A lightweight tab container.
37860  * <br><br>
37861  * Usage:
37862  * <pre><code>
37863 // basic tabs 1, built from existing content
37864 var tabs = new Roo.TabPanel("tabs1");
37865 tabs.addTab("script", "View Script");
37866 tabs.addTab("markup", "View Markup");
37867 tabs.activate("script");
37868
37869 // more advanced tabs, built from javascript
37870 var jtabs = new Roo.TabPanel("jtabs");
37871 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37872
37873 // set up the UpdateManager
37874 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37875 var updater = tab2.getUpdateManager();
37876 updater.setDefaultUrl("ajax1.htm");
37877 tab2.on('activate', updater.refresh, updater, true);
37878
37879 // Use setUrl for Ajax loading
37880 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37881 tab3.setUrl("ajax2.htm", null, true);
37882
37883 // Disabled tab
37884 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37885 tab4.disable();
37886
37887 jtabs.activate("jtabs-1");
37888  * </code></pre>
37889  * @constructor
37890  * Create a new TabPanel.
37891  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37892  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37893  */
37894 Roo.bootstrap.panel.Tabs = function(config){
37895     /**
37896     * The container element for this TabPanel.
37897     * @type Roo.Element
37898     */
37899     this.el = Roo.get(config.el);
37900     delete config.el;
37901     if(config){
37902         if(typeof config == "boolean"){
37903             this.tabPosition = config ? "bottom" : "top";
37904         }else{
37905             Roo.apply(this, config);
37906         }
37907     }
37908     
37909     if(this.tabPosition == "bottom"){
37910         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37911         this.el.addClass("roo-tabs-bottom");
37912     }
37913     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37914     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37915     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37916     if(Roo.isIE){
37917         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37918     }
37919     if(this.tabPosition != "bottom"){
37920         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37921          * @type Roo.Element
37922          */
37923         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37924         this.el.addClass("roo-tabs-top");
37925     }
37926     this.items = [];
37927
37928     this.bodyEl.setStyle("position", "relative");
37929
37930     this.active = null;
37931     this.activateDelegate = this.activate.createDelegate(this);
37932
37933     this.addEvents({
37934         /**
37935          * @event tabchange
37936          * Fires when the active tab changes
37937          * @param {Roo.TabPanel} this
37938          * @param {Roo.TabPanelItem} activePanel The new active tab
37939          */
37940         "tabchange": true,
37941         /**
37942          * @event beforetabchange
37943          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37944          * @param {Roo.TabPanel} this
37945          * @param {Object} e Set cancel to true on this object to cancel the tab change
37946          * @param {Roo.TabPanelItem} tab The tab being changed to
37947          */
37948         "beforetabchange" : true
37949     });
37950
37951     Roo.EventManager.onWindowResize(this.onResize, this);
37952     this.cpad = this.el.getPadding("lr");
37953     this.hiddenCount = 0;
37954
37955
37956     // toolbar on the tabbar support...
37957     if (this.toolbar) {
37958         alert("no toolbar support yet");
37959         this.toolbar  = false;
37960         /*
37961         var tcfg = this.toolbar;
37962         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37963         this.toolbar = new Roo.Toolbar(tcfg);
37964         if (Roo.isSafari) {
37965             var tbl = tcfg.container.child('table', true);
37966             tbl.setAttribute('width', '100%');
37967         }
37968         */
37969         
37970     }
37971    
37972
37973
37974     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37975 };
37976
37977 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37978     /*
37979      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37980      */
37981     tabPosition : "top",
37982     /*
37983      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37984      */
37985     currentTabWidth : 0,
37986     /*
37987      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37988      */
37989     minTabWidth : 40,
37990     /*
37991      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37992      */
37993     maxTabWidth : 250,
37994     /*
37995      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37996      */
37997     preferredTabWidth : 175,
37998     /*
37999      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38000      */
38001     resizeTabs : false,
38002     /*
38003      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38004      */
38005     monitorResize : true,
38006     /*
38007      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38008      */
38009     toolbar : false,
38010
38011     /**
38012      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38013      * @param {String} id The id of the div to use <b>or create</b>
38014      * @param {String} text The text for the tab
38015      * @param {String} content (optional) Content to put in the TabPanelItem body
38016      * @param {Boolean} closable (optional) True to create a close icon on the tab
38017      * @return {Roo.TabPanelItem} The created TabPanelItem
38018      */
38019     addTab : function(id, text, content, closable, tpl)
38020     {
38021         var item = new Roo.bootstrap.panel.TabItem({
38022             panel: this,
38023             id : id,
38024             text : text,
38025             closable : closable,
38026             tpl : tpl
38027         });
38028         this.addTabItem(item);
38029         if(content){
38030             item.setContent(content);
38031         }
38032         return item;
38033     },
38034
38035     /**
38036      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38037      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38038      * @return {Roo.TabPanelItem}
38039      */
38040     getTab : function(id){
38041         return this.items[id];
38042     },
38043
38044     /**
38045      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38046      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38047      */
38048     hideTab : function(id){
38049         var t = this.items[id];
38050         if(!t.isHidden()){
38051            t.setHidden(true);
38052            this.hiddenCount++;
38053            this.autoSizeTabs();
38054         }
38055     },
38056
38057     /**
38058      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38059      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38060      */
38061     unhideTab : function(id){
38062         var t = this.items[id];
38063         if(t.isHidden()){
38064            t.setHidden(false);
38065            this.hiddenCount--;
38066            this.autoSizeTabs();
38067         }
38068     },
38069
38070     /**
38071      * Adds an existing {@link Roo.TabPanelItem}.
38072      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38073      */
38074     addTabItem : function(item){
38075         this.items[item.id] = item;
38076         this.items.push(item);
38077       //  if(this.resizeTabs){
38078     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38079   //         this.autoSizeTabs();
38080 //        }else{
38081 //            item.autoSize();
38082        // }
38083     },
38084
38085     /**
38086      * Removes a {@link Roo.TabPanelItem}.
38087      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38088      */
38089     removeTab : function(id){
38090         var items = this.items;
38091         var tab = items[id];
38092         if(!tab) { return; }
38093         var index = items.indexOf(tab);
38094         if(this.active == tab && items.length > 1){
38095             var newTab = this.getNextAvailable(index);
38096             if(newTab) {
38097                 newTab.activate();
38098             }
38099         }
38100         this.stripEl.dom.removeChild(tab.pnode.dom);
38101         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38102             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38103         }
38104         items.splice(index, 1);
38105         delete this.items[tab.id];
38106         tab.fireEvent("close", tab);
38107         tab.purgeListeners();
38108         this.autoSizeTabs();
38109     },
38110
38111     getNextAvailable : function(start){
38112         var items = this.items;
38113         var index = start;
38114         // look for a next tab that will slide over to
38115         // replace the one being removed
38116         while(index < items.length){
38117             var item = items[++index];
38118             if(item && !item.isHidden()){
38119                 return item;
38120             }
38121         }
38122         // if one isn't found select the previous tab (on the left)
38123         index = start;
38124         while(index >= 0){
38125             var item = items[--index];
38126             if(item && !item.isHidden()){
38127                 return item;
38128             }
38129         }
38130         return null;
38131     },
38132
38133     /**
38134      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38135      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38136      */
38137     disableTab : function(id){
38138         var tab = this.items[id];
38139         if(tab && this.active != tab){
38140             tab.disable();
38141         }
38142     },
38143
38144     /**
38145      * Enables a {@link Roo.TabPanelItem} that is disabled.
38146      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38147      */
38148     enableTab : function(id){
38149         var tab = this.items[id];
38150         tab.enable();
38151     },
38152
38153     /**
38154      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38155      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38156      * @return {Roo.TabPanelItem} The TabPanelItem.
38157      */
38158     activate : function(id){
38159         var tab = this.items[id];
38160         if(!tab){
38161             return null;
38162         }
38163         if(tab == this.active || tab.disabled){
38164             return tab;
38165         }
38166         var e = {};
38167         this.fireEvent("beforetabchange", this, e, tab);
38168         if(e.cancel !== true && !tab.disabled){
38169             if(this.active){
38170                 this.active.hide();
38171             }
38172             this.active = this.items[id];
38173             this.active.show();
38174             this.fireEvent("tabchange", this, this.active);
38175         }
38176         return tab;
38177     },
38178
38179     /**
38180      * Gets the active {@link Roo.TabPanelItem}.
38181      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38182      */
38183     getActiveTab : function(){
38184         return this.active;
38185     },
38186
38187     /**
38188      * Updates the tab body element to fit the height of the container element
38189      * for overflow scrolling
38190      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38191      */
38192     syncHeight : function(targetHeight){
38193         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38194         var bm = this.bodyEl.getMargins();
38195         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38196         this.bodyEl.setHeight(newHeight);
38197         return newHeight;
38198     },
38199
38200     onResize : function(){
38201         if(this.monitorResize){
38202             this.autoSizeTabs();
38203         }
38204     },
38205
38206     /**
38207      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38208      */
38209     beginUpdate : function(){
38210         this.updating = true;
38211     },
38212
38213     /**
38214      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38215      */
38216     endUpdate : function(){
38217         this.updating = false;
38218         this.autoSizeTabs();
38219     },
38220
38221     /**
38222      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38223      */
38224     autoSizeTabs : function(){
38225         var count = this.items.length;
38226         var vcount = count - this.hiddenCount;
38227         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38228             return;
38229         }
38230         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38231         var availWidth = Math.floor(w / vcount);
38232         var b = this.stripBody;
38233         if(b.getWidth() > w){
38234             var tabs = this.items;
38235             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38236             if(availWidth < this.minTabWidth){
38237                 /*if(!this.sleft){    // incomplete scrolling code
38238                     this.createScrollButtons();
38239                 }
38240                 this.showScroll();
38241                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38242             }
38243         }else{
38244             if(this.currentTabWidth < this.preferredTabWidth){
38245                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38246             }
38247         }
38248     },
38249
38250     /**
38251      * Returns the number of tabs in this TabPanel.
38252      * @return {Number}
38253      */
38254      getCount : function(){
38255          return this.items.length;
38256      },
38257
38258     /**
38259      * Resizes all the tabs to the passed width
38260      * @param {Number} The new width
38261      */
38262     setTabWidth : function(width){
38263         this.currentTabWidth = width;
38264         for(var i = 0, len = this.items.length; i < len; i++) {
38265                 if(!this.items[i].isHidden()) {
38266                 this.items[i].setWidth(width);
38267             }
38268         }
38269     },
38270
38271     /**
38272      * Destroys this TabPanel
38273      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38274      */
38275     destroy : function(removeEl){
38276         Roo.EventManager.removeResizeListener(this.onResize, this);
38277         for(var i = 0, len = this.items.length; i < len; i++){
38278             this.items[i].purgeListeners();
38279         }
38280         if(removeEl === true){
38281             this.el.update("");
38282             this.el.remove();
38283         }
38284     },
38285     
38286     createStrip : function(container)
38287     {
38288         var strip = document.createElement("nav");
38289         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38290         container.appendChild(strip);
38291         return strip;
38292     },
38293     
38294     createStripList : function(strip)
38295     {
38296         // div wrapper for retard IE
38297         // returns the "tr" element.
38298         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38299         //'<div class="x-tabs-strip-wrap">'+
38300           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38301           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38302         return strip.firstChild; //.firstChild.firstChild.firstChild;
38303     },
38304     createBody : function(container)
38305     {
38306         var body = document.createElement("div");
38307         Roo.id(body, "tab-body");
38308         //Roo.fly(body).addClass("x-tabs-body");
38309         Roo.fly(body).addClass("tab-content");
38310         container.appendChild(body);
38311         return body;
38312     },
38313     createItemBody :function(bodyEl, id){
38314         var body = Roo.getDom(id);
38315         if(!body){
38316             body = document.createElement("div");
38317             body.id = id;
38318         }
38319         //Roo.fly(body).addClass("x-tabs-item-body");
38320         Roo.fly(body).addClass("tab-pane");
38321          bodyEl.insertBefore(body, bodyEl.firstChild);
38322         return body;
38323     },
38324     /** @private */
38325     createStripElements :  function(stripEl, text, closable, tpl)
38326     {
38327         var td = document.createElement("li"); // was td..
38328         
38329         
38330         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38331         
38332         
38333         stripEl.appendChild(td);
38334         /*if(closable){
38335             td.className = "x-tabs-closable";
38336             if(!this.closeTpl){
38337                 this.closeTpl = new Roo.Template(
38338                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38339                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38340                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38341                 );
38342             }
38343             var el = this.closeTpl.overwrite(td, {"text": text});
38344             var close = el.getElementsByTagName("div")[0];
38345             var inner = el.getElementsByTagName("em")[0];
38346             return {"el": el, "close": close, "inner": inner};
38347         } else {
38348         */
38349         // not sure what this is..
38350 //            if(!this.tabTpl){
38351                 //this.tabTpl = new Roo.Template(
38352                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38353                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38354                 //);
38355 //                this.tabTpl = new Roo.Template(
38356 //                   '<a href="#">' +
38357 //                   '<span unselectable="on"' +
38358 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38359 //                            ' >{text}</span></a>'
38360 //                );
38361 //                
38362 //            }
38363
38364
38365             var template = tpl || this.tabTpl || false;
38366             
38367             if(!template){
38368                 
38369                 template = new Roo.Template(
38370                    '<a href="#">' +
38371                    '<span unselectable="on"' +
38372                             (this.disableTooltips ? '' : ' title="{text}"') +
38373                             ' >{text}</span></a>'
38374                 );
38375             }
38376             
38377             switch (typeof(template)) {
38378                 case 'object' :
38379                     break;
38380                 case 'string' :
38381                     template = new Roo.Template(template);
38382                     break;
38383                 default :
38384                     break;
38385             }
38386             
38387             var el = template.overwrite(td, {"text": text});
38388             
38389             var inner = el.getElementsByTagName("span")[0];
38390             
38391             return {"el": el, "inner": inner};
38392             
38393     }
38394         
38395     
38396 });
38397
38398 /**
38399  * @class Roo.TabPanelItem
38400  * @extends Roo.util.Observable
38401  * Represents an individual item (tab plus body) in a TabPanel.
38402  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38403  * @param {String} id The id of this TabPanelItem
38404  * @param {String} text The text for the tab of this TabPanelItem
38405  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38406  */
38407 Roo.bootstrap.panel.TabItem = function(config){
38408     /**
38409      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38410      * @type Roo.TabPanel
38411      */
38412     this.tabPanel = config.panel;
38413     /**
38414      * The id for this TabPanelItem
38415      * @type String
38416      */
38417     this.id = config.id;
38418     /** @private */
38419     this.disabled = false;
38420     /** @private */
38421     this.text = config.text;
38422     /** @private */
38423     this.loaded = false;
38424     this.closable = config.closable;
38425
38426     /**
38427      * The body element for this TabPanelItem.
38428      * @type Roo.Element
38429      */
38430     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38431     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38432     this.bodyEl.setStyle("display", "block");
38433     this.bodyEl.setStyle("zoom", "1");
38434     //this.hideAction();
38435
38436     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38437     /** @private */
38438     this.el = Roo.get(els.el);
38439     this.inner = Roo.get(els.inner, true);
38440     this.textEl = Roo.get(this.el.dom.firstChild, true);
38441     this.pnode = Roo.get(els.el.parentNode, true);
38442 //    this.el.on("mousedown", this.onTabMouseDown, this);
38443     this.el.on("click", this.onTabClick, this);
38444     /** @private */
38445     if(config.closable){
38446         var c = Roo.get(els.close, true);
38447         c.dom.title = this.closeText;
38448         c.addClassOnOver("close-over");
38449         c.on("click", this.closeClick, this);
38450      }
38451
38452     this.addEvents({
38453          /**
38454          * @event activate
38455          * Fires when this tab becomes the active tab.
38456          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38457          * @param {Roo.TabPanelItem} this
38458          */
38459         "activate": true,
38460         /**
38461          * @event beforeclose
38462          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38463          * @param {Roo.TabPanelItem} this
38464          * @param {Object} e Set cancel to true on this object to cancel the close.
38465          */
38466         "beforeclose": true,
38467         /**
38468          * @event close
38469          * Fires when this tab is closed.
38470          * @param {Roo.TabPanelItem} this
38471          */
38472          "close": true,
38473         /**
38474          * @event deactivate
38475          * Fires when this tab is no longer the active tab.
38476          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38477          * @param {Roo.TabPanelItem} this
38478          */
38479          "deactivate" : true
38480     });
38481     this.hidden = false;
38482
38483     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38484 };
38485
38486 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38487            {
38488     purgeListeners : function(){
38489        Roo.util.Observable.prototype.purgeListeners.call(this);
38490        this.el.removeAllListeners();
38491     },
38492     /**
38493      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38494      */
38495     show : function(){
38496         this.pnode.addClass("active");
38497         this.showAction();
38498         if(Roo.isOpera){
38499             this.tabPanel.stripWrap.repaint();
38500         }
38501         this.fireEvent("activate", this.tabPanel, this);
38502     },
38503
38504     /**
38505      * Returns true if this tab is the active tab.
38506      * @return {Boolean}
38507      */
38508     isActive : function(){
38509         return this.tabPanel.getActiveTab() == this;
38510     },
38511
38512     /**
38513      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38514      */
38515     hide : function(){
38516         this.pnode.removeClass("active");
38517         this.hideAction();
38518         this.fireEvent("deactivate", this.tabPanel, this);
38519     },
38520
38521     hideAction : function(){
38522         this.bodyEl.hide();
38523         this.bodyEl.setStyle("position", "absolute");
38524         this.bodyEl.setLeft("-20000px");
38525         this.bodyEl.setTop("-20000px");
38526     },
38527
38528     showAction : function(){
38529         this.bodyEl.setStyle("position", "relative");
38530         this.bodyEl.setTop("");
38531         this.bodyEl.setLeft("");
38532         this.bodyEl.show();
38533     },
38534
38535     /**
38536      * Set the tooltip for the tab.
38537      * @param {String} tooltip The tab's tooltip
38538      */
38539     setTooltip : function(text){
38540         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38541             this.textEl.dom.qtip = text;
38542             this.textEl.dom.removeAttribute('title');
38543         }else{
38544             this.textEl.dom.title = text;
38545         }
38546     },
38547
38548     onTabClick : function(e){
38549         e.preventDefault();
38550         this.tabPanel.activate(this.id);
38551     },
38552
38553     onTabMouseDown : function(e){
38554         e.preventDefault();
38555         this.tabPanel.activate(this.id);
38556     },
38557 /*
38558     getWidth : function(){
38559         return this.inner.getWidth();
38560     },
38561
38562     setWidth : function(width){
38563         var iwidth = width - this.pnode.getPadding("lr");
38564         this.inner.setWidth(iwidth);
38565         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38566         this.pnode.setWidth(width);
38567     },
38568 */
38569     /**
38570      * Show or hide the tab
38571      * @param {Boolean} hidden True to hide or false to show.
38572      */
38573     setHidden : function(hidden){
38574         this.hidden = hidden;
38575         this.pnode.setStyle("display", hidden ? "none" : "");
38576     },
38577
38578     /**
38579      * Returns true if this tab is "hidden"
38580      * @return {Boolean}
38581      */
38582     isHidden : function(){
38583         return this.hidden;
38584     },
38585
38586     /**
38587      * Returns the text for this tab
38588      * @return {String}
38589      */
38590     getText : function(){
38591         return this.text;
38592     },
38593     /*
38594     autoSize : function(){
38595         //this.el.beginMeasure();
38596         this.textEl.setWidth(1);
38597         /*
38598          *  #2804 [new] Tabs in Roojs
38599          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38600          */
38601         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38602         //this.el.endMeasure();
38603     //},
38604
38605     /**
38606      * Sets the text for the tab (Note: this also sets the tooltip text)
38607      * @param {String} text The tab's text and tooltip
38608      */
38609     setText : function(text){
38610         this.text = text;
38611         this.textEl.update(text);
38612         this.setTooltip(text);
38613         //if(!this.tabPanel.resizeTabs){
38614         //    this.autoSize();
38615         //}
38616     },
38617     /**
38618      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38619      */
38620     activate : function(){
38621         this.tabPanel.activate(this.id);
38622     },
38623
38624     /**
38625      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38626      */
38627     disable : function(){
38628         if(this.tabPanel.active != this){
38629             this.disabled = true;
38630             this.pnode.addClass("disabled");
38631         }
38632     },
38633
38634     /**
38635      * Enables this TabPanelItem if it was previously disabled.
38636      */
38637     enable : function(){
38638         this.disabled = false;
38639         this.pnode.removeClass("disabled");
38640     },
38641
38642     /**
38643      * Sets the content for this TabPanelItem.
38644      * @param {String} content The content
38645      * @param {Boolean} loadScripts true to look for and load scripts
38646      */
38647     setContent : function(content, loadScripts){
38648         this.bodyEl.update(content, loadScripts);
38649     },
38650
38651     /**
38652      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38653      * @return {Roo.UpdateManager} The UpdateManager
38654      */
38655     getUpdateManager : function(){
38656         return this.bodyEl.getUpdateManager();
38657     },
38658
38659     /**
38660      * Set a URL to be used to load the content for this TabPanelItem.
38661      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38662      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
38663      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
38664      * @return {Roo.UpdateManager} The UpdateManager
38665      */
38666     setUrl : function(url, params, loadOnce){
38667         if(this.refreshDelegate){
38668             this.un('activate', this.refreshDelegate);
38669         }
38670         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38671         this.on("activate", this.refreshDelegate);
38672         return this.bodyEl.getUpdateManager();
38673     },
38674
38675     /** @private */
38676     _handleRefresh : function(url, params, loadOnce){
38677         if(!loadOnce || !this.loaded){
38678             var updater = this.bodyEl.getUpdateManager();
38679             updater.update(url, params, this._setLoaded.createDelegate(this));
38680         }
38681     },
38682
38683     /**
38684      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38685      *   Will fail silently if the setUrl method has not been called.
38686      *   This does not activate the panel, just updates its content.
38687      */
38688     refresh : function(){
38689         if(this.refreshDelegate){
38690            this.loaded = false;
38691            this.refreshDelegate();
38692         }
38693     },
38694
38695     /** @private */
38696     _setLoaded : function(){
38697         this.loaded = true;
38698     },
38699
38700     /** @private */
38701     closeClick : function(e){
38702         var o = {};
38703         e.stopEvent();
38704         this.fireEvent("beforeclose", this, o);
38705         if(o.cancel !== true){
38706             this.tabPanel.removeTab(this.id);
38707         }
38708     },
38709     /**
38710      * The text displayed in the tooltip for the close icon.
38711      * @type String
38712      */
38713     closeText : "Close this tab"
38714 });
38715 /**
38716 *    This script refer to:
38717 *    Title: International Telephone Input
38718 *    Author: Jack O'Connor
38719 *    Code version:  v12.1.12
38720 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38721 **/
38722
38723 Roo.bootstrap.PhoneInputData = function() {
38724     var d = [
38725       [
38726         "Afghanistan (‫افغانستان‬‎)",
38727         "af",
38728         "93"
38729       ],
38730       [
38731         "Albania (Shqipëri)",
38732         "al",
38733         "355"
38734       ],
38735       [
38736         "Algeria (‫الجزائر‬‎)",
38737         "dz",
38738         "213"
38739       ],
38740       [
38741         "American Samoa",
38742         "as",
38743         "1684"
38744       ],
38745       [
38746         "Andorra",
38747         "ad",
38748         "376"
38749       ],
38750       [
38751         "Angola",
38752         "ao",
38753         "244"
38754       ],
38755       [
38756         "Anguilla",
38757         "ai",
38758         "1264"
38759       ],
38760       [
38761         "Antigua and Barbuda",
38762         "ag",
38763         "1268"
38764       ],
38765       [
38766         "Argentina",
38767         "ar",
38768         "54"
38769       ],
38770       [
38771         "Armenia (Հայաստան)",
38772         "am",
38773         "374"
38774       ],
38775       [
38776         "Aruba",
38777         "aw",
38778         "297"
38779       ],
38780       [
38781         "Australia",
38782         "au",
38783         "61",
38784         0
38785       ],
38786       [
38787         "Austria (Österreich)",
38788         "at",
38789         "43"
38790       ],
38791       [
38792         "Azerbaijan (Azərbaycan)",
38793         "az",
38794         "994"
38795       ],
38796       [
38797         "Bahamas",
38798         "bs",
38799         "1242"
38800       ],
38801       [
38802         "Bahrain (‫البحرين‬‎)",
38803         "bh",
38804         "973"
38805       ],
38806       [
38807         "Bangladesh (বাংলাদেশ)",
38808         "bd",
38809         "880"
38810       ],
38811       [
38812         "Barbados",
38813         "bb",
38814         "1246"
38815       ],
38816       [
38817         "Belarus (Беларусь)",
38818         "by",
38819         "375"
38820       ],
38821       [
38822         "Belgium (België)",
38823         "be",
38824         "32"
38825       ],
38826       [
38827         "Belize",
38828         "bz",
38829         "501"
38830       ],
38831       [
38832         "Benin (Bénin)",
38833         "bj",
38834         "229"
38835       ],
38836       [
38837         "Bermuda",
38838         "bm",
38839         "1441"
38840       ],
38841       [
38842         "Bhutan (འབྲུག)",
38843         "bt",
38844         "975"
38845       ],
38846       [
38847         "Bolivia",
38848         "bo",
38849         "591"
38850       ],
38851       [
38852         "Bosnia and Herzegovina (Босна и Херцеговина)",
38853         "ba",
38854         "387"
38855       ],
38856       [
38857         "Botswana",
38858         "bw",
38859         "267"
38860       ],
38861       [
38862         "Brazil (Brasil)",
38863         "br",
38864         "55"
38865       ],
38866       [
38867         "British Indian Ocean Territory",
38868         "io",
38869         "246"
38870       ],
38871       [
38872         "British Virgin Islands",
38873         "vg",
38874         "1284"
38875       ],
38876       [
38877         "Brunei",
38878         "bn",
38879         "673"
38880       ],
38881       [
38882         "Bulgaria (България)",
38883         "bg",
38884         "359"
38885       ],
38886       [
38887         "Burkina Faso",
38888         "bf",
38889         "226"
38890       ],
38891       [
38892         "Burundi (Uburundi)",
38893         "bi",
38894         "257"
38895       ],
38896       [
38897         "Cambodia (កម្ពុជា)",
38898         "kh",
38899         "855"
38900       ],
38901       [
38902         "Cameroon (Cameroun)",
38903         "cm",
38904         "237"
38905       ],
38906       [
38907         "Canada",
38908         "ca",
38909         "1",
38910         1,
38911         ["204", "226", "236", "249", "250", "289", "306", "343", "365", "387", "403", "416", "418", "431", "437", "438", "450", "506", "514", "519", "548", "579", "581", "587", "604", "613", "639", "647", "672", "705", "709", "742", "778", "780", "782", "807", "819", "825", "867", "873", "902", "905"]
38912       ],
38913       [
38914         "Cape Verde (Kabu Verdi)",
38915         "cv",
38916         "238"
38917       ],
38918       [
38919         "Caribbean Netherlands",
38920         "bq",
38921         "599",
38922         1
38923       ],
38924       [
38925         "Cayman Islands",
38926         "ky",
38927         "1345"
38928       ],
38929       [
38930         "Central African Republic (République centrafricaine)",
38931         "cf",
38932         "236"
38933       ],
38934       [
38935         "Chad (Tchad)",
38936         "td",
38937         "235"
38938       ],
38939       [
38940         "Chile",
38941         "cl",
38942         "56"
38943       ],
38944       [
38945         "China (中国)",
38946         "cn",
38947         "86"
38948       ],
38949       [
38950         "Christmas Island",
38951         "cx",
38952         "61",
38953         2
38954       ],
38955       [
38956         "Cocos (Keeling) Islands",
38957         "cc",
38958         "61",
38959         1
38960       ],
38961       [
38962         "Colombia",
38963         "co",
38964         "57"
38965       ],
38966       [
38967         "Comoros (‫جزر القمر‬‎)",
38968         "km",
38969         "269"
38970       ],
38971       [
38972         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38973         "cd",
38974         "243"
38975       ],
38976       [
38977         "Congo (Republic) (Congo-Brazzaville)",
38978         "cg",
38979         "242"
38980       ],
38981       [
38982         "Cook Islands",
38983         "ck",
38984         "682"
38985       ],
38986       [
38987         "Costa Rica",
38988         "cr",
38989         "506"
38990       ],
38991       [
38992         "Côte d’Ivoire",
38993         "ci",
38994         "225"
38995       ],
38996       [
38997         "Croatia (Hrvatska)",
38998         "hr",
38999         "385"
39000       ],
39001       [
39002         "Cuba",
39003         "cu",
39004         "53"
39005       ],
39006       [
39007         "Curaçao",
39008         "cw",
39009         "599",
39010         0
39011       ],
39012       [
39013         "Cyprus (Κύπρος)",
39014         "cy",
39015         "357"
39016       ],
39017       [
39018         "Czech Republic (Česká republika)",
39019         "cz",
39020         "420"
39021       ],
39022       [
39023         "Denmark (Danmark)",
39024         "dk",
39025         "45"
39026       ],
39027       [
39028         "Djibouti",
39029         "dj",
39030         "253"
39031       ],
39032       [
39033         "Dominica",
39034         "dm",
39035         "1767"
39036       ],
39037       [
39038         "Dominican Republic (República Dominicana)",
39039         "do",
39040         "1",
39041         2,
39042         ["809", "829", "849"]
39043       ],
39044       [
39045         "Ecuador",
39046         "ec",
39047         "593"
39048       ],
39049       [
39050         "Egypt (‫مصر‬‎)",
39051         "eg",
39052         "20"
39053       ],
39054       [
39055         "El Salvador",
39056         "sv",
39057         "503"
39058       ],
39059       [
39060         "Equatorial Guinea (Guinea Ecuatorial)",
39061         "gq",
39062         "240"
39063       ],
39064       [
39065         "Eritrea",
39066         "er",
39067         "291"
39068       ],
39069       [
39070         "Estonia (Eesti)",
39071         "ee",
39072         "372"
39073       ],
39074       [
39075         "Ethiopia",
39076         "et",
39077         "251"
39078       ],
39079       [
39080         "Falkland Islands (Islas Malvinas)",
39081         "fk",
39082         "500"
39083       ],
39084       [
39085         "Faroe Islands (Føroyar)",
39086         "fo",
39087         "298"
39088       ],
39089       [
39090         "Fiji",
39091         "fj",
39092         "679"
39093       ],
39094       [
39095         "Finland (Suomi)",
39096         "fi",
39097         "358",
39098         0
39099       ],
39100       [
39101         "France",
39102         "fr",
39103         "33"
39104       ],
39105       [
39106         "French Guiana (Guyane française)",
39107         "gf",
39108         "594"
39109       ],
39110       [
39111         "French Polynesia (Polynésie française)",
39112         "pf",
39113         "689"
39114       ],
39115       [
39116         "Gabon",
39117         "ga",
39118         "241"
39119       ],
39120       [
39121         "Gambia",
39122         "gm",
39123         "220"
39124       ],
39125       [
39126         "Georgia (საქართველო)",
39127         "ge",
39128         "995"
39129       ],
39130       [
39131         "Germany (Deutschland)",
39132         "de",
39133         "49"
39134       ],
39135       [
39136         "Ghana (Gaana)",
39137         "gh",
39138         "233"
39139       ],
39140       [
39141         "Gibraltar",
39142         "gi",
39143         "350"
39144       ],
39145       [
39146         "Greece (Ελλάδα)",
39147         "gr",
39148         "30"
39149       ],
39150       [
39151         "Greenland (Kalaallit Nunaat)",
39152         "gl",
39153         "299"
39154       ],
39155       [
39156         "Grenada",
39157         "gd",
39158         "1473"
39159       ],
39160       [
39161         "Guadeloupe",
39162         "gp",
39163         "590",
39164         0
39165       ],
39166       [
39167         "Guam",
39168         "gu",
39169         "1671"
39170       ],
39171       [
39172         "Guatemala",
39173         "gt",
39174         "502"
39175       ],
39176       [
39177         "Guernsey",
39178         "gg",
39179         "44",
39180         1
39181       ],
39182       [
39183         "Guinea (Guinée)",
39184         "gn",
39185         "224"
39186       ],
39187       [
39188         "Guinea-Bissau (Guiné Bissau)",
39189         "gw",
39190         "245"
39191       ],
39192       [
39193         "Guyana",
39194         "gy",
39195         "592"
39196       ],
39197       [
39198         "Haiti",
39199         "ht",
39200         "509"
39201       ],
39202       [
39203         "Honduras",
39204         "hn",
39205         "504"
39206       ],
39207       [
39208         "Hong Kong (香港)",
39209         "hk",
39210         "852"
39211       ],
39212       [
39213         "Hungary (Magyarország)",
39214         "hu",
39215         "36"
39216       ],
39217       [
39218         "Iceland (Ísland)",
39219         "is",
39220         "354"
39221       ],
39222       [
39223         "India (भारत)",
39224         "in",
39225         "91"
39226       ],
39227       [
39228         "Indonesia",
39229         "id",
39230         "62"
39231       ],
39232       [
39233         "Iran (‫ایران‬‎)",
39234         "ir",
39235         "98"
39236       ],
39237       [
39238         "Iraq (‫العراق‬‎)",
39239         "iq",
39240         "964"
39241       ],
39242       [
39243         "Ireland",
39244         "ie",
39245         "353"
39246       ],
39247       [
39248         "Isle of Man",
39249         "im",
39250         "44",
39251         2
39252       ],
39253       [
39254         "Israel (‫ישראל‬‎)",
39255         "il",
39256         "972"
39257       ],
39258       [
39259         "Italy (Italia)",
39260         "it",
39261         "39",
39262         0
39263       ],
39264       [
39265         "Jamaica",
39266         "jm",
39267         "1876"
39268       ],
39269       [
39270         "Japan (日本)",
39271         "jp",
39272         "81"
39273       ],
39274       [
39275         "Jersey",
39276         "je",
39277         "44",
39278         3
39279       ],
39280       [
39281         "Jordan (‫الأردن‬‎)",
39282         "jo",
39283         "962"
39284       ],
39285       [
39286         "Kazakhstan (Казахстан)",
39287         "kz",
39288         "7",
39289         1
39290       ],
39291       [
39292         "Kenya",
39293         "ke",
39294         "254"
39295       ],
39296       [
39297         "Kiribati",
39298         "ki",
39299         "686"
39300       ],
39301       [
39302         "Kosovo",
39303         "xk",
39304         "383"
39305       ],
39306       [
39307         "Kuwait (‫الكويت‬‎)",
39308         "kw",
39309         "965"
39310       ],
39311       [
39312         "Kyrgyzstan (Кыргызстан)",
39313         "kg",
39314         "996"
39315       ],
39316       [
39317         "Laos (ລາວ)",
39318         "la",
39319         "856"
39320       ],
39321       [
39322         "Latvia (Latvija)",
39323         "lv",
39324         "371"
39325       ],
39326       [
39327         "Lebanon (‫لبنان‬‎)",
39328         "lb",
39329         "961"
39330       ],
39331       [
39332         "Lesotho",
39333         "ls",
39334         "266"
39335       ],
39336       [
39337         "Liberia",
39338         "lr",
39339         "231"
39340       ],
39341       [
39342         "Libya (‫ليبيا‬‎)",
39343         "ly",
39344         "218"
39345       ],
39346       [
39347         "Liechtenstein",
39348         "li",
39349         "423"
39350       ],
39351       [
39352         "Lithuania (Lietuva)",
39353         "lt",
39354         "370"
39355       ],
39356       [
39357         "Luxembourg",
39358         "lu",
39359         "352"
39360       ],
39361       [
39362         "Macau (澳門)",
39363         "mo",
39364         "853"
39365       ],
39366       [
39367         "Macedonia (FYROM) (Македонија)",
39368         "mk",
39369         "389"
39370       ],
39371       [
39372         "Madagascar (Madagasikara)",
39373         "mg",
39374         "261"
39375       ],
39376       [
39377         "Malawi",
39378         "mw",
39379         "265"
39380       ],
39381       [
39382         "Malaysia",
39383         "my",
39384         "60"
39385       ],
39386       [
39387         "Maldives",
39388         "mv",
39389         "960"
39390       ],
39391       [
39392         "Mali",
39393         "ml",
39394         "223"
39395       ],
39396       [
39397         "Malta",
39398         "mt",
39399         "356"
39400       ],
39401       [
39402         "Marshall Islands",
39403         "mh",
39404         "692"
39405       ],
39406       [
39407         "Martinique",
39408         "mq",
39409         "596"
39410       ],
39411       [
39412         "Mauritania (‫موريتانيا‬‎)",
39413         "mr",
39414         "222"
39415       ],
39416       [
39417         "Mauritius (Moris)",
39418         "mu",
39419         "230"
39420       ],
39421       [
39422         "Mayotte",
39423         "yt",
39424         "262",
39425         1
39426       ],
39427       [
39428         "Mexico (México)",
39429         "mx",
39430         "52"
39431       ],
39432       [
39433         "Micronesia",
39434         "fm",
39435         "691"
39436       ],
39437       [
39438         "Moldova (Republica Moldova)",
39439         "md",
39440         "373"
39441       ],
39442       [
39443         "Monaco",
39444         "mc",
39445         "377"
39446       ],
39447       [
39448         "Mongolia (Монгол)",
39449         "mn",
39450         "976"
39451       ],
39452       [
39453         "Montenegro (Crna Gora)",
39454         "me",
39455         "382"
39456       ],
39457       [
39458         "Montserrat",
39459         "ms",
39460         "1664"
39461       ],
39462       [
39463         "Morocco (‫المغرب‬‎)",
39464         "ma",
39465         "212",
39466         0
39467       ],
39468       [
39469         "Mozambique (Moçambique)",
39470         "mz",
39471         "258"
39472       ],
39473       [
39474         "Myanmar (Burma) (မြန်မာ)",
39475         "mm",
39476         "95"
39477       ],
39478       [
39479         "Namibia (Namibië)",
39480         "na",
39481         "264"
39482       ],
39483       [
39484         "Nauru",
39485         "nr",
39486         "674"
39487       ],
39488       [
39489         "Nepal (नेपाल)",
39490         "np",
39491         "977"
39492       ],
39493       [
39494         "Netherlands (Nederland)",
39495         "nl",
39496         "31"
39497       ],
39498       [
39499         "New Caledonia (Nouvelle-Calédonie)",
39500         "nc",
39501         "687"
39502       ],
39503       [
39504         "New Zealand",
39505         "nz",
39506         "64"
39507       ],
39508       [
39509         "Nicaragua",
39510         "ni",
39511         "505"
39512       ],
39513       [
39514         "Niger (Nijar)",
39515         "ne",
39516         "227"
39517       ],
39518       [
39519         "Nigeria",
39520         "ng",
39521         "234"
39522       ],
39523       [
39524         "Niue",
39525         "nu",
39526         "683"
39527       ],
39528       [
39529         "Norfolk Island",
39530         "nf",
39531         "672"
39532       ],
39533       [
39534         "North Korea (조선 민주주의 인민 공화국)",
39535         "kp",
39536         "850"
39537       ],
39538       [
39539         "Northern Mariana Islands",
39540         "mp",
39541         "1670"
39542       ],
39543       [
39544         "Norway (Norge)",
39545         "no",
39546         "47",
39547         0
39548       ],
39549       [
39550         "Oman (‫عُمان‬‎)",
39551         "om",
39552         "968"
39553       ],
39554       [
39555         "Pakistan (‫پاکستان‬‎)",
39556         "pk",
39557         "92"
39558       ],
39559       [
39560         "Palau",
39561         "pw",
39562         "680"
39563       ],
39564       [
39565         "Palestine (‫فلسطين‬‎)",
39566         "ps",
39567         "970"
39568       ],
39569       [
39570         "Panama (Panamá)",
39571         "pa",
39572         "507"
39573       ],
39574       [
39575         "Papua New Guinea",
39576         "pg",
39577         "675"
39578       ],
39579       [
39580         "Paraguay",
39581         "py",
39582         "595"
39583       ],
39584       [
39585         "Peru (Perú)",
39586         "pe",
39587         "51"
39588       ],
39589       [
39590         "Philippines",
39591         "ph",
39592         "63"
39593       ],
39594       [
39595         "Poland (Polska)",
39596         "pl",
39597         "48"
39598       ],
39599       [
39600         "Portugal",
39601         "pt",
39602         "351"
39603       ],
39604       [
39605         "Puerto Rico",
39606         "pr",
39607         "1",
39608         3,
39609         ["787", "939"]
39610       ],
39611       [
39612         "Qatar (‫قطر‬‎)",
39613         "qa",
39614         "974"
39615       ],
39616       [
39617         "Réunion (La Réunion)",
39618         "re",
39619         "262",
39620         0
39621       ],
39622       [
39623         "Romania (România)",
39624         "ro",
39625         "40"
39626       ],
39627       [
39628         "Russia (Россия)",
39629         "ru",
39630         "7",
39631         0
39632       ],
39633       [
39634         "Rwanda",
39635         "rw",
39636         "250"
39637       ],
39638       [
39639         "Saint Barthélemy",
39640         "bl",
39641         "590",
39642         1
39643       ],
39644       [
39645         "Saint Helena",
39646         "sh",
39647         "290"
39648       ],
39649       [
39650         "Saint Kitts and Nevis",
39651         "kn",
39652         "1869"
39653       ],
39654       [
39655         "Saint Lucia",
39656         "lc",
39657         "1758"
39658       ],
39659       [
39660         "Saint Martin (Saint-Martin (partie française))",
39661         "mf",
39662         "590",
39663         2
39664       ],
39665       [
39666         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39667         "pm",
39668         "508"
39669       ],
39670       [
39671         "Saint Vincent and the Grenadines",
39672         "vc",
39673         "1784"
39674       ],
39675       [
39676         "Samoa",
39677         "ws",
39678         "685"
39679       ],
39680       [
39681         "San Marino",
39682         "sm",
39683         "378"
39684       ],
39685       [
39686         "São Tomé and Príncipe (São Tomé e Príncipe)",
39687         "st",
39688         "239"
39689       ],
39690       [
39691         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39692         "sa",
39693         "966"
39694       ],
39695       [
39696         "Senegal (Sénégal)",
39697         "sn",
39698         "221"
39699       ],
39700       [
39701         "Serbia (Србија)",
39702         "rs",
39703         "381"
39704       ],
39705       [
39706         "Seychelles",
39707         "sc",
39708         "248"
39709       ],
39710       [
39711         "Sierra Leone",
39712         "sl",
39713         "232"
39714       ],
39715       [
39716         "Singapore",
39717         "sg",
39718         "65"
39719       ],
39720       [
39721         "Sint Maarten",
39722         "sx",
39723         "1721"
39724       ],
39725       [
39726         "Slovakia (Slovensko)",
39727         "sk",
39728         "421"
39729       ],
39730       [
39731         "Slovenia (Slovenija)",
39732         "si",
39733         "386"
39734       ],
39735       [
39736         "Solomon Islands",
39737         "sb",
39738         "677"
39739       ],
39740       [
39741         "Somalia (Soomaaliya)",
39742         "so",
39743         "252"
39744       ],
39745       [
39746         "South Africa",
39747         "za",
39748         "27"
39749       ],
39750       [
39751         "South Korea (대한민국)",
39752         "kr",
39753         "82"
39754       ],
39755       [
39756         "South Sudan (‫جنوب السودان‬‎)",
39757         "ss",
39758         "211"
39759       ],
39760       [
39761         "Spain (España)",
39762         "es",
39763         "34"
39764       ],
39765       [
39766         "Sri Lanka (ශ්‍රී ලංකාව)",
39767         "lk",
39768         "94"
39769       ],
39770       [
39771         "Sudan (‫السودان‬‎)",
39772         "sd",
39773         "249"
39774       ],
39775       [
39776         "Suriname",
39777         "sr",
39778         "597"
39779       ],
39780       [
39781         "Svalbard and Jan Mayen",
39782         "sj",
39783         "47",
39784         1
39785       ],
39786       [
39787         "Swaziland",
39788         "sz",
39789         "268"
39790       ],
39791       [
39792         "Sweden (Sverige)",
39793         "se",
39794         "46"
39795       ],
39796       [
39797         "Switzerland (Schweiz)",
39798         "ch",
39799         "41"
39800       ],
39801       [
39802         "Syria (‫سوريا‬‎)",
39803         "sy",
39804         "963"
39805       ],
39806       [
39807         "Taiwan (台灣)",
39808         "tw",
39809         "886"
39810       ],
39811       [
39812         "Tajikistan",
39813         "tj",
39814         "992"
39815       ],
39816       [
39817         "Tanzania",
39818         "tz",
39819         "255"
39820       ],
39821       [
39822         "Thailand (ไทย)",
39823         "th",
39824         "66"
39825       ],
39826       [
39827         "Timor-Leste",
39828         "tl",
39829         "670"
39830       ],
39831       [
39832         "Togo",
39833         "tg",
39834         "228"
39835       ],
39836       [
39837         "Tokelau",
39838         "tk",
39839         "690"
39840       ],
39841       [
39842         "Tonga",
39843         "to",
39844         "676"
39845       ],
39846       [
39847         "Trinidad and Tobago",
39848         "tt",
39849         "1868"
39850       ],
39851       [
39852         "Tunisia (‫تونس‬‎)",
39853         "tn",
39854         "216"
39855       ],
39856       [
39857         "Turkey (Türkiye)",
39858         "tr",
39859         "90"
39860       ],
39861       [
39862         "Turkmenistan",
39863         "tm",
39864         "993"
39865       ],
39866       [
39867         "Turks and Caicos Islands",
39868         "tc",
39869         "1649"
39870       ],
39871       [
39872         "Tuvalu",
39873         "tv",
39874         "688"
39875       ],
39876       [
39877         "U.S. Virgin Islands",
39878         "vi",
39879         "1340"
39880       ],
39881       [
39882         "Uganda",
39883         "ug",
39884         "256"
39885       ],
39886       [
39887         "Ukraine (Україна)",
39888         "ua",
39889         "380"
39890       ],
39891       [
39892         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39893         "ae",
39894         "971"
39895       ],
39896       [
39897         "United Kingdom",
39898         "gb",
39899         "44",
39900         0
39901       ],
39902       [
39903         "United States",
39904         "us",
39905         "1",
39906         0
39907       ],
39908       [
39909         "Uruguay",
39910         "uy",
39911         "598"
39912       ],
39913       [
39914         "Uzbekistan (Oʻzbekiston)",
39915         "uz",
39916         "998"
39917       ],
39918       [
39919         "Vanuatu",
39920         "vu",
39921         "678"
39922       ],
39923       [
39924         "Vatican City (Città del Vaticano)",
39925         "va",
39926         "39",
39927         1
39928       ],
39929       [
39930         "Venezuela",
39931         "ve",
39932         "58"
39933       ],
39934       [
39935         "Vietnam (Việt Nam)",
39936         "vn",
39937         "84"
39938       ],
39939       [
39940         "Wallis and Futuna (Wallis-et-Futuna)",
39941         "wf",
39942         "681"
39943       ],
39944       [
39945         "Western Sahara (‫الصحراء الغربية‬‎)",
39946         "eh",
39947         "212",
39948         1
39949       ],
39950       [
39951         "Yemen (‫اليمن‬‎)",
39952         "ye",
39953         "967"
39954       ],
39955       [
39956         "Zambia",
39957         "zm",
39958         "260"
39959       ],
39960       [
39961         "Zimbabwe",
39962         "zw",
39963         "263"
39964       ],
39965       [
39966         "Åland Islands",
39967         "ax",
39968         "358",
39969         1
39970       ]
39971   ];
39972   
39973   return d;
39974 }/**
39975 *    This script refer to:
39976 *    Title: International Telephone Input
39977 *    Author: Jack O'Connor
39978 *    Code version:  v12.1.12
39979 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39980 **/
39981
39982 /**
39983  * @class Roo.bootstrap.PhoneInput
39984  * @extends Roo.bootstrap.TriggerField
39985  * An input with International dial-code selection
39986  
39987  * @cfg {String} defaultDialCode default '+852'
39988  * @cfg {Array} preferedCountries default []
39989   
39990  * @constructor
39991  * Create a new PhoneInput.
39992  * @param {Object} config Configuration options
39993  */
39994
39995 Roo.bootstrap.PhoneInput = function(config) {
39996     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39997 };
39998
39999 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40000         
40001         listWidth: undefined,
40002         
40003         selectedClass: 'active',
40004         
40005         invalidClass : "has-warning",
40006         
40007         validClass: 'has-success',
40008         
40009         allowed: '0123456789',
40010         
40011         max_length: 15,
40012         
40013         /**
40014          * @cfg {String} defaultDialCode The default dial code when initializing the input
40015          */
40016         defaultDialCode: '+852',
40017         
40018         /**
40019          * @cfg {Array} preferedCountries A list of iso2 in array (e.g. ['hk','us']). Those related countries will show at the top of the input's choices
40020          */
40021         preferedCountries: false,
40022         
40023         getAutoCreate : function()
40024         {
40025             var data = Roo.bootstrap.PhoneInputData();
40026             var align = this.labelAlign || this.parentLabelAlign();
40027             var id = Roo.id();
40028             
40029             this.allCountries = [];
40030             this.dialCodeMapping = [];
40031             
40032             for (var i = 0; i < data.length; i++) {
40033               var c = data[i];
40034               this.allCountries[i] = {
40035                 name: c[0],
40036                 iso2: c[1],
40037                 dialCode: c[2],
40038                 priority: c[3] || 0,
40039                 areaCodes: c[4] || null
40040               };
40041               this.dialCodeMapping[c[2]] = {
40042                   name: c[0],
40043                   iso2: c[1],
40044                   priority: c[3] || 0,
40045                   areaCodes: c[4] || null
40046               };
40047             }
40048             
40049             var cfg = {
40050                 cls: 'form-group',
40051                 cn: []
40052             };
40053             
40054             var input =  {
40055                 tag: 'input',
40056                 id : id,
40057                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40058                 maxlength: this.max_length,
40059                 cls : 'form-control tel-input',
40060                 autocomplete: 'new-password'
40061             };
40062             
40063             var hiddenInput = {
40064                 tag: 'input',
40065                 type: 'hidden',
40066                 cls: 'hidden-tel-input'
40067             };
40068             
40069             if (this.name) {
40070                 hiddenInput.name = this.name;
40071             }
40072             
40073             if (this.disabled) {
40074                 input.disabled = true;
40075             }
40076             
40077             var flag_container = {
40078                 tag: 'div',
40079                 cls: 'flag-box',
40080                 cn: [
40081                     {
40082                         tag: 'div',
40083                         cls: 'flag'
40084                     },
40085                     {
40086                         tag: 'div',
40087                         cls: 'caret'
40088                     }
40089                 ]
40090             };
40091             
40092             var box = {
40093                 tag: 'div',
40094                 cls: this.hasFeedback ? 'has-feedback' : '',
40095                 cn: [
40096                     hiddenInput,
40097                     input,
40098                     {
40099                         tag: 'input',
40100                         cls: 'dial-code-holder',
40101                         disabled: true
40102                     }
40103                 ]
40104             };
40105             
40106             var container = {
40107                 cls: 'roo-select2-container input-group',
40108                 cn: [
40109                     flag_container,
40110                     box
40111                 ]
40112             };
40113             
40114             if (this.fieldLabel.length) {
40115                 var indicator = {
40116                     tag: 'i',
40117                     tooltip: 'This field is required'
40118                 };
40119                 
40120                 var label = {
40121                     tag: 'label',
40122                     'for':  id,
40123                     cls: 'control-label',
40124                     cn: []
40125                 };
40126                 
40127                 var label_text = {
40128                     tag: 'span',
40129                     html: this.fieldLabel
40130                 };
40131                 
40132                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40133                 label.cn = [
40134                     indicator,
40135                     label_text
40136                 ];
40137                 
40138                 if(this.indicatorpos == 'right') {
40139                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40140                     label.cn = [
40141                         label_text,
40142                         indicator
40143                     ];
40144                 }
40145                 
40146                 if(align == 'left') {
40147                     container = {
40148                         tag: 'div',
40149                         cn: [
40150                             container
40151                         ]
40152                     };
40153                     
40154                     if(this.labelWidth > 12){
40155                         label.style = "width: " + this.labelWidth + 'px';
40156                     }
40157                     if(this.labelWidth < 13 && this.labelmd == 0){
40158                         this.labelmd = this.labelWidth;
40159                     }
40160                     if(this.labellg > 0){
40161                         label.cls += ' col-lg-' + this.labellg;
40162                         input.cls += ' col-lg-' + (12 - this.labellg);
40163                     }
40164                     if(this.labelmd > 0){
40165                         label.cls += ' col-md-' + this.labelmd;
40166                         container.cls += ' col-md-' + (12 - this.labelmd);
40167                     }
40168                     if(this.labelsm > 0){
40169                         label.cls += ' col-sm-' + this.labelsm;
40170                         container.cls += ' col-sm-' + (12 - this.labelsm);
40171                     }
40172                     if(this.labelxs > 0){
40173                         label.cls += ' col-xs-' + this.labelxs;
40174                         container.cls += ' col-xs-' + (12 - this.labelxs);
40175                     }
40176                 }
40177             }
40178             
40179             cfg.cn = [
40180                 label,
40181                 container
40182             ];
40183             
40184             var settings = this;
40185             
40186             ['xs','sm','md','lg'].map(function(size){
40187                 if (settings[size]) {
40188                     cfg.cls += ' col-' + size + '-' + settings[size];
40189                 }
40190             });
40191             
40192             this.store = new Roo.data.Store({
40193                 proxy : new Roo.data.MemoryProxy({}),
40194                 reader : new Roo.data.JsonReader({
40195                     fields : [
40196                         {
40197                             'name' : 'name',
40198                             'type' : 'string'
40199                         },
40200                         {
40201                             'name' : 'iso2',
40202                             'type' : 'string'
40203                         },
40204                         {
40205                             'name' : 'dialCode',
40206                             'type' : 'string'
40207                         },
40208                         {
40209                             'name' : 'priority',
40210                             'type' : 'string'
40211                         },
40212                         {
40213                             'name' : 'areaCodes',
40214                             'type' : 'string'
40215                         }
40216                     ]
40217                 })
40218             });
40219             
40220             if(!this.preferedCountries) {
40221                 this.preferedCountries = [
40222                     'hk',
40223                     'gb',
40224                     'us'
40225                 ];
40226             }
40227             
40228             var p = this.preferedCountries.reverse();
40229             
40230             if(p) {
40231                 for (var i = 0; i < p.length; i++) {
40232                     for (var j = 0; j < this.allCountries.length; j++) {
40233                         if(this.allCountries[j].iso2 == p[i]) {
40234                             var t = this.allCountries[j];
40235                             this.allCountries.splice(j,1);
40236                             this.allCountries.unshift(t);
40237                         }
40238                     } 
40239                 }
40240             }
40241             
40242             this.store.proxy.data = {
40243                 success: true,
40244                 data: this.allCountries
40245             };
40246             
40247             return cfg;
40248         },
40249         
40250         initEvents : function()
40251         {
40252             this.createList();
40253             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40254             
40255             this.indicator = this.indicatorEl();
40256             this.flag = this.flagEl();
40257             this.dialCodeHolder = this.dialCodeHolderEl();
40258             
40259             this.trigger = this.el.select('div.flag-box',true).first();
40260             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40261             
40262             var _this = this;
40263             
40264             (function(){
40265                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40266                 _this.list.setWidth(lw);
40267             }).defer(100);
40268             
40269             this.list.on('mouseover', this.onViewOver, this);
40270             this.list.on('mousemove', this.onViewMove, this);
40271             this.inputEl().on("keyup", this.onKeyUp, this);
40272             this.inputEl().on("keypress", this.onKeyPress, this);
40273             
40274             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40275
40276             this.view = new Roo.View(this.list, this.tpl, {
40277                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40278             });
40279             
40280             this.view.on('click', this.onViewClick, this);
40281             this.setValue(this.defaultDialCode);
40282         },
40283         
40284         onTriggerClick : function(e)
40285         {
40286             Roo.log('trigger click');
40287             if(this.disabled){
40288                 return;
40289             }
40290             
40291             if(this.isExpanded()){
40292                 this.collapse();
40293                 this.hasFocus = false;
40294             }else {
40295                 this.store.load({});
40296                 this.hasFocus = true;
40297                 this.expand();
40298             }
40299         },
40300         
40301         isExpanded : function()
40302         {
40303             return this.list.isVisible();
40304         },
40305         
40306         collapse : function()
40307         {
40308             if(!this.isExpanded()){
40309                 return;
40310             }
40311             this.list.hide();
40312             Roo.get(document).un('mousedown', this.collapseIf, this);
40313             Roo.get(document).un('mousewheel', this.collapseIf, this);
40314             this.fireEvent('collapse', this);
40315             this.validate();
40316         },
40317         
40318         expand : function()
40319         {
40320             Roo.log('expand');
40321
40322             if(this.isExpanded() || !this.hasFocus){
40323                 return;
40324             }
40325             
40326             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40327             this.list.setWidth(lw);
40328             
40329             this.list.show();
40330             this.restrictHeight();
40331             
40332             Roo.get(document).on('mousedown', this.collapseIf, this);
40333             Roo.get(document).on('mousewheel', this.collapseIf, this);
40334             
40335             this.fireEvent('expand', this);
40336         },
40337         
40338         restrictHeight : function()
40339         {
40340             this.list.alignTo(this.inputEl(), this.listAlign);
40341             this.list.alignTo(this.inputEl(), this.listAlign);
40342         },
40343         
40344         onViewOver : function(e, t)
40345         {
40346             if(this.inKeyMode){
40347                 return;
40348             }
40349             var item = this.view.findItemFromChild(t);
40350             
40351             if(item){
40352                 var index = this.view.indexOf(item);
40353                 this.select(index, false);
40354             }
40355         },
40356
40357         // private
40358         onViewClick : function(view, doFocus, el, e)
40359         {
40360             var index = this.view.getSelectedIndexes()[0];
40361             
40362             var r = this.store.getAt(index);
40363             
40364             if(r){
40365                 this.onSelect(r, index);
40366             }
40367             if(doFocus !== false && !this.blockFocus){
40368                 this.inputEl().focus();
40369             }
40370         },
40371         
40372         onViewMove : function(e, t)
40373         {
40374             this.inKeyMode = false;
40375         },
40376         
40377         select : function(index, scrollIntoView)
40378         {
40379             this.selectedIndex = index;
40380             this.view.select(index);
40381             if(scrollIntoView !== false){
40382                 var el = this.view.getNode(index);
40383                 if(el){
40384                     this.list.scrollChildIntoView(el, false);
40385                 }
40386             }
40387         },
40388         
40389         createList : function()
40390         {
40391             this.list = Roo.get(document.body).createChild({
40392                 tag: 'ul',
40393                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40394                 style: 'display:none'
40395             });
40396             
40397             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40398         },
40399         
40400         collapseIf : function(e)
40401         {
40402             var in_combo  = e.within(this.el);
40403             var in_list =  e.within(this.list);
40404             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40405             
40406             if (in_combo || in_list || is_list) {
40407                 return;
40408             }
40409             this.collapse();
40410         },
40411         
40412         onSelect : function(record, index)
40413         {
40414             if(this.fireEvent('beforeselect', this, record, index) !== false){
40415                 
40416                 this.setFlagClass(record.data.iso2);
40417                 this.setDialCode(record.data.dialCode);
40418                 this.hasFocus = false;
40419                 this.collapse();
40420                 this.fireEvent('select', this, record, index);
40421             }
40422         },
40423         
40424         flagEl : function()
40425         {
40426             var flag = this.el.select('div.flag',true).first();
40427             if(!flag){
40428                 return false;
40429             }
40430             return flag;
40431         },
40432         
40433         dialCodeHolderEl : function()
40434         {
40435             var d = this.el.select('input.dial-code-holder',true).first();
40436             if(!d){
40437                 return false;
40438             }
40439             return d;
40440         },
40441         
40442         setDialCode : function(v)
40443         {
40444             this.dialCodeHolder.dom.value = '+'+v;
40445         },
40446         
40447         setFlagClass : function(n)
40448         {
40449             this.flag.dom.className = 'flag '+n;
40450         },
40451         
40452         getValue : function()
40453         {
40454             var v = this.inputEl().getValue();
40455             if(this.dialCodeHolder) {
40456                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40457             }
40458             return v;
40459         },
40460         
40461         setValue : function(v)
40462         {
40463             var d = this.getDialCode(v);
40464             
40465             //invalid dial code
40466             if(v.length == 0 || !d || d.length == 0) {
40467                 if(this.rendered){
40468                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40469                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40470                 }
40471                 return;
40472             }
40473             
40474             //valid dial code
40475             this.setFlagClass(this.dialCodeMapping[d].iso2);
40476             this.setDialCode(d);
40477             this.inputEl().dom.value = v.replace('+'+d,'');
40478             this.hiddenEl().dom.value = this.getValue();
40479             
40480             this.validate();
40481         },
40482         
40483         getDialCode : function(v)
40484         {
40485             v = v ||  '';
40486             
40487             if (v.length == 0) {
40488                 return this.dialCodeHolder.dom.value;
40489             }
40490             
40491             var dialCode = "";
40492             if (v.charAt(0) != "+") {
40493                 return false;
40494             }
40495             var numericChars = "";
40496             for (var i = 1; i < v.length; i++) {
40497               var c = v.charAt(i);
40498               if (!isNaN(c)) {
40499                 numericChars += c;
40500                 if (this.dialCodeMapping[numericChars]) {
40501                   dialCode = v.substr(1, i);
40502                 }
40503                 if (numericChars.length == 4) {
40504                   break;
40505                 }
40506               }
40507             }
40508             return dialCode;
40509         },
40510         
40511         reset : function()
40512         {
40513             this.setValue(this.defaultDialCode);
40514             this.validate();
40515         },
40516         
40517         hiddenEl : function()
40518         {
40519             return this.el.select('input.hidden-tel-input',true).first();
40520         },
40521         
40522         // after setting val
40523         onKeyUp : function(e){
40524             this.setValue(this.getValue());
40525         },
40526         
40527         onKeyPress : function(e){
40528             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40529                 e.stopEvent();
40530             }
40531         }
40532         
40533 });
40534 /**
40535  * @class Roo.bootstrap.MoneyField
40536  * @extends Roo.bootstrap.ComboBox
40537  * Bootstrap MoneyField class
40538  * 
40539  * @constructor
40540  * Create a new MoneyField.
40541  * @param {Object} config Configuration options
40542  */
40543
40544 Roo.bootstrap.MoneyField = function(config) {
40545     
40546     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40547     
40548 };
40549
40550 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40551     
40552     /**
40553      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40554      */
40555     allowDecimals : true,
40556     /**
40557      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40558      */
40559     decimalSeparator : ".",
40560     /**
40561      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40562      */
40563     decimalPrecision : 0,
40564     /**
40565      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40566      */
40567     allowNegative : true,
40568     /**
40569      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40570      */
40571     allowZero: true,
40572     /**
40573      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40574      */
40575     minValue : Number.NEGATIVE_INFINITY,
40576     /**
40577      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40578      */
40579     maxValue : Number.MAX_VALUE,
40580     /**
40581      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40582      */
40583     minText : "The minimum value for this field is {0}",
40584     /**
40585      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40586      */
40587     maxText : "The maximum value for this field is {0}",
40588     /**
40589      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40590      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40591      */
40592     nanText : "{0} is not a valid number",
40593     /**
40594      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40595      */
40596     castInt : true,
40597     /**
40598      * @cfg {String} defaults currency of the MoneyField
40599      * value should be in lkey
40600      */
40601     defaultCurrency : false,
40602     /**
40603      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40604      */
40605     thousandsDelimiter : false,
40606     /**
40607      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40608      */
40609     max_length: false,
40610     
40611     inputlg : 9,
40612     inputmd : 9,
40613     inputsm : 9,
40614     inputxs : 6,
40615     
40616     store : false,
40617     
40618     getAutoCreate : function()
40619     {
40620         var align = this.labelAlign || this.parentLabelAlign();
40621         
40622         var id = Roo.id();
40623
40624         var cfg = {
40625             cls: 'form-group',
40626             cn: []
40627         };
40628
40629         var input =  {
40630             tag: 'input',
40631             id : id,
40632             cls : 'form-control roo-money-amount-input',
40633             autocomplete: 'new-password'
40634         };
40635         
40636         var hiddenInput = {
40637             tag: 'input',
40638             type: 'hidden',
40639             id: Roo.id(),
40640             cls: 'hidden-number-input'
40641         };
40642         
40643         if(this.max_length) {
40644             input.maxlength = this.max_length; 
40645         }
40646         
40647         if (this.name) {
40648             hiddenInput.name = this.name;
40649         }
40650
40651         if (this.disabled) {
40652             input.disabled = true;
40653         }
40654
40655         var clg = 12 - this.inputlg;
40656         var cmd = 12 - this.inputmd;
40657         var csm = 12 - this.inputsm;
40658         var cxs = 12 - this.inputxs;
40659         
40660         var container = {
40661             tag : 'div',
40662             cls : 'row roo-money-field',
40663             cn : [
40664                 {
40665                     tag : 'div',
40666                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40667                     cn : [
40668                         {
40669                             tag : 'div',
40670                             cls: 'roo-select2-container input-group',
40671                             cn: [
40672                                 {
40673                                     tag : 'input',
40674                                     cls : 'form-control roo-money-currency-input',
40675                                     autocomplete: 'new-password',
40676                                     readOnly : 1,
40677                                     name : this.currencyName
40678                                 },
40679                                 {
40680                                     tag :'span',
40681                                     cls : 'input-group-addon',
40682                                     cn : [
40683                                         {
40684                                             tag: 'span',
40685                                             cls: 'caret'
40686                                         }
40687                                     ]
40688                                 }
40689                             ]
40690                         }
40691                     ]
40692                 },
40693                 {
40694                     tag : 'div',
40695                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40696                     cn : [
40697                         {
40698                             tag: 'div',
40699                             cls: this.hasFeedback ? 'has-feedback' : '',
40700                             cn: [
40701                                 input
40702                             ]
40703                         }
40704                     ]
40705                 }
40706             ]
40707             
40708         };
40709         
40710         if (this.fieldLabel.length) {
40711             var indicator = {
40712                 tag: 'i',
40713                 tooltip: 'This field is required'
40714             };
40715
40716             var label = {
40717                 tag: 'label',
40718                 'for':  id,
40719                 cls: 'control-label',
40720                 cn: []
40721             };
40722
40723             var label_text = {
40724                 tag: 'span',
40725                 html: this.fieldLabel
40726             };
40727
40728             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40729             label.cn = [
40730                 indicator,
40731                 label_text
40732             ];
40733
40734             if(this.indicatorpos == 'right') {
40735                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40736                 label.cn = [
40737                     label_text,
40738                     indicator
40739                 ];
40740             }
40741
40742             if(align == 'left') {
40743                 container = {
40744                     tag: 'div',
40745                     cn: [
40746                         container
40747                     ]
40748                 };
40749
40750                 if(this.labelWidth > 12){
40751                     label.style = "width: " + this.labelWidth + 'px';
40752                 }
40753                 if(this.labelWidth < 13 && this.labelmd == 0){
40754                     this.labelmd = this.labelWidth;
40755                 }
40756                 if(this.labellg > 0){
40757                     label.cls += ' col-lg-' + this.labellg;
40758                     input.cls += ' col-lg-' + (12 - this.labellg);
40759                 }
40760                 if(this.labelmd > 0){
40761                     label.cls += ' col-md-' + this.labelmd;
40762                     container.cls += ' col-md-' + (12 - this.labelmd);
40763                 }
40764                 if(this.labelsm > 0){
40765                     label.cls += ' col-sm-' + this.labelsm;
40766                     container.cls += ' col-sm-' + (12 - this.labelsm);
40767                 }
40768                 if(this.labelxs > 0){
40769                     label.cls += ' col-xs-' + this.labelxs;
40770                     container.cls += ' col-xs-' + (12 - this.labelxs);
40771                 }
40772             }
40773         }
40774
40775         cfg.cn = [
40776             label,
40777             container,
40778             hiddenInput
40779         ];
40780         
40781         var settings = this;
40782
40783         ['xs','sm','md','lg'].map(function(size){
40784             if (settings[size]) {
40785                 cfg.cls += ' col-' + size + '-' + settings[size];
40786             }
40787         });
40788         
40789         return cfg;
40790     },
40791     
40792     initEvents : function()
40793     {
40794         this.indicator = this.indicatorEl();
40795         
40796         this.initCurrencyEvent();
40797         
40798         this.initNumberEvent();
40799     },
40800     
40801     initCurrencyEvent : function()
40802     {
40803         if (!this.store) {
40804             throw "can not find store for combo";
40805         }
40806         
40807         this.store = Roo.factory(this.store, Roo.data);
40808         this.store.parent = this;
40809         
40810         this.createList();
40811         
40812         this.triggerEl = this.el.select('.input-group-addon', true).first();
40813         
40814         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40815         
40816         var _this = this;
40817         
40818         (function(){
40819             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40820             _this.list.setWidth(lw);
40821         }).defer(100);
40822         
40823         this.list.on('mouseover', this.onViewOver, this);
40824         this.list.on('mousemove', this.onViewMove, this);
40825         this.list.on('scroll', this.onViewScroll, this);
40826         
40827         if(!this.tpl){
40828             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40829         }
40830         
40831         this.view = new Roo.View(this.list, this.tpl, {
40832             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40833         });
40834         
40835         this.view.on('click', this.onViewClick, this);
40836         
40837         this.store.on('beforeload', this.onBeforeLoad, this);
40838         this.store.on('load', this.onLoad, this);
40839         this.store.on('loadexception', this.onLoadException, this);
40840         
40841         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40842             "up" : function(e){
40843                 this.inKeyMode = true;
40844                 this.selectPrev();
40845             },
40846
40847             "down" : function(e){
40848                 if(!this.isExpanded()){
40849                     this.onTriggerClick();
40850                 }else{
40851                     this.inKeyMode = true;
40852                     this.selectNext();
40853                 }
40854             },
40855
40856             "enter" : function(e){
40857                 this.collapse();
40858                 
40859                 if(this.fireEvent("specialkey", this, e)){
40860                     this.onViewClick(false);
40861                 }
40862                 
40863                 return true;
40864             },
40865
40866             "esc" : function(e){
40867                 this.collapse();
40868             },
40869
40870             "tab" : function(e){
40871                 this.collapse();
40872                 
40873                 if(this.fireEvent("specialkey", this, e)){
40874                     this.onViewClick(false);
40875                 }
40876                 
40877                 return true;
40878             },
40879
40880             scope : this,
40881
40882             doRelay : function(foo, bar, hname){
40883                 if(hname == 'down' || this.scope.isExpanded()){
40884                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40885                 }
40886                 return true;
40887             },
40888
40889             forceKeyDown: true
40890         });
40891         
40892         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40893         
40894     },
40895     
40896     initNumberEvent : function(e)
40897     {
40898         this.inputEl().on("keydown" , this.fireKey,  this);
40899         this.inputEl().on("focus", this.onFocus,  this);
40900         this.inputEl().on("blur", this.onBlur,  this);
40901         
40902         this.inputEl().relayEvent('keyup', this);
40903         
40904         if(this.indicator){
40905             this.indicator.addClass('invisible');
40906         }
40907  
40908         this.originalValue = this.getValue();
40909         
40910         if(this.validationEvent == 'keyup'){
40911             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40912             this.inputEl().on('keyup', this.filterValidation, this);
40913         }
40914         else if(this.validationEvent !== false){
40915             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40916         }
40917         
40918         if(this.selectOnFocus){
40919             this.on("focus", this.preFocus, this);
40920             
40921         }
40922         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40923             this.inputEl().on("keypress", this.filterKeys, this);
40924         } else {
40925             this.inputEl().relayEvent('keypress', this);
40926         }
40927         
40928         var allowed = "0123456789";
40929         
40930         if(this.allowDecimals){
40931             allowed += this.decimalSeparator;
40932         }
40933         
40934         if(this.allowNegative){
40935             allowed += "-";
40936         }
40937         
40938         if(this.thousandsDelimiter) {
40939             allowed += ",";
40940         }
40941         
40942         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40943         
40944         var keyPress = function(e){
40945             
40946             var k = e.getKey();
40947             
40948             var c = e.getCharCode();
40949             
40950             if(
40951                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40952                     allowed.indexOf(String.fromCharCode(c)) === -1
40953             ){
40954                 e.stopEvent();
40955                 return;
40956             }
40957             
40958             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40959                 return;
40960             }
40961             
40962             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40963                 e.stopEvent();
40964             }
40965         };
40966         
40967         this.inputEl().on("keypress", keyPress, this);
40968         
40969     },
40970     
40971     onTriggerClick : function(e)
40972     {   
40973         if(this.disabled){
40974             return;
40975         }
40976         
40977         this.page = 0;
40978         this.loadNext = false;
40979         
40980         if(this.isExpanded()){
40981             this.collapse();
40982             return;
40983         }
40984         
40985         this.hasFocus = true;
40986         
40987         if(this.triggerAction == 'all') {
40988             this.doQuery(this.allQuery, true);
40989             return;
40990         }
40991         
40992         this.doQuery(this.getRawValue());
40993     },
40994     
40995     getCurrency : function()
40996     {   
40997         var v = this.currencyEl().getValue();
40998         
40999         return v;
41000     },
41001     
41002     restrictHeight : function()
41003     {
41004         this.list.alignTo(this.currencyEl(), this.listAlign);
41005         this.list.alignTo(this.currencyEl(), this.listAlign);
41006     },
41007     
41008     onViewClick : function(view, doFocus, el, e)
41009     {
41010         var index = this.view.getSelectedIndexes()[0];
41011         
41012         var r = this.store.getAt(index);
41013         
41014         if(r){
41015             this.onSelect(r, index);
41016         }
41017     },
41018     
41019     onSelect : function(record, index){
41020         
41021         if(this.fireEvent('beforeselect', this, record, index) !== false){
41022         
41023             this.setFromCurrencyData(index > -1 ? record.data : false);
41024             
41025             this.collapse();
41026             
41027             this.fireEvent('select', this, record, index);
41028         }
41029     },
41030     
41031     setFromCurrencyData : function(o)
41032     {
41033         var currency = '';
41034         
41035         this.lastCurrency = o;
41036         
41037         if (this.currencyField) {
41038             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41039         } else {
41040             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41041         }
41042         
41043         this.lastSelectionText = currency;
41044         
41045         //setting default currency
41046         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41047             this.setCurrency(this.defaultCurrency);
41048             return;
41049         }
41050         
41051         this.setCurrency(currency);
41052     },
41053     
41054     setFromData : function(o)
41055     {
41056         var c = {};
41057         
41058         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41059         
41060         this.setFromCurrencyData(c);
41061         
41062         var value = '';
41063         
41064         if (this.name) {
41065             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41066         } else {
41067             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41068         }
41069         
41070         this.setValue(value);
41071         
41072     },
41073     
41074     setCurrency : function(v)
41075     {   
41076         this.currencyValue = v;
41077         
41078         if(this.rendered){
41079             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41080             this.validate();
41081         }
41082     },
41083     
41084     setValue : function(v)
41085     {
41086         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41087         
41088         this.value = v;
41089         
41090         if(this.rendered){
41091             
41092             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41093             
41094             this.inputEl().dom.value = (v == '') ? '' :
41095                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41096             
41097             if(!this.allowZero && v === '0') {
41098                 this.hiddenEl().dom.value = '';
41099                 this.inputEl().dom.value = '';
41100             }
41101             
41102             this.validate();
41103         }
41104     },
41105     
41106     getRawValue : function()
41107     {
41108         var v = this.inputEl().getValue();
41109         
41110         return v;
41111     },
41112     
41113     getValue : function()
41114     {
41115         return this.fixPrecision(this.parseValue(this.getRawValue()));
41116     },
41117     
41118     parseValue : function(value)
41119     {
41120         if(this.thousandsDelimiter) {
41121             value += "";
41122             r = new RegExp(",", "g");
41123             value = value.replace(r, "");
41124         }
41125         
41126         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41127         return isNaN(value) ? '' : value;
41128         
41129     },
41130     
41131     fixPrecision : function(value)
41132     {
41133         if(this.thousandsDelimiter) {
41134             value += "";
41135             r = new RegExp(",", "g");
41136             value = value.replace(r, "");
41137         }
41138         
41139         var nan = isNaN(value);
41140         
41141         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41142             return nan ? '' : value;
41143         }
41144         return parseFloat(value).toFixed(this.decimalPrecision);
41145     },
41146     
41147     decimalPrecisionFcn : function(v)
41148     {
41149         return Math.floor(v);
41150     },
41151     
41152     validateValue : function(value)
41153     {
41154         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41155             return false;
41156         }
41157         
41158         var num = this.parseValue(value);
41159         
41160         if(isNaN(num)){
41161             this.markInvalid(String.format(this.nanText, value));
41162             return false;
41163         }
41164         
41165         if(num < this.minValue){
41166             this.markInvalid(String.format(this.minText, this.minValue));
41167             return false;
41168         }
41169         
41170         if(num > this.maxValue){
41171             this.markInvalid(String.format(this.maxText, this.maxValue));
41172             return false;
41173         }
41174         
41175         return true;
41176     },
41177     
41178     validate : function()
41179     {
41180         if(this.disabled || this.allowBlank){
41181             this.markValid();
41182             return true;
41183         }
41184         
41185         var currency = this.getCurrency();
41186         
41187         if(this.validateValue(this.getRawValue()) && currency.length){
41188             this.markValid();
41189             return true;
41190         }
41191         
41192         this.markInvalid();
41193         return false;
41194     },
41195     
41196     getName: function()
41197     {
41198         return this.name;
41199     },
41200     
41201     beforeBlur : function()
41202     {
41203         if(!this.castInt){
41204             return;
41205         }
41206         
41207         var v = this.parseValue(this.getRawValue());
41208         
41209         if(v || v == 0){
41210             this.setValue(v);
41211         }
41212     },
41213     
41214     onBlur : function()
41215     {
41216         this.beforeBlur();
41217         
41218         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41219             //this.el.removeClass(this.focusClass);
41220         }
41221         
41222         this.hasFocus = false;
41223         
41224         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41225             this.validate();
41226         }
41227         
41228         var v = this.getValue();
41229         
41230         if(String(v) !== String(this.startValue)){
41231             this.fireEvent('change', this, v, this.startValue);
41232         }
41233         
41234         this.fireEvent("blur", this);
41235     },
41236     
41237     inputEl : function()
41238     {
41239         return this.el.select('.roo-money-amount-input', true).first();
41240     },
41241     
41242     currencyEl : function()
41243     {
41244         return this.el.select('.roo-money-currency-input', true).first();
41245     },
41246     
41247     hiddenEl : function()
41248     {
41249         return this.el.select('input.hidden-number-input',true).first();
41250     }
41251     
41252 });