83849e1738cb745819000ac490694638951590a2
[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, function(s) {
10                     if ( s.href  && 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','d-none']);
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','d-none']);
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * element
442  * 
443  */
444
445 /**
446  * @class Roo.bootstrap.Element
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Element class
449  * @cfg {String} html contents of the element
450  * @cfg {String} tag tag of the element
451  * @cfg {String} cls class of the element
452  * @cfg {Boolean} preventDefault (true|false) default false
453  * @cfg {Boolean} clickable (true|false) default false
454  * 
455  * @constructor
456  * Create a new Element
457  * @param {Object} config The config object
458  */
459
460 Roo.bootstrap.Element = function(config){
461     Roo.bootstrap.Element.superclass.constructor.call(this, config);
462     
463     this.addEvents({
464         // raw events
465         /**
466          * @event click
467          * When a element is chick
468          * @param {Roo.bootstrap.Element} this
469          * @param {Roo.EventObject} e
470          */
471         "click" : true
472     });
473 };
474
475 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
476     
477     tag: 'div',
478     cls: '',
479     html: '',
480     preventDefault: false, 
481     clickable: false,
482     
483     getAutoCreate : function(){
484         
485         var cfg = {
486             tag: this.tag,
487             // cls: this.cls, double assign in parent class Component.js :: onRender
488             html: this.html
489         };
490         
491         return cfg;
492     },
493     
494     initEvents: function() 
495     {
496         Roo.bootstrap.Element.superclass.initEvents.call(this);
497         
498         if(this.clickable){
499             this.el.on('click', this.onClick, this);
500         }
501         
502     },
503     
504     onClick : function(e)
505     {
506         if(this.preventDefault){
507             e.preventDefault();
508         }
509         
510         this.fireEvent('click', this, e);
511     },
512     
513     getValue : function()
514     {
515         return this.el.dom.innerHTML;
516     },
517     
518     setValue : function(value)
519     {
520         this.el.dom.innerHTML = value;
521     }
522    
523 });
524
525  
526
527  /*
528  * - LGPL
529  *
530  * Body
531  *
532  */
533
534 /**
535  * @class Roo.bootstrap.Body
536  * @extends Roo.bootstrap.Component
537  * Bootstrap Body class
538  *
539  * @constructor
540  * Create a new body
541  * @param {Object} config The config object
542  */
543
544 Roo.bootstrap.Body = function(config){
545
546     config = config || {};
547
548     Roo.bootstrap.Body.superclass.constructor.call(this, config);
549     this.el = Roo.get(config.el ? config.el : document.body );
550     if (this.cls && this.cls.length) {
551         Roo.get(document.body).addClass(this.cls);
552     }
553 };
554
555 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
556
557     is_body : true,// just to make sure it's constructed?
558
559         autoCreate : {
560         cls: 'container'
561     },
562     onRender : function(ct, position)
563     {
564        /* Roo.log("Roo.bootstrap.Body - onRender");
565         if (this.cls && this.cls.length) {
566             Roo.get(document.body).addClass(this.cls);
567         }
568         // style??? xttr???
569         */
570     }
571
572
573
574
575 });
576 /*
577  * - LGPL
578  *
579  * button group
580  * 
581  */
582
583
584 /**
585  * @class Roo.bootstrap.ButtonGroup
586  * @extends Roo.bootstrap.Component
587  * Bootstrap ButtonGroup class
588  * @cfg {String} size lg | sm | xs (default empty normal)
589  * @cfg {String} align vertical | justified  (default none)
590  * @cfg {String} direction up | down (default down)
591  * @cfg {Boolean} toolbar false | true
592  * @cfg {Boolean} btn true | false
593  * 
594  * 
595  * @constructor
596  * Create a new Input
597  * @param {Object} config The config object
598  */
599
600 Roo.bootstrap.ButtonGroup = function(config){
601     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
602 };
603
604 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
605     
606     size: '',
607     align: '',
608     direction: '',
609     toolbar: false,
610     btn: true,
611
612     getAutoCreate : function(){
613         var cfg = {
614             cls: 'btn-group',
615             html : null
616         };
617         
618         cfg.html = this.html || cfg.html;
619         
620         if (this.toolbar) {
621             cfg = {
622                 cls: 'btn-toolbar',
623                 html: null
624             };
625             
626             return cfg;
627         }
628         
629         if (['vertical','justified'].indexOf(this.align)!==-1) {
630             cfg.cls = 'btn-group-' + this.align;
631             
632             if (this.align == 'justified') {
633                 console.log(this.items);
634             }
635         }
636         
637         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
638             cfg.cls += ' btn-group-' + this.size;
639         }
640         
641         if (this.direction == 'up') {
642             cfg.cls += ' dropup' ;
643         }
644         
645         return cfg;
646     },
647     /**
648      * Add a button to the group (similar to NavItem API.)
649      */
650     addItem : function(cfg)
651     {
652         var cn = new Roo.bootstrap.Button(cfg);
653         //this.register(cn);
654         cn.parentId = this.id;
655         cn.onRender(this.el, null);
656         return cn;
657     }
658    
659 });
660
661  /*
662  * - LGPL
663  *
664  * button
665  * 
666  */
667
668 /**
669  * @class Roo.bootstrap.Button
670  * @extends Roo.bootstrap.Component
671  * Bootstrap Button class
672  * @cfg {String} html The button content
673  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
674  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
675  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
676  * @cfg {String} size ( lg | sm | xs)
677  * @cfg {String} tag ( a | input | submit)
678  * @cfg {String} href empty or href
679  * @cfg {Boolean} disabled default false;
680  * @cfg {Boolean} isClose default false;
681  * @cfg {String} glyphicon depricated - use fa
682  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
683  * @cfg {String} badge text for badge
684  * @cfg {String} theme (default|glow)  
685  * @cfg {Boolean} inverse dark themed version
686  * @cfg {Boolean} toggle is it a slidy toggle button
687  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
688  * @cfg {String} ontext text for on slidy toggle state
689  * @cfg {String} offtext text for off slidy toggle state
690  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
691  * @cfg {Boolean} removeClass remove the standard class..
692  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
693  * 
694  * @constructor
695  * Create a new button
696  * @param {Object} config The config object
697  */
698
699
700 Roo.bootstrap.Button = function(config){
701     Roo.bootstrap.Button.superclass.constructor.call(this, config);
702     this.weightClass = ["btn-default btn-outline-secondary", 
703                        "btn-primary", 
704                        "btn-success", 
705                        "btn-info", 
706                        "btn-warning",
707                        "btn-danger",
708                        "btn-link"
709                       ],  
710     this.addEvents({
711         // raw events
712         /**
713          * @event click
714          * When a butotn is pressed
715          * @param {Roo.bootstrap.Button} btn
716          * @param {Roo.EventObject} e
717          */
718         "click" : true,
719          /**
720          * @event toggle
721          * After the button has been toggles
722          * @param {Roo.bootstrap.Button} btn
723          * @param {Roo.EventObject} e
724          * @param {boolean} pressed (also available as button.pressed)
725          */
726         "toggle" : true
727     });
728 };
729
730 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
731     html: false,
732     active: false,
733     weight: '',
734     badge_weight: '',
735     outline : false,
736     size: '',
737     tag: 'button',
738     href: '',
739     disabled: false,
740     isClose: false,
741     glyphicon: '',
742     fa: '',
743     badge: '',
744     theme: 'default',
745     inverse: false,
746     
747     toggle: false,
748     ontext: 'ON',
749     offtext: 'OFF',
750     defaulton: true,
751     preventDefault: true,
752     removeClass: false,
753     name: false,
754     target: false,
755      
756     pressed : null,
757      
758     
759     getAutoCreate : function(){
760         
761         var cfg = {
762             tag : 'button',
763             cls : 'roo-button',
764             html: ''
765         };
766         
767         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
768             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
769             this.tag = 'button';
770         } else {
771             cfg.tag = this.tag;
772         }
773         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
774         
775         if (this.toggle == true) {
776             cfg={
777                 tag: 'div',
778                 cls: 'slider-frame roo-button',
779                 cn: [
780                     {
781                         tag: 'span',
782                         'data-on-text':'ON',
783                         'data-off-text':'OFF',
784                         cls: 'slider-button',
785                         html: this.offtext
786                     }
787                 ]
788             };
789             
790             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
791                 cfg.cls += ' '+this.weight;
792             }
793             
794             return cfg;
795         }
796         
797         if (this.isClose) {
798             cfg.cls += ' close';
799             
800             cfg["aria-hidden"] = true;
801             
802             cfg.html = "&times;";
803             
804             return cfg;
805         }
806         
807          
808         if (this.theme==='default') {
809             cfg.cls = 'btn roo-button';
810             
811             //if (this.parentType != 'Navbar') {
812             this.weight = this.weight.length ?  this.weight : 'default';
813             //}
814             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
815                 
816                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
817                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
818                 cfg.cls += ' btn-' + outline + weight;
819                 if (this.weight == 'default') {
820                     // BC
821                     cfg.cls += ' btn-' + this.weight;
822                 }
823             }
824         } else if (this.theme==='glow') {
825             
826             cfg.tag = 'a';
827             cfg.cls = 'btn-glow roo-button';
828             
829             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
830                 
831                 cfg.cls += ' ' + this.weight;
832             }
833         }
834    
835         
836         if (this.inverse) {
837             this.cls += ' inverse';
838         }
839         
840         
841         if (this.active || this.pressed === true) {
842             cfg.cls += ' active';
843         }
844         
845         if (this.disabled) {
846             cfg.disabled = 'disabled';
847         }
848         
849         if (this.items) {
850             Roo.log('changing to ul' );
851             cfg.tag = 'ul';
852             this.glyphicon = 'caret';
853             if (Roo.bootstrap.version == 4) {
854                 this.fa = 'caret-down';
855             }
856             
857         }
858         
859         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
860          
861         //gsRoo.log(this.parentType);
862         if (this.parentType === 'Navbar' && !this.parent().bar) {
863             Roo.log('changing to li?');
864             
865             cfg.tag = 'li';
866             
867             cfg.cls = '';
868             cfg.cn =  [{
869                 tag : 'a',
870                 cls : 'roo-button',
871                 html : this.html,
872                 href : this.href || '#'
873             }];
874             if (this.menu) {
875                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
876                 cfg.cls += ' dropdown';
877             }   
878             
879             delete cfg.html;
880             
881         }
882         
883        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
884         
885         if (this.glyphicon) {
886             cfg.html = ' ' + cfg.html;
887             
888             cfg.cn = [
889                 {
890                     tag: 'span',
891                     cls: 'glyphicon glyphicon-' + this.glyphicon
892                 }
893             ];
894         }
895         if (this.fa) {
896             cfg.html = ' ' + cfg.html;
897             
898             cfg.cn = [
899                 {
900                     tag: 'i',
901                     cls: 'fa fas fa-' + this.fa
902                 }
903             ];
904         }
905         
906         if (this.badge) {
907             cfg.html += ' ';
908             
909             cfg.tag = 'a';
910             
911 //            cfg.cls='btn roo-button';
912             
913             cfg.href=this.href;
914             
915             var value = cfg.html;
916             
917             if(this.glyphicon){
918                 value = {
919                     tag: 'span',
920                     cls: 'glyphicon glyphicon-' + this.glyphicon,
921                     html: this.html
922                 };
923             }
924             if(this.fa){
925                 value = {
926                     tag: 'i',
927                     cls: 'fa fas fa-' + this.fa,
928                     html: this.html
929                 };
930             }
931             
932             var bw = this.badge_weight.length ? this.badge_weight :
933                 (this.weight.length ? this.weight : 'secondary');
934             bw = bw == 'default' ? 'secondary' : bw;
935             
936             cfg.cn = [
937                 value,
938                 {
939                     tag: 'span',
940                     cls: 'badge badge-' + bw,
941                     html: this.badge
942                 }
943             ];
944             
945             cfg.html='';
946         }
947         
948         if (this.menu) {
949             cfg.cls += ' dropdown';
950             cfg.html = typeof(cfg.html) != 'undefined' ?
951                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
952         }
953         
954         if (cfg.tag !== 'a' && this.href !== '') {
955             throw "Tag must be a to set href.";
956         } else if (this.href.length > 0) {
957             cfg.href = this.href;
958         }
959         
960         if(this.removeClass){
961             cfg.cls = '';
962         }
963         
964         if(this.target){
965             cfg.target = this.target;
966         }
967         
968         return cfg;
969     },
970     initEvents: function() {
971        // Roo.log('init events?');
972 //        Roo.log(this.el.dom);
973         // add the menu...
974         
975         if (typeof (this.menu) != 'undefined') {
976             this.menu.parentType = this.xtype;
977             this.menu.triggerEl = this.el;
978             this.addxtype(Roo.apply({}, this.menu));
979         }
980
981
982        if (this.el.hasClass('roo-button')) {
983             this.el.on('click', this.onClick, this);
984        } else {
985             this.el.select('.roo-button').on('click', this.onClick, this);
986        }
987        
988        if(this.removeClass){
989            this.el.on('click', this.onClick, this);
990        }
991        
992        this.el.enableDisplayMode();
993         
994     },
995     onClick : function(e)
996     {
997         if (this.disabled) {
998             return;
999         }
1000         
1001         Roo.log('button on click ');
1002         if(this.preventDefault){
1003             e.preventDefault();
1004         }
1005         
1006         if (this.pressed === true || this.pressed === false) {
1007             this.toggleActive(e);
1008         }
1009         
1010         
1011         this.fireEvent('click', this, e);
1012     },
1013     
1014     /**
1015      * Enables this button
1016      */
1017     enable : function()
1018     {
1019         this.disabled = false;
1020         this.el.removeClass('disabled');
1021     },
1022     
1023     /**
1024      * Disable this button
1025      */
1026     disable : function()
1027     {
1028         this.disabled = true;
1029         this.el.addClass('disabled');
1030     },
1031      /**
1032      * sets the active state on/off, 
1033      * @param {Boolean} state (optional) Force a particular state
1034      */
1035     setActive : function(v) {
1036         
1037         this.el[v ? 'addClass' : 'removeClass']('active');
1038         this.pressed = v;
1039     },
1040      /**
1041      * toggles the current active state 
1042      */
1043     toggleActive : function(e)
1044     {
1045         this.setActive(!this.pressed);
1046         this.fireEvent('toggle', this, e, !this.pressed);
1047     },
1048      /**
1049      * get the current active state
1050      * @return {boolean} true if it's active
1051      */
1052     isActive : function()
1053     {
1054         return this.el.hasClass('active');
1055     },
1056     /**
1057      * set the text of the first selected button
1058      */
1059     setText : function(str)
1060     {
1061         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1062     },
1063     /**
1064      * get the text of the first selected button
1065      */
1066     getText : function()
1067     {
1068         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1069     },
1070     
1071     setWeight : function(str)
1072     {
1073         this.el.removeClass(this.weightClass);
1074         this.weight = str;
1075         var outline = this.outline ? 'outline-' : '';
1076         if (str == 'default') {
1077             this.el.addClass('btn-default btn-outline-secondary');        
1078             return;
1079         }
1080         this.el.addClass('btn-' + outline + str);        
1081     }
1082     
1083     
1084 });
1085
1086  /*
1087  * - LGPL
1088  *
1089  * column
1090  * 
1091  */
1092
1093 /**
1094  * @class Roo.bootstrap.Column
1095  * @extends Roo.bootstrap.Component
1096  * Bootstrap Column class
1097  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1098  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1099  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1100  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1101  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1102  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1103  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1104  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1105  *
1106  * 
1107  * @cfg {Boolean} hidden (true|false) hide the element
1108  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1109  * @cfg {String} fa (ban|check|...) font awesome icon
1110  * @cfg {Number} fasize (1|2|....) font awsome size
1111
1112  * @cfg {String} icon (info-sign|check|...) glyphicon name
1113
1114  * @cfg {String} html content of column.
1115  * 
1116  * @constructor
1117  * Create a new Column
1118  * @param {Object} config The config object
1119  */
1120
1121 Roo.bootstrap.Column = function(config){
1122     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1123 };
1124
1125 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1126     
1127     xs: false,
1128     sm: false,
1129     md: false,
1130     lg: false,
1131     xsoff: false,
1132     smoff: false,
1133     mdoff: false,
1134     lgoff: false,
1135     html: '',
1136     offset: 0,
1137     alert: false,
1138     fa: false,
1139     icon : false,
1140     hidden : false,
1141     fasize : 1,
1142     
1143     getAutoCreate : function(){
1144         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1145         
1146         cfg = {
1147             tag: 'div',
1148             cls: 'column'
1149         };
1150         
1151         var settings=this;
1152         var sizes =   ['xs','sm','md','lg'];
1153         sizes.map(function(size ,ix){
1154             //Roo.log( size + ':' + settings[size]);
1155             
1156             if (settings[size+'off'] !== false) {
1157                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1158             }
1159             
1160             if (settings[size] === false) {
1161                 return;
1162             }
1163             
1164             if (!settings[size]) { // 0 = hidden
1165                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1166                 // bootsrap4
1167                 for (var i = ix; i > -1; i--) {
1168                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1169                 }
1170                 
1171                 
1172                 return;
1173             }
1174             cfg.cls += ' col-' + size + '-' + settings[size] + (
1175                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1176             );
1177             
1178         });
1179         
1180         if (this.hidden) {
1181             cfg.cls += ' hidden';
1182         }
1183         
1184         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1185             cfg.cls +=' alert alert-' + this.alert;
1186         }
1187         
1188         
1189         if (this.html.length) {
1190             cfg.html = this.html;
1191         }
1192         if (this.fa) {
1193             var fasize = '';
1194             if (this.fasize > 1) {
1195                 fasize = ' fa-' + this.fasize + 'x';
1196             }
1197             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1198             
1199             
1200         }
1201         if (this.icon) {
1202             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1203         }
1204         
1205         return cfg;
1206     }
1207    
1208 });
1209
1210  
1211
1212  /*
1213  * - LGPL
1214  *
1215  * page container.
1216  * 
1217  */
1218
1219
1220 /**
1221  * @class Roo.bootstrap.Container
1222  * @extends Roo.bootstrap.Component
1223  * Bootstrap Container class
1224  * @cfg {Boolean} jumbotron is it a jumbotron element
1225  * @cfg {String} html content of element
1226  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1227  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1228  * @cfg {String} header content of header (for panel)
1229  * @cfg {String} footer content of footer (for panel)
1230  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1231  * @cfg {String} tag (header|aside|section) type of HTML tag.
1232  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1233  * @cfg {String} fa font awesome icon
1234  * @cfg {String} icon (info-sign|check|...) glyphicon name
1235  * @cfg {Boolean} hidden (true|false) hide the element
1236  * @cfg {Boolean} expandable (true|false) default false
1237  * @cfg {Boolean} expanded (true|false) default true
1238  * @cfg {String} rheader contet on the right of header
1239  * @cfg {Boolean} clickable (true|false) default false
1240
1241  *     
1242  * @constructor
1243  * Create a new Container
1244  * @param {Object} config The config object
1245  */
1246
1247 Roo.bootstrap.Container = function(config){
1248     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1249     
1250     this.addEvents({
1251         // raw events
1252          /**
1253          * @event expand
1254          * After the panel has been expand
1255          * 
1256          * @param {Roo.bootstrap.Container} this
1257          */
1258         "expand" : true,
1259         /**
1260          * @event collapse
1261          * After the panel has been collapsed
1262          * 
1263          * @param {Roo.bootstrap.Container} this
1264          */
1265         "collapse" : true,
1266         /**
1267          * @event click
1268          * When a element is chick
1269          * @param {Roo.bootstrap.Container} this
1270          * @param {Roo.EventObject} e
1271          */
1272         "click" : true
1273     });
1274 };
1275
1276 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1277     
1278     jumbotron : false,
1279     well: '',
1280     panel : '',
1281     header: '',
1282     footer : '',
1283     sticky: '',
1284     tag : false,
1285     alert : false,
1286     fa: false,
1287     icon : false,
1288     expandable : false,
1289     rheader : '',
1290     expanded : true,
1291     clickable: false,
1292   
1293      
1294     getChildContainer : function() {
1295         
1296         if(!this.el){
1297             return false;
1298         }
1299         
1300         if (this.panel.length) {
1301             return this.el.select('.panel-body',true).first();
1302         }
1303         
1304         return this.el;
1305     },
1306     
1307     
1308     getAutoCreate : function(){
1309         
1310         var cfg = {
1311             tag : this.tag || 'div',
1312             html : '',
1313             cls : ''
1314         };
1315         if (this.jumbotron) {
1316             cfg.cls = 'jumbotron';
1317         }
1318         
1319         
1320         
1321         // - this is applied by the parent..
1322         //if (this.cls) {
1323         //    cfg.cls = this.cls + '';
1324         //}
1325         
1326         if (this.sticky.length) {
1327             
1328             var bd = Roo.get(document.body);
1329             if (!bd.hasClass('bootstrap-sticky')) {
1330                 bd.addClass('bootstrap-sticky');
1331                 Roo.select('html',true).setStyle('height', '100%');
1332             }
1333              
1334             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1335         }
1336         
1337         
1338         if (this.well.length) {
1339             switch (this.well) {
1340                 case 'lg':
1341                 case 'sm':
1342                     cfg.cls +=' well well-' +this.well;
1343                     break;
1344                 default:
1345                     cfg.cls +=' well';
1346                     break;
1347             }
1348         }
1349         
1350         if (this.hidden) {
1351             cfg.cls += ' hidden';
1352         }
1353         
1354         
1355         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1356             cfg.cls +=' alert alert-' + this.alert;
1357         }
1358         
1359         var body = cfg;
1360         
1361         if (this.panel.length) {
1362             cfg.cls += ' panel panel-' + this.panel;
1363             cfg.cn = [];
1364             if (this.header.length) {
1365                 
1366                 var h = [];
1367                 
1368                 if(this.expandable){
1369                     
1370                     cfg.cls = cfg.cls + ' expandable';
1371                     
1372                     h.push({
1373                         tag: 'i',
1374                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1375                     });
1376                     
1377                 }
1378                 
1379                 h.push(
1380                     {
1381                         tag: 'span',
1382                         cls : 'panel-title',
1383                         html : (this.expandable ? '&nbsp;' : '') + this.header
1384                     },
1385                     {
1386                         tag: 'span',
1387                         cls: 'panel-header-right',
1388                         html: this.rheader
1389                     }
1390                 );
1391                 
1392                 cfg.cn.push({
1393                     cls : 'panel-heading',
1394                     style : this.expandable ? 'cursor: pointer' : '',
1395                     cn : h
1396                 });
1397                 
1398             }
1399             
1400             body = false;
1401             cfg.cn.push({
1402                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1403                 html : this.html
1404             });
1405             
1406             
1407             if (this.footer.length) {
1408                 cfg.cn.push({
1409                     cls : 'panel-footer',
1410                     html : this.footer
1411                     
1412                 });
1413             }
1414             
1415         }
1416         
1417         if (body) {
1418             body.html = this.html || cfg.html;
1419             // prefix with the icons..
1420             if (this.fa) {
1421                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1422             }
1423             if (this.icon) {
1424                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1425             }
1426             
1427             
1428         }
1429         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1430             cfg.cls =  'container';
1431         }
1432         
1433         return cfg;
1434     },
1435     
1436     initEvents: function() 
1437     {
1438         if(this.expandable){
1439             var headerEl = this.headerEl();
1440         
1441             if(headerEl){
1442                 headerEl.on('click', this.onToggleClick, this);
1443             }
1444         }
1445         
1446         if(this.clickable){
1447             this.el.on('click', this.onClick, this);
1448         }
1449         
1450     },
1451     
1452     onToggleClick : function()
1453     {
1454         var headerEl = this.headerEl();
1455         
1456         if(!headerEl){
1457             return;
1458         }
1459         
1460         if(this.expanded){
1461             this.collapse();
1462             return;
1463         }
1464         
1465         this.expand();
1466     },
1467     
1468     expand : function()
1469     {
1470         if(this.fireEvent('expand', this)) {
1471             
1472             this.expanded = true;
1473             
1474             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1475             
1476             this.el.select('.panel-body',true).first().removeClass('hide');
1477             
1478             var toggleEl = this.toggleEl();
1479
1480             if(!toggleEl){
1481                 return;
1482             }
1483
1484             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1485         }
1486         
1487     },
1488     
1489     collapse : function()
1490     {
1491         if(this.fireEvent('collapse', this)) {
1492             
1493             this.expanded = false;
1494             
1495             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1496             this.el.select('.panel-body',true).first().addClass('hide');
1497         
1498             var toggleEl = this.toggleEl();
1499
1500             if(!toggleEl){
1501                 return;
1502             }
1503
1504             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1505         }
1506     },
1507     
1508     toggleEl : function()
1509     {
1510         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1511             return;
1512         }
1513         
1514         return this.el.select('.panel-heading .fa',true).first();
1515     },
1516     
1517     headerEl : function()
1518     {
1519         if(!this.el || !this.panel.length || !this.header.length){
1520             return;
1521         }
1522         
1523         return this.el.select('.panel-heading',true).first()
1524     },
1525     
1526     bodyEl : function()
1527     {
1528         if(!this.el || !this.panel.length){
1529             return;
1530         }
1531         
1532         return this.el.select('.panel-body',true).first()
1533     },
1534     
1535     titleEl : function()
1536     {
1537         if(!this.el || !this.panel.length || !this.header.length){
1538             return;
1539         }
1540         
1541         return this.el.select('.panel-title',true).first();
1542     },
1543     
1544     setTitle : function(v)
1545     {
1546         var titleEl = this.titleEl();
1547         
1548         if(!titleEl){
1549             return;
1550         }
1551         
1552         titleEl.dom.innerHTML = v;
1553     },
1554     
1555     getTitle : function()
1556     {
1557         
1558         var titleEl = this.titleEl();
1559         
1560         if(!titleEl){
1561             return '';
1562         }
1563         
1564         return titleEl.dom.innerHTML;
1565     },
1566     
1567     setRightTitle : function(v)
1568     {
1569         var t = this.el.select('.panel-header-right',true).first();
1570         
1571         if(!t){
1572             return;
1573         }
1574         
1575         t.dom.innerHTML = v;
1576     },
1577     
1578     onClick : function(e)
1579     {
1580         e.preventDefault();
1581         
1582         this.fireEvent('click', this, e);
1583     }
1584 });
1585
1586  /*
1587  *  - LGPL
1588  *
1589  *  This is BS4's Card element.. - similar to our containers probably..
1590  * 
1591  */
1592 /**
1593  * @class Roo.bootstrap.Card
1594  * @extends Roo.bootstrap.Component
1595  * Bootstrap Card class
1596  *
1597  *
1598  * possible... may not be implemented..
1599  * @cfg {String} header_image  src url of image.
1600  * @cfg {String|Object} header
1601  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1602  * 
1603  * @cfg {String} title
1604  * @cfg {String} subtitle
1605  * @cfg {String} html -- html contents - or just use children..
1606  * @cfg {String} footer
1607  
1608  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1609  * 
1610  * @cfg {String} margin (0|1|2|3|4|5|auto)
1611  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1612  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1613  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1614  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1615  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1616  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1617  *
1618  * @cfg {String} padding (0|1|2|3|4|5)
1619  * @cfg {String} padding_top (0|1|2|3|4|5)
1620  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1621  * @cfg {String} padding_left (0|1|2|3|4|5)
1622  * @cfg {String} padding_right (0|1|2|3|4|5)
1623  * @cfg {String} padding_x (0|1|2|3|4|5)
1624  * @cfg {String} padding_y (0|1|2|3|4|5)
1625  *
1626  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1627  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1628  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1629  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1630  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1631  
1632  * @config {Boolean} dragable  if this card can be dragged.
1633  * @config {Boolean} drag_group  group for drag
1634  * 
1635  
1636  * @constructor
1637  * Create a new Container
1638  * @param {Object} config The config object
1639  */
1640
1641 Roo.bootstrap.Card = function(config){
1642     Roo.bootstrap.Card.superclass.constructor.call(this, config);
1643     
1644     this.addEvents({
1645         
1646     });
1647 };
1648
1649
1650 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
1651     
1652     
1653     weight : '',
1654     
1655     margin: '', /// may be better in component?
1656     margin_top: '', 
1657     margin_bottom: '', 
1658     margin_left: '',
1659     margin_right: '',
1660     margin_x: '',
1661     margin_y: '',
1662     
1663     padding : '',
1664     padding_top: '', 
1665     padding_bottom: '', 
1666     padding_left: '',
1667     padding_right: '',
1668     padding_x: '',
1669     padding_y: '',
1670     
1671     display: '', 
1672     display_xs: '', 
1673     display_sm: '', 
1674     display_lg: '',
1675     display_xl: '',
1676  
1677     header_image  : '',
1678     header : '',
1679     header_size : 0,
1680     title : '',
1681     subtitle : '',
1682     html : '',
1683     footer: '',
1684     
1685     dragable : false,
1686     drag_group : false,
1687     
1688     childContainer : false,
1689
1690     layoutCls : function()
1691     {
1692         var cls = '';
1693         var t = this;
1694         Roo.log(this.margin_bottom.length);
1695         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
1696             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
1697             
1698             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
1699                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
1700             }
1701             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
1702                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
1703             }
1704         });
1705         
1706         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
1707             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
1708                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
1709             }
1710         });
1711         
1712         // more generic support?
1713         if (this.hidden) {
1714             cls += ' d-none';
1715         }
1716         
1717         return cls;
1718     },
1719  
1720        // Roo.log("Call onRender: " + this.xtype);
1721         /*  We are looking at something like this.
1722 <div class="card">
1723     <img src="..." class="card-img-top" alt="...">
1724     <div class="card-body">
1725         <h5 class="card-title">Card title</h5>
1726          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
1727
1728         >> this bit is really the body...
1729         <div> << we will ad dthis in hopefully it will not break shit.
1730         
1731         ** card text does not actually have any styling...
1732         
1733             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
1734         
1735         </div> <<
1736           <a href="#" class="card-link">Card link</a>
1737           
1738     </div>
1739     <div class="card-footer">
1740         <small class="text-muted">Last updated 3 mins ago</small>
1741     </div>
1742 </div>
1743          */
1744     getAutoCreate : function(){
1745         
1746         var cfg = {
1747             tag : 'div',
1748             cls : 'card',
1749             cn : [ ]
1750         };
1751         
1752         if (this.weight.length && this.weight != 'light') {
1753             cfg.cls += ' text-white';
1754         } else {
1755             cfg.cls += ' text-dark'; // need as it's nested..
1756         }
1757         if (this.weight.length) {
1758             cfg.cls += ' bg-' + this.weight;
1759         }
1760         
1761         cfg.cls += this.layoutCls(); 
1762         
1763         if (this.header.length) {
1764             cfg.cn.push({
1765                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
1766                 cls : 'card-header',
1767                 html : this.header // escape?
1768             });
1769         } else {
1770             cfg.cn.push({
1771                 tag : 'div',
1772                 cls : 'card-header d-none'
1773             });
1774         }
1775         if (this.header_image.length) {
1776             cfg.cn.push({
1777                 tag : 'img',
1778                 cls : 'card-img-top',
1779                 src: this.header_image // escape?
1780             });
1781         } else {
1782             cfg.cn.push({
1783                 tag : 'div',
1784                 cls : 'card-img-top d-none' 
1785             });
1786         }
1787         
1788         var body = {
1789             tag : 'div',
1790             cls : 'card-body',
1791             cn : []
1792         };
1793         cfg.cn.push(body);
1794         
1795         if (this.title.length) {
1796             body.cn.push({
1797                 tag : 'div',
1798                 cls : 'card-title',
1799                 src: this.title // escape?
1800             });
1801         }
1802         
1803         if (this.subtitle.length) {
1804             body.cn.push({
1805                 tag : 'div',
1806                 cls : 'card-title',
1807                 src: this.subtitle // escape?
1808             });
1809         }
1810         
1811         body.cn.push({
1812             tag : 'div',
1813             cls : 'roo-card-body-ctr'
1814         });
1815         
1816         if (this.html.length) {
1817             body.cn.push({
1818                 tag: 'div',
1819                 html : this.html
1820             });
1821         }
1822         // fixme ? handle objects?
1823         if (this.footer.length) {
1824             cfg.cn.push({
1825                 tag : 'div',
1826                 cls : 'card-footer',
1827                 html: this.footer // escape?
1828             });
1829         }
1830         // footer...
1831         
1832         return cfg;
1833     },
1834     
1835     
1836     getCardHeader : function()
1837     {
1838         var  ret = this.el.select('.card-header',true).first();
1839         if (ret.hasClass('d-none')) {
1840             ret.removeClass('d-none');
1841         }
1842         
1843         return ret;
1844     },
1845     
1846     getCardImageTop : function()
1847     {
1848         var  ret = this.el.select('.card-img-top',true).first();
1849         if (ret.hasClass('d-none')) {
1850             ret.removeClass('d-none');
1851         }
1852         
1853         return ret;
1854     },
1855     
1856     getChildContainer : function()
1857     {
1858         
1859         if(!this.el){
1860             return false;
1861         }
1862         return this.el.select('.roo-card-body-ctr',true).first();    
1863     },
1864     
1865     initEvents: function() 
1866     {
1867         if(this.dragable){
1868              this.dragZone = new Roo.dd.DragZone(this.getEl(), {
1869                     containerScroll: true,
1870                     ddGroup: this.drag_group || 'default_card_drag_group'
1871             });
1872             this.dragZone.getDragData = this.getDragData.createDelegate(this);
1873         }
1874         
1875         
1876         
1877     },
1878     getDragData : function(e) {
1879         var target = this.getEl();
1880         if (target) {
1881             //this.handleSelection(e);
1882             
1883             var dragData = {
1884                 source: this,
1885                 copy: false,
1886                 nodes: this.getEl(),
1887                 records: []
1888             };
1889             
1890             
1891             dragData.ddel = target.dom ;        // the div element
1892             Roo.log(target.getWidth( ));
1893              dragData.ddel.style.width = target.getWidth() + 'px';
1894             
1895             return dragData;
1896         }
1897         return false;
1898     }
1899     
1900 });
1901
1902 /*
1903  * - LGPL
1904  *
1905  * Card header - holder for the card header elements.
1906  * 
1907  */
1908
1909 /**
1910  * @class Roo.bootstrap.CardHeader
1911  * @extends Roo.bootstrap.Element
1912  * Bootstrap CardHeader class
1913  * @constructor
1914  * Create a new Card Header - that you can embed children into
1915  * @param {Object} config The config object
1916  */
1917
1918 Roo.bootstrap.CardHeader = function(config){
1919     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
1920 };
1921
1922 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
1923     
1924     
1925     container_method : 'getCardHeader' 
1926     
1927      
1928     
1929     
1930    
1931 });
1932
1933  
1934
1935  /*
1936  * - LGPL
1937  *
1938  * Card header - holder for the card header elements.
1939  * 
1940  */
1941
1942 /**
1943  * @class Roo.bootstrap.CardImageTop
1944  * @extends Roo.bootstrap.Element
1945  * Bootstrap CardImageTop class
1946  * @constructor
1947  * Create a new Card Image Top container
1948  * @param {Object} config The config object
1949  */
1950
1951 Roo.bootstrap.CardImageTop = function(config){
1952     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
1953 };
1954
1955 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
1956     
1957    
1958     container_method : 'getCardImageTop' 
1959     
1960      
1961     
1962    
1963 });
1964
1965  
1966
1967  /*
1968  * - LGPL
1969  *
1970  * image
1971  * 
1972  */
1973
1974
1975 /**
1976  * @class Roo.bootstrap.Img
1977  * @extends Roo.bootstrap.Component
1978  * Bootstrap Img class
1979  * @cfg {Boolean} imgResponsive false | true
1980  * @cfg {String} border rounded | circle | thumbnail
1981  * @cfg {String} src image source
1982  * @cfg {String} alt image alternative text
1983  * @cfg {String} href a tag href
1984  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1985  * @cfg {String} xsUrl xs image source
1986  * @cfg {String} smUrl sm image source
1987  * @cfg {String} mdUrl md image source
1988  * @cfg {String} lgUrl lg image source
1989  * 
1990  * @constructor
1991  * Create a new Input
1992  * @param {Object} config The config object
1993  */
1994
1995 Roo.bootstrap.Img = function(config){
1996     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1997     
1998     this.addEvents({
1999         // img events
2000         /**
2001          * @event click
2002          * The img click event for the img.
2003          * @param {Roo.EventObject} e
2004          */
2005         "click" : true
2006     });
2007 };
2008
2009 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2010     
2011     imgResponsive: true,
2012     border: '',
2013     src: 'about:blank',
2014     href: false,
2015     target: false,
2016     xsUrl: '',
2017     smUrl: '',
2018     mdUrl: '',
2019     lgUrl: '',
2020
2021     getAutoCreate : function()
2022     {   
2023         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2024             return this.createSingleImg();
2025         }
2026         
2027         var cfg = {
2028             tag: 'div',
2029             cls: 'roo-image-responsive-group',
2030             cn: []
2031         };
2032         var _this = this;
2033         
2034         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2035             
2036             if(!_this[size + 'Url']){
2037                 return;
2038             }
2039             
2040             var img = {
2041                 tag: 'img',
2042                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2043                 html: _this.html || cfg.html,
2044                 src: _this[size + 'Url']
2045             };
2046             
2047             img.cls += ' roo-image-responsive-' + size;
2048             
2049             var s = ['xs', 'sm', 'md', 'lg'];
2050             
2051             s.splice(s.indexOf(size), 1);
2052             
2053             Roo.each(s, function(ss){
2054                 img.cls += ' hidden-' + ss;
2055             });
2056             
2057             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2058                 cfg.cls += ' img-' + _this.border;
2059             }
2060             
2061             if(_this.alt){
2062                 cfg.alt = _this.alt;
2063             }
2064             
2065             if(_this.href){
2066                 var a = {
2067                     tag: 'a',
2068                     href: _this.href,
2069                     cn: [
2070                         img
2071                     ]
2072                 };
2073
2074                 if(this.target){
2075                     a.target = _this.target;
2076                 }
2077             }
2078             
2079             cfg.cn.push((_this.href) ? a : img);
2080             
2081         });
2082         
2083         return cfg;
2084     },
2085     
2086     createSingleImg : function()
2087     {
2088         var cfg = {
2089             tag: 'img',
2090             cls: (this.imgResponsive) ? 'img-responsive' : '',
2091             html : null,
2092             src : 'about:blank'  // just incase src get's set to undefined?!?
2093         };
2094         
2095         cfg.html = this.html || cfg.html;
2096         
2097         cfg.src = this.src || cfg.src;
2098         
2099         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2100             cfg.cls += ' img-' + this.border;
2101         }
2102         
2103         if(this.alt){
2104             cfg.alt = this.alt;
2105         }
2106         
2107         if(this.href){
2108             var a = {
2109                 tag: 'a',
2110                 href: this.href,
2111                 cn: [
2112                     cfg
2113                 ]
2114             };
2115             
2116             if(this.target){
2117                 a.target = this.target;
2118             }
2119             
2120         }
2121         
2122         return (this.href) ? a : cfg;
2123     },
2124     
2125     initEvents: function() 
2126     {
2127         if(!this.href){
2128             this.el.on('click', this.onClick, this);
2129         }
2130         
2131     },
2132     
2133     onClick : function(e)
2134     {
2135         Roo.log('img onclick');
2136         this.fireEvent('click', this, e);
2137     },
2138     /**
2139      * Sets the url of the image - used to update it
2140      * @param {String} url the url of the image
2141      */
2142     
2143     setSrc : function(url)
2144     {
2145         this.src =  url;
2146         
2147         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2148             this.el.dom.src =  url;
2149             return;
2150         }
2151         
2152         this.el.select('img', true).first().dom.src =  url;
2153     }
2154     
2155     
2156    
2157 });
2158
2159  /*
2160  * - LGPL
2161  *
2162  * image
2163  * 
2164  */
2165
2166
2167 /**
2168  * @class Roo.bootstrap.Link
2169  * @extends Roo.bootstrap.Component
2170  * Bootstrap Link Class
2171  * @cfg {String} alt image alternative text
2172  * @cfg {String} href a tag href
2173  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2174  * @cfg {String} html the content of the link.
2175  * @cfg {String} anchor name for the anchor link
2176  * @cfg {String} fa - favicon
2177
2178  * @cfg {Boolean} preventDefault (true | false) default false
2179
2180  * 
2181  * @constructor
2182  * Create a new Input
2183  * @param {Object} config The config object
2184  */
2185
2186 Roo.bootstrap.Link = function(config){
2187     Roo.bootstrap.Link.superclass.constructor.call(this, config);
2188     
2189     this.addEvents({
2190         // img events
2191         /**
2192          * @event click
2193          * The img click event for the img.
2194          * @param {Roo.EventObject} e
2195          */
2196         "click" : true
2197     });
2198 };
2199
2200 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
2201     
2202     href: false,
2203     target: false,
2204     preventDefault: false,
2205     anchor : false,
2206     alt : false,
2207     fa: false,
2208
2209
2210     getAutoCreate : function()
2211     {
2212         var html = this.html || '';
2213         
2214         if (this.fa !== false) {
2215             html = '<i class="fa fa-' + this.fa + '"></i>';
2216         }
2217         var cfg = {
2218             tag: 'a'
2219         };
2220         // anchor's do not require html/href...
2221         if (this.anchor === false) {
2222             cfg.html = html;
2223             cfg.href = this.href || '#';
2224         } else {
2225             cfg.name = this.anchor;
2226             if (this.html !== false || this.fa !== false) {
2227                 cfg.html = html;
2228             }
2229             if (this.href !== false) {
2230                 cfg.href = this.href;
2231             }
2232         }
2233         
2234         if(this.alt !== false){
2235             cfg.alt = this.alt;
2236         }
2237         
2238         
2239         if(this.target !== false) {
2240             cfg.target = this.target;
2241         }
2242         
2243         return cfg;
2244     },
2245     
2246     initEvents: function() {
2247         
2248         if(!this.href || this.preventDefault){
2249             this.el.on('click', this.onClick, this);
2250         }
2251     },
2252     
2253     onClick : function(e)
2254     {
2255         if(this.preventDefault){
2256             e.preventDefault();
2257         }
2258         //Roo.log('img onclick');
2259         this.fireEvent('click', this, e);
2260     }
2261    
2262 });
2263
2264  /*
2265  * - LGPL
2266  *
2267  * header
2268  * 
2269  */
2270
2271 /**
2272  * @class Roo.bootstrap.Header
2273  * @extends Roo.bootstrap.Component
2274  * Bootstrap Header class
2275  * @cfg {String} html content of header
2276  * @cfg {Number} level (1|2|3|4|5|6) default 1
2277  * 
2278  * @constructor
2279  * Create a new Header
2280  * @param {Object} config The config object
2281  */
2282
2283
2284 Roo.bootstrap.Header  = function(config){
2285     Roo.bootstrap.Header.superclass.constructor.call(this, config);
2286 };
2287
2288 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
2289     
2290     //href : false,
2291     html : false,
2292     level : 1,
2293     
2294     
2295     
2296     getAutoCreate : function(){
2297         
2298         
2299         
2300         var cfg = {
2301             tag: 'h' + (1 *this.level),
2302             html: this.html || ''
2303         } ;
2304         
2305         return cfg;
2306     }
2307    
2308 });
2309
2310  
2311
2312  /*
2313  * Based on:
2314  * Ext JS Library 1.1.1
2315  * Copyright(c) 2006-2007, Ext JS, LLC.
2316  *
2317  * Originally Released Under LGPL - original licence link has changed is not relivant.
2318  *
2319  * Fork - LGPL
2320  * <script type="text/javascript">
2321  */
2322  
2323 /**
2324  * @class Roo.bootstrap.MenuMgr
2325  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
2326  * @singleton
2327  */
2328 Roo.bootstrap.MenuMgr = function(){
2329    var menus, active, groups = {}, attached = false, lastShow = new Date();
2330
2331    // private - called when first menu is created
2332    function init(){
2333        menus = {};
2334        active = new Roo.util.MixedCollection();
2335        Roo.get(document).addKeyListener(27, function(){
2336            if(active.length > 0){
2337                hideAll();
2338            }
2339        });
2340    }
2341
2342    // private
2343    function hideAll(){
2344        if(active && active.length > 0){
2345            var c = active.clone();
2346            c.each(function(m){
2347                m.hide();
2348            });
2349        }
2350    }
2351
2352    // private
2353    function onHide(m){
2354        active.remove(m);
2355        if(active.length < 1){
2356            Roo.get(document).un("mouseup", onMouseDown);
2357             
2358            attached = false;
2359        }
2360    }
2361
2362    // private
2363    function onShow(m){
2364        var last = active.last();
2365        lastShow = new Date();
2366        active.add(m);
2367        if(!attached){
2368           Roo.get(document).on("mouseup", onMouseDown);
2369            
2370            attached = true;
2371        }
2372        if(m.parentMenu){
2373           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
2374           m.parentMenu.activeChild = m;
2375        }else if(last && last.isVisible()){
2376           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
2377        }
2378    }
2379
2380    // private
2381    function onBeforeHide(m){
2382        if(m.activeChild){
2383            m.activeChild.hide();
2384        }
2385        if(m.autoHideTimer){
2386            clearTimeout(m.autoHideTimer);
2387            delete m.autoHideTimer;
2388        }
2389    }
2390
2391    // private
2392    function onBeforeShow(m){
2393        var pm = m.parentMenu;
2394        if(!pm && !m.allowOtherMenus){
2395            hideAll();
2396        }else if(pm && pm.activeChild && active != m){
2397            pm.activeChild.hide();
2398        }
2399    }
2400
2401    // private this should really trigger on mouseup..
2402    function onMouseDown(e){
2403         Roo.log("on Mouse Up");
2404         
2405         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
2406             Roo.log("MenuManager hideAll");
2407             hideAll();
2408             e.stopEvent();
2409         }
2410         
2411         
2412    }
2413
2414    // private
2415    function onBeforeCheck(mi, state){
2416        if(state){
2417            var g = groups[mi.group];
2418            for(var i = 0, l = g.length; i < l; i++){
2419                if(g[i] != mi){
2420                    g[i].setChecked(false);
2421                }
2422            }
2423        }
2424    }
2425
2426    return {
2427
2428        /**
2429         * Hides all menus that are currently visible
2430         */
2431        hideAll : function(){
2432             hideAll();  
2433        },
2434
2435        // private
2436        register : function(menu){
2437            if(!menus){
2438                init();
2439            }
2440            menus[menu.id] = menu;
2441            menu.on("beforehide", onBeforeHide);
2442            menu.on("hide", onHide);
2443            menu.on("beforeshow", onBeforeShow);
2444            menu.on("show", onShow);
2445            var g = menu.group;
2446            if(g && menu.events["checkchange"]){
2447                if(!groups[g]){
2448                    groups[g] = [];
2449                }
2450                groups[g].push(menu);
2451                menu.on("checkchange", onCheck);
2452            }
2453        },
2454
2455         /**
2456          * Returns a {@link Roo.menu.Menu} object
2457          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
2458          * be used to generate and return a new Menu instance.
2459          */
2460        get : function(menu){
2461            if(typeof menu == "string"){ // menu id
2462                return menus[menu];
2463            }else if(menu.events){  // menu instance
2464                return menu;
2465            }
2466            /*else if(typeof menu.length == 'number'){ // array of menu items?
2467                return new Roo.bootstrap.Menu({items:menu});
2468            }else{ // otherwise, must be a config
2469                return new Roo.bootstrap.Menu(menu);
2470            }
2471            */
2472            return false;
2473        },
2474
2475        // private
2476        unregister : function(menu){
2477            delete menus[menu.id];
2478            menu.un("beforehide", onBeforeHide);
2479            menu.un("hide", onHide);
2480            menu.un("beforeshow", onBeforeShow);
2481            menu.un("show", onShow);
2482            var g = menu.group;
2483            if(g && menu.events["checkchange"]){
2484                groups[g].remove(menu);
2485                menu.un("checkchange", onCheck);
2486            }
2487        },
2488
2489        // private
2490        registerCheckable : function(menuItem){
2491            var g = menuItem.group;
2492            if(g){
2493                if(!groups[g]){
2494                    groups[g] = [];
2495                }
2496                groups[g].push(menuItem);
2497                menuItem.on("beforecheckchange", onBeforeCheck);
2498            }
2499        },
2500
2501        // private
2502        unregisterCheckable : function(menuItem){
2503            var g = menuItem.group;
2504            if(g){
2505                groups[g].remove(menuItem);
2506                menuItem.un("beforecheckchange", onBeforeCheck);
2507            }
2508        }
2509    };
2510 }();/*
2511  * - LGPL
2512  *
2513  * menu
2514  * 
2515  */
2516
2517 /**
2518  * @class Roo.bootstrap.Menu
2519  * @extends Roo.bootstrap.Component
2520  * Bootstrap Menu class - container for MenuItems
2521  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2522  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2523  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2524  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2525  * 
2526  * @constructor
2527  * Create a new Menu
2528  * @param {Object} config The config object
2529  */
2530
2531
2532 Roo.bootstrap.Menu = function(config){
2533     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2534     if (this.registerMenu && this.type != 'treeview')  {
2535         Roo.bootstrap.MenuMgr.register(this);
2536     }
2537     
2538     
2539     this.addEvents({
2540         /**
2541          * @event beforeshow
2542          * Fires before this menu is displayed (return false to block)
2543          * @param {Roo.menu.Menu} this
2544          */
2545         beforeshow : true,
2546         /**
2547          * @event beforehide
2548          * Fires before this menu is hidden (return false to block)
2549          * @param {Roo.menu.Menu} this
2550          */
2551         beforehide : true,
2552         /**
2553          * @event show
2554          * Fires after this menu is displayed
2555          * @param {Roo.menu.Menu} this
2556          */
2557         show : true,
2558         /**
2559          * @event hide
2560          * Fires after this menu is hidden
2561          * @param {Roo.menu.Menu} this
2562          */
2563         hide : true,
2564         /**
2565          * @event click
2566          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2567          * @param {Roo.menu.Menu} this
2568          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2569          * @param {Roo.EventObject} e
2570          */
2571         click : true,
2572         /**
2573          * @event mouseover
2574          * Fires when the mouse is hovering over this menu
2575          * @param {Roo.menu.Menu} this
2576          * @param {Roo.EventObject} e
2577          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2578          */
2579         mouseover : true,
2580         /**
2581          * @event mouseout
2582          * Fires when the mouse exits this menu
2583          * @param {Roo.menu.Menu} this
2584          * @param {Roo.EventObject} e
2585          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2586          */
2587         mouseout : true,
2588         /**
2589          * @event itemclick
2590          * Fires when a menu item contained in this menu is clicked
2591          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2592          * @param {Roo.EventObject} e
2593          */
2594         itemclick: true
2595     });
2596     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2597 };
2598
2599 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2600     
2601    /// html : false,
2602     //align : '',
2603     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2604     type: false,
2605     /**
2606      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2607      */
2608     registerMenu : true,
2609     
2610     menuItems :false, // stores the menu items..
2611     
2612     hidden:true,
2613         
2614     parentMenu : false,
2615     
2616     stopEvent : true,
2617     
2618     isLink : false,
2619     
2620     getChildContainer : function() {
2621         return this.el;  
2622     },
2623     
2624     getAutoCreate : function(){
2625          
2626         //if (['right'].indexOf(this.align)!==-1) {
2627         //    cfg.cn[1].cls += ' pull-right'
2628         //}
2629         
2630         
2631         var cfg = {
2632             tag : 'ul',
2633             cls : 'dropdown-menu' ,
2634             style : 'z-index:1000'
2635             
2636         };
2637         
2638         if (this.type === 'submenu') {
2639             cfg.cls = 'submenu active';
2640         }
2641         if (this.type === 'treeview') {
2642             cfg.cls = 'treeview-menu';
2643         }
2644         
2645         return cfg;
2646     },
2647     initEvents : function() {
2648         
2649        // Roo.log("ADD event");
2650        // Roo.log(this.triggerEl.dom);
2651         
2652         this.triggerEl.on('click', this.onTriggerClick, this);
2653         
2654         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2655         
2656         
2657         if (this.triggerEl.hasClass('nav-item')) {
2658             // dropdown toggle on the 'a' in BS4?
2659             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2660         } else {
2661             this.triggerEl.addClass('dropdown-toggle');
2662         }
2663         if (Roo.isTouch) {
2664             this.el.on('touchstart'  , this.onTouch, this);
2665         }
2666         this.el.on('click' , this.onClick, this);
2667
2668         this.el.on("mouseover", this.onMouseOver, this);
2669         this.el.on("mouseout", this.onMouseOut, this);
2670         
2671     },
2672     
2673     findTargetItem : function(e)
2674     {
2675         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2676         if(!t){
2677             return false;
2678         }
2679         //Roo.log(t);         Roo.log(t.id);
2680         if(t && t.id){
2681             //Roo.log(this.menuitems);
2682             return this.menuitems.get(t.id);
2683             
2684             //return this.items.get(t.menuItemId);
2685         }
2686         
2687         return false;
2688     },
2689     
2690     onTouch : function(e) 
2691     {
2692         Roo.log("menu.onTouch");
2693         //e.stopEvent(); this make the user popdown broken
2694         this.onClick(e);
2695     },
2696     
2697     onClick : function(e)
2698     {
2699         Roo.log("menu.onClick");
2700         
2701         var t = this.findTargetItem(e);
2702         if(!t || t.isContainer){
2703             return;
2704         }
2705         Roo.log(e);
2706         /*
2707         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2708             if(t == this.activeItem && t.shouldDeactivate(e)){
2709                 this.activeItem.deactivate();
2710                 delete this.activeItem;
2711                 return;
2712             }
2713             if(t.canActivate){
2714                 this.setActiveItem(t, true);
2715             }
2716             return;
2717             
2718             
2719         }
2720         */
2721        
2722         Roo.log('pass click event');
2723         
2724         t.onClick(e);
2725         
2726         this.fireEvent("click", this, t, e);
2727         
2728         var _this = this;
2729         
2730         if(!t.href.length || t.href == '#'){
2731             (function() { _this.hide(); }).defer(100);
2732         }
2733         
2734     },
2735     
2736     onMouseOver : function(e){
2737         var t  = this.findTargetItem(e);
2738         //Roo.log(t);
2739         //if(t){
2740         //    if(t.canActivate && !t.disabled){
2741         //        this.setActiveItem(t, true);
2742         //    }
2743         //}
2744         
2745         this.fireEvent("mouseover", this, e, t);
2746     },
2747     isVisible : function(){
2748         return !this.hidden;
2749     },
2750     onMouseOut : function(e){
2751         var t  = this.findTargetItem(e);
2752         
2753         //if(t ){
2754         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2755         //        this.activeItem.deactivate();
2756         //        delete this.activeItem;
2757         //    }
2758         //}
2759         this.fireEvent("mouseout", this, e, t);
2760     },
2761     
2762     
2763     /**
2764      * Displays this menu relative to another element
2765      * @param {String/HTMLElement/Roo.Element} element The element to align to
2766      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2767      * the element (defaults to this.defaultAlign)
2768      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2769      */
2770     show : function(el, pos, parentMenu)
2771     {
2772         if (false === this.fireEvent("beforeshow", this)) {
2773             Roo.log("show canceled");
2774             return;
2775         }
2776         this.parentMenu = parentMenu;
2777         if(!this.el){
2778             this.render();
2779         }
2780         
2781         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2782     },
2783      /**
2784      * Displays this menu at a specific xy position
2785      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2786      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2787      */
2788     showAt : function(xy, parentMenu, /* private: */_e){
2789         this.parentMenu = parentMenu;
2790         if(!this.el){
2791             this.render();
2792         }
2793         if(_e !== false){
2794             this.fireEvent("beforeshow", this);
2795             //xy = this.el.adjustForConstraints(xy);
2796         }
2797         
2798         //this.el.show();
2799         this.hideMenuItems();
2800         this.hidden = false;
2801         this.triggerEl.addClass('open');
2802         this.el.addClass('show');
2803         
2804         // reassign x when hitting right
2805         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2806             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2807         }
2808         
2809         // reassign y when hitting bottom
2810         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2811             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2812         }
2813         
2814         // but the list may align on trigger left or trigger top... should it be a properity?
2815         
2816         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2817             this.el.setXY(xy);
2818         }
2819         
2820         this.focus();
2821         this.fireEvent("show", this);
2822     },
2823     
2824     focus : function(){
2825         return;
2826         if(!this.hidden){
2827             this.doFocus.defer(50, this);
2828         }
2829     },
2830
2831     doFocus : function(){
2832         if(!this.hidden){
2833             this.focusEl.focus();
2834         }
2835     },
2836
2837     /**
2838      * Hides this menu and optionally all parent menus
2839      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2840      */
2841     hide : function(deep)
2842     {
2843         if (false === this.fireEvent("beforehide", this)) {
2844             Roo.log("hide canceled");
2845             return;
2846         }
2847         this.hideMenuItems();
2848         if(this.el && this.isVisible()){
2849            
2850             if(this.activeItem){
2851                 this.activeItem.deactivate();
2852                 this.activeItem = null;
2853             }
2854             this.triggerEl.removeClass('open');;
2855             this.el.removeClass('show');
2856             this.hidden = true;
2857             this.fireEvent("hide", this);
2858         }
2859         if(deep === true && this.parentMenu){
2860             this.parentMenu.hide(true);
2861         }
2862     },
2863     
2864     onTriggerClick : function(e)
2865     {
2866         Roo.log('trigger click');
2867         
2868         var target = e.getTarget();
2869         
2870         Roo.log(target.nodeName.toLowerCase());
2871         
2872         if(target.nodeName.toLowerCase() === 'i'){
2873             e.preventDefault();
2874         }
2875         
2876     },
2877     
2878     onTriggerPress  : function(e)
2879     {
2880         Roo.log('trigger press');
2881         //Roo.log(e.getTarget());
2882        // Roo.log(this.triggerEl.dom);
2883        
2884         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2885         var pel = Roo.get(e.getTarget());
2886         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2887             Roo.log('is treeview or dropdown?');
2888             return;
2889         }
2890         
2891         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2892             return;
2893         }
2894         
2895         if (this.isVisible()) {
2896             Roo.log('hide');
2897             this.hide();
2898         } else {
2899             Roo.log('show');
2900             this.show(this.triggerEl, '?', false);
2901         }
2902         
2903         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2904             e.stopEvent();
2905         }
2906         
2907     },
2908        
2909     
2910     hideMenuItems : function()
2911     {
2912         Roo.log("hide Menu Items");
2913         if (!this.el) { 
2914             return;
2915         }
2916         
2917         this.el.select('.open',true).each(function(aa) {
2918             
2919             aa.removeClass('open');
2920          
2921         });
2922     },
2923     addxtypeChild : function (tree, cntr) {
2924         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2925           
2926         this.menuitems.add(comp);
2927         return comp;
2928
2929     },
2930     getEl : function()
2931     {
2932         Roo.log(this.el);
2933         return this.el;
2934     },
2935     
2936     clear : function()
2937     {
2938         this.getEl().dom.innerHTML = '';
2939         this.menuitems.clear();
2940     }
2941 });
2942
2943  
2944  /*
2945  * - LGPL
2946  *
2947  * menu item
2948  * 
2949  */
2950
2951
2952 /**
2953  * @class Roo.bootstrap.MenuItem
2954  * @extends Roo.bootstrap.Component
2955  * Bootstrap MenuItem class
2956  * @cfg {String} html the menu label
2957  * @cfg {String} href the link
2958  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2959  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2960  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2961  * @cfg {String} fa favicon to show on left of menu item.
2962  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2963  * 
2964  * 
2965  * @constructor
2966  * Create a new MenuItem
2967  * @param {Object} config The config object
2968  */
2969
2970
2971 Roo.bootstrap.MenuItem = function(config){
2972     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2973     this.addEvents({
2974         // raw events
2975         /**
2976          * @event click
2977          * The raw click event for the entire grid.
2978          * @param {Roo.bootstrap.MenuItem} this
2979          * @param {Roo.EventObject} e
2980          */
2981         "click" : true
2982     });
2983 };
2984
2985 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2986     
2987     href : false,
2988     html : false,
2989     preventDefault: false,
2990     isContainer : false,
2991     active : false,
2992     fa: false,
2993     
2994     getAutoCreate : function(){
2995         
2996         if(this.isContainer){
2997             return {
2998                 tag: 'li',
2999                 cls: 'dropdown-menu-item '
3000             };
3001         }
3002         var ctag = {
3003             tag: 'span',
3004             html: 'Link'
3005         };
3006         
3007         var anc = {
3008             tag : 'a',
3009             cls : 'dropdown-item',
3010             href : '#',
3011             cn : [  ]
3012         };
3013         
3014         if (this.fa !== false) {
3015             anc.cn.push({
3016                 tag : 'i',
3017                 cls : 'fa fa-' + this.fa
3018             });
3019         }
3020         
3021         anc.cn.push(ctag);
3022         
3023         
3024         var cfg= {
3025             tag: 'li',
3026             cls: 'dropdown-menu-item',
3027             cn: [ anc ]
3028         };
3029         if (this.parent().type == 'treeview') {
3030             cfg.cls = 'treeview-menu';
3031         }
3032         if (this.active) {
3033             cfg.cls += ' active';
3034         }
3035         
3036         
3037         
3038         anc.href = this.href || cfg.cn[0].href ;
3039         ctag.html = this.html || cfg.cn[0].html ;
3040         return cfg;
3041     },
3042     
3043     initEvents: function()
3044     {
3045         if (this.parent().type == 'treeview') {
3046             this.el.select('a').on('click', this.onClick, this);
3047         }
3048         
3049         if (this.menu) {
3050             this.menu.parentType = this.xtype;
3051             this.menu.triggerEl = this.el;
3052             this.menu = this.addxtype(Roo.apply({}, this.menu));
3053         }
3054         
3055     },
3056     onClick : function(e)
3057     {
3058         Roo.log('item on click ');
3059         
3060         if(this.preventDefault){
3061             e.preventDefault();
3062         }
3063         //this.parent().hideMenuItems();
3064         
3065         this.fireEvent('click', this, e);
3066     },
3067     getEl : function()
3068     {
3069         return this.el;
3070     } 
3071 });
3072
3073  
3074
3075  /*
3076  * - LGPL
3077  *
3078  * menu separator
3079  * 
3080  */
3081
3082
3083 /**
3084  * @class Roo.bootstrap.MenuSeparator
3085  * @extends Roo.bootstrap.Component
3086  * Bootstrap MenuSeparator class
3087  * 
3088  * @constructor
3089  * Create a new MenuItem
3090  * @param {Object} config The config object
3091  */
3092
3093
3094 Roo.bootstrap.MenuSeparator = function(config){
3095     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3096 };
3097
3098 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3099     
3100     getAutoCreate : function(){
3101         var cfg = {
3102             cls: 'divider',
3103             tag : 'li'
3104         };
3105         
3106         return cfg;
3107     }
3108    
3109 });
3110
3111  
3112
3113  
3114 /*
3115 * Licence: LGPL
3116 */
3117
3118 /**
3119  * @class Roo.bootstrap.Modal
3120  * @extends Roo.bootstrap.Component
3121  * Bootstrap Modal class
3122  * @cfg {String} title Title of dialog
3123  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3124  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3125  * @cfg {Boolean} specificTitle default false
3126  * @cfg {Array} buttons Array of buttons or standard button set..
3127  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3128  * @cfg {Boolean} animate default true
3129  * @cfg {Boolean} allow_close default true
3130  * @cfg {Boolean} fitwindow default false
3131  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3132  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3133  * @cfg {String} size (sm|lg) default empty
3134  * @cfg {Number} max_width set the max width of modal
3135  *
3136  *
3137  * @constructor
3138  * Create a new Modal Dialog
3139  * @param {Object} config The config object
3140  */
3141
3142 Roo.bootstrap.Modal = function(config){
3143     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3144     this.addEvents({
3145         // raw events
3146         /**
3147          * @event btnclick
3148          * The raw btnclick event for the button
3149          * @param {Roo.EventObject} e
3150          */
3151         "btnclick" : true,
3152         /**
3153          * @event resize
3154          * Fire when dialog resize
3155          * @param {Roo.bootstrap.Modal} this
3156          * @param {Roo.EventObject} e
3157          */
3158         "resize" : true
3159     });
3160     this.buttons = this.buttons || [];
3161
3162     if (this.tmpl) {
3163         this.tmpl = Roo.factory(this.tmpl);
3164     }
3165
3166 };
3167
3168 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3169
3170     title : 'test dialog',
3171
3172     buttons : false,
3173
3174     // set on load...
3175
3176     html: false,
3177
3178     tmp: false,
3179
3180     specificTitle: false,
3181
3182     buttonPosition: 'right',
3183
3184     allow_close : true,
3185
3186     animate : true,
3187
3188     fitwindow: false,
3189     
3190      // private
3191     dialogEl: false,
3192     bodyEl:  false,
3193     footerEl:  false,
3194     titleEl:  false,
3195     closeEl:  false,
3196
3197     size: '',
3198     
3199     max_width: 0,
3200     
3201     max_height: 0,
3202     
3203     fit_content: false,
3204
3205     onRender : function(ct, position)
3206     {
3207         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3208
3209         if(!this.el){
3210             var cfg = Roo.apply({},  this.getAutoCreate());
3211             cfg.id = Roo.id();
3212             //if(!cfg.name){
3213             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3214             //}
3215             //if (!cfg.name.length) {
3216             //    delete cfg.name;
3217            // }
3218             if (this.cls) {
3219                 cfg.cls += ' ' + this.cls;
3220             }
3221             if (this.style) {
3222                 cfg.style = this.style;
3223             }
3224             this.el = Roo.get(document.body).createChild(cfg, position);
3225         }
3226         //var type = this.el.dom.type;
3227
3228
3229         if(this.tabIndex !== undefined){
3230             this.el.dom.setAttribute('tabIndex', this.tabIndex);
3231         }
3232
3233         this.dialogEl = this.el.select('.modal-dialog',true).first();
3234         this.bodyEl = this.el.select('.modal-body',true).first();
3235         this.closeEl = this.el.select('.modal-header .close', true).first();
3236         this.headerEl = this.el.select('.modal-header',true).first();
3237         this.titleEl = this.el.select('.modal-title',true).first();
3238         this.footerEl = this.el.select('.modal-footer',true).first();
3239
3240         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
3241         
3242         //this.el.addClass("x-dlg-modal");
3243
3244         if (this.buttons.length) {
3245             Roo.each(this.buttons, function(bb) {
3246                 var b = Roo.apply({}, bb);
3247                 b.xns = b.xns || Roo.bootstrap;
3248                 b.xtype = b.xtype || 'Button';
3249                 if (typeof(b.listeners) == 'undefined') {
3250                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
3251                 }
3252
3253                 var btn = Roo.factory(b);
3254
3255                 btn.render(this.getButtonContainer());
3256
3257             },this);
3258         }
3259         // render the children.
3260         var nitems = [];
3261
3262         if(typeof(this.items) != 'undefined'){
3263             var items = this.items;
3264             delete this.items;
3265
3266             for(var i =0;i < items.length;i++) {
3267                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
3268             }
3269         }
3270
3271         this.items = nitems;
3272
3273         // where are these used - they used to be body/close/footer
3274
3275
3276         this.initEvents();
3277         //this.el.addClass([this.fieldClass, this.cls]);
3278
3279     },
3280
3281     getAutoCreate : function()
3282     {
3283         // we will default to modal-body-overflow - might need to remove or make optional later.
3284         var bdy = {
3285                 cls : 'modal-body enable-modal-body-overflow ', 
3286                 html : this.html || ''
3287         };
3288
3289         var title = {
3290             tag: 'h4',
3291             cls : 'modal-title',
3292             html : this.title
3293         };
3294
3295         if(this.specificTitle){
3296             title = this.title;
3297
3298         }
3299
3300         var header = [];
3301         if (this.allow_close && Roo.bootstrap.version == 3) {
3302             header.push({
3303                 tag: 'button',
3304                 cls : 'close',
3305                 html : '&times'
3306             });
3307         }
3308
3309         header.push(title);
3310
3311         if (this.allow_close && Roo.bootstrap.version == 4) {
3312             header.push({
3313                 tag: 'button',
3314                 cls : 'close',
3315                 html : '&times'
3316             });
3317         }
3318         
3319         var size = '';
3320
3321         if(this.size.length){
3322             size = 'modal-' + this.size;
3323         }
3324         
3325         var footer = Roo.bootstrap.version == 3 ?
3326             {
3327                 cls : 'modal-footer',
3328                 cn : [
3329                     {
3330                         tag: 'div',
3331                         cls: 'btn-' + this.buttonPosition
3332                     }
3333                 ]
3334
3335             } :
3336             {  // BS4 uses mr-auto on left buttons....
3337                 cls : 'modal-footer'
3338             };
3339
3340             
3341
3342         
3343         
3344         var modal = {
3345             cls: "modal",
3346              cn : [
3347                 {
3348                     cls: "modal-dialog " + size,
3349                     cn : [
3350                         {
3351                             cls : "modal-content",
3352                             cn : [
3353                                 {
3354                                     cls : 'modal-header',
3355                                     cn : header
3356                                 },
3357                                 bdy,
3358                                 footer
3359                             ]
3360
3361                         }
3362                     ]
3363
3364                 }
3365             ]
3366         };
3367
3368         if(this.animate){
3369             modal.cls += ' fade';
3370         }
3371
3372         return modal;
3373
3374     },
3375     getChildContainer : function() {
3376
3377          return this.bodyEl;
3378
3379     },
3380     getButtonContainer : function() {
3381         
3382          return Roo.bootstrap.version == 4 ?
3383             this.el.select('.modal-footer',true).first()
3384             : this.el.select('.modal-footer div',true).first();
3385
3386     },
3387     initEvents : function()
3388     {
3389         if (this.allow_close) {
3390             this.closeEl.on('click', this.hide, this);
3391         }
3392         Roo.EventManager.onWindowResize(this.resize, this, true);
3393
3394
3395     },
3396   
3397
3398     resize : function()
3399     {
3400         this.maskEl.setSize(
3401             Roo.lib.Dom.getViewWidth(true),
3402             Roo.lib.Dom.getViewHeight(true)
3403         );
3404         
3405         if (this.fitwindow) {
3406             
3407            
3408             this.setSize(
3409                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
3410                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
3411             );
3412             return;
3413         }
3414         
3415         if(this.max_width !== 0) {
3416             
3417             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
3418             
3419             if(this.height) {
3420                 this.setSize(w, this.height);
3421                 return;
3422             }
3423             
3424             if(this.max_height) {
3425                 this.setSize(w,Math.min(
3426                     this.max_height,
3427                     Roo.lib.Dom.getViewportHeight(true) - 60
3428                 ));
3429                 
3430                 return;
3431             }
3432             
3433             if(!this.fit_content) {
3434                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
3435                 return;
3436             }
3437             
3438             this.setSize(w, Math.min(
3439                 60 +
3440                 this.headerEl.getHeight() + 
3441                 this.footerEl.getHeight() + 
3442                 this.getChildHeight(this.bodyEl.dom.childNodes),
3443                 Roo.lib.Dom.getViewportHeight(true) - 60)
3444             );
3445         }
3446         
3447     },
3448
3449     setSize : function(w,h)
3450     {
3451         if (!w && !h) {
3452             return;
3453         }
3454         
3455         this.resizeTo(w,h);
3456     },
3457
3458     show : function() {
3459
3460         if (!this.rendered) {
3461             this.render();
3462         }
3463
3464         //this.el.setStyle('display', 'block');
3465         this.el.removeClass('hideing');
3466         this.el.dom.style.display='block';
3467         
3468         Roo.get(document.body).addClass('modal-open');
3469  
3470         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
3471             
3472             (function(){
3473                 this.el.addClass('show');
3474                 this.el.addClass('in');
3475             }).defer(50, this);
3476         }else{
3477             this.el.addClass('show');
3478             this.el.addClass('in');
3479         }
3480
3481         // not sure how we can show data in here..
3482         //if (this.tmpl) {
3483         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3484         //}
3485
3486         Roo.get(document.body).addClass("x-body-masked");
3487         
3488         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3489         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3490         this.maskEl.dom.style.display = 'block';
3491         this.maskEl.addClass('show');
3492         
3493         
3494         this.resize();
3495         
3496         this.fireEvent('show', this);
3497
3498         // set zindex here - otherwise it appears to be ignored...
3499         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3500
3501         (function () {
3502             this.items.forEach( function(e) {
3503                 e.layout ? e.layout() : false;
3504
3505             });
3506         }).defer(100,this);
3507
3508     },
3509     hide : function()
3510     {
3511         if(this.fireEvent("beforehide", this) !== false){
3512             
3513             this.maskEl.removeClass('show');
3514             
3515             this.maskEl.dom.style.display = '';
3516             Roo.get(document.body).removeClass("x-body-masked");
3517             this.el.removeClass('in');
3518             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3519
3520             if(this.animate){ // why
3521                 this.el.addClass('hideing');
3522                 this.el.removeClass('show');
3523                 (function(){
3524                     if (!this.el.hasClass('hideing')) {
3525                         return; // it's been shown again...
3526                     }
3527                     
3528                     this.el.dom.style.display='';
3529
3530                     Roo.get(document.body).removeClass('modal-open');
3531                     this.el.removeClass('hideing');
3532                 }).defer(150,this);
3533                 
3534             }else{
3535                 this.el.removeClass('show');
3536                 this.el.dom.style.display='';
3537                 Roo.get(document.body).removeClass('modal-open');
3538
3539             }
3540             this.fireEvent('hide', this);
3541         }
3542     },
3543     isVisible : function()
3544     {
3545         
3546         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3547         
3548     },
3549
3550     addButton : function(str, cb)
3551     {
3552
3553
3554         var b = Roo.apply({}, { html : str } );
3555         b.xns = b.xns || Roo.bootstrap;
3556         b.xtype = b.xtype || 'Button';
3557         if (typeof(b.listeners) == 'undefined') {
3558             b.listeners = { click : cb.createDelegate(this)  };
3559         }
3560
3561         var btn = Roo.factory(b);
3562
3563         btn.render(this.getButtonContainer());
3564
3565         return btn;
3566
3567     },
3568
3569     setDefaultButton : function(btn)
3570     {
3571         //this.el.select('.modal-footer').()
3572     },
3573
3574     resizeTo: function(w,h)
3575     {
3576         this.dialogEl.setWidth(w);
3577         
3578         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
3579
3580         this.bodyEl.setHeight(h - diff);
3581         
3582         this.fireEvent('resize', this);
3583     },
3584     
3585     setContentSize  : function(w, h)
3586     {
3587
3588     },
3589     onButtonClick: function(btn,e)
3590     {
3591         //Roo.log([a,b,c]);
3592         this.fireEvent('btnclick', btn.name, e);
3593     },
3594      /**
3595      * Set the title of the Dialog
3596      * @param {String} str new Title
3597      */
3598     setTitle: function(str) {
3599         this.titleEl.dom.innerHTML = str;
3600     },
3601     /**
3602      * Set the body of the Dialog
3603      * @param {String} str new Title
3604      */
3605     setBody: function(str) {
3606         this.bodyEl.dom.innerHTML = str;
3607     },
3608     /**
3609      * Set the body of the Dialog using the template
3610      * @param {Obj} data - apply this data to the template and replace the body contents.
3611      */
3612     applyBody: function(obj)
3613     {
3614         if (!this.tmpl) {
3615             Roo.log("Error - using apply Body without a template");
3616             //code
3617         }
3618         this.tmpl.overwrite(this.bodyEl, obj);
3619     },
3620     
3621     getChildHeight : function(child_nodes)
3622     {
3623         if(
3624             !child_nodes ||
3625             child_nodes.length == 0
3626         ) {
3627             return;
3628         }
3629         
3630         var child_height = 0;
3631         
3632         for(var i = 0; i < child_nodes.length; i++) {
3633             
3634             /*
3635             * for modal with tabs...
3636             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3637                 
3638                 var layout_childs = child_nodes[i].childNodes;
3639                 
3640                 for(var j = 0; j < layout_childs.length; j++) {
3641                     
3642                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3643                         
3644                         var layout_body_childs = layout_childs[j].childNodes;
3645                         
3646                         for(var k = 0; k < layout_body_childs.length; k++) {
3647                             
3648                             if(layout_body_childs[k].classList.contains('navbar')) {
3649                                 child_height += layout_body_childs[k].offsetHeight;
3650                                 continue;
3651                             }
3652                             
3653                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3654                                 
3655                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3656                                 
3657                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3658                                     
3659                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3660                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3661                                         continue;
3662                                     }
3663                                     
3664                                 }
3665                                 
3666                             }
3667                             
3668                         }
3669                     }
3670                 }
3671                 continue;
3672             }
3673             */
3674             
3675             child_height += child_nodes[i].offsetHeight;
3676             // Roo.log(child_nodes[i].offsetHeight);
3677         }
3678         
3679         return child_height;
3680     }
3681
3682 });
3683
3684
3685 Roo.apply(Roo.bootstrap.Modal,  {
3686     /**
3687          * Button config that displays a single OK button
3688          * @type Object
3689          */
3690         OK :  [{
3691             name : 'ok',
3692             weight : 'primary',
3693             html : 'OK'
3694         }],
3695         /**
3696          * Button config that displays Yes and No buttons
3697          * @type Object
3698          */
3699         YESNO : [
3700             {
3701                 name  : 'no',
3702                 html : 'No'
3703             },
3704             {
3705                 name  :'yes',
3706                 weight : 'primary',
3707                 html : 'Yes'
3708             }
3709         ],
3710
3711         /**
3712          * Button config that displays OK and Cancel buttons
3713          * @type Object
3714          */
3715         OKCANCEL : [
3716             {
3717                name : 'cancel',
3718                 html : 'Cancel'
3719             },
3720             {
3721                 name : 'ok',
3722                 weight : 'primary',
3723                 html : 'OK'
3724             }
3725         ],
3726         /**
3727          * Button config that displays Yes, No and Cancel buttons
3728          * @type Object
3729          */
3730         YESNOCANCEL : [
3731             {
3732                 name : 'yes',
3733                 weight : 'primary',
3734                 html : 'Yes'
3735             },
3736             {
3737                 name : 'no',
3738                 html : 'No'
3739             },
3740             {
3741                 name : 'cancel',
3742                 html : 'Cancel'
3743             }
3744         ],
3745         
3746         zIndex : 10001
3747 });
3748 /*
3749  * - LGPL
3750  *
3751  * messagebox - can be used as a replace
3752  * 
3753  */
3754 /**
3755  * @class Roo.MessageBox
3756  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3757  * Example usage:
3758  *<pre><code>
3759 // Basic alert:
3760 Roo.Msg.alert('Status', 'Changes saved successfully.');
3761
3762 // Prompt for user data:
3763 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3764     if (btn == 'ok'){
3765         // process text value...
3766     }
3767 });
3768
3769 // Show a dialog using config options:
3770 Roo.Msg.show({
3771    title:'Save Changes?',
3772    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3773    buttons: Roo.Msg.YESNOCANCEL,
3774    fn: processResult,
3775    animEl: 'elId'
3776 });
3777 </code></pre>
3778  * @singleton
3779  */
3780 Roo.bootstrap.MessageBox = function(){
3781     var dlg, opt, mask, waitTimer;
3782     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3783     var buttons, activeTextEl, bwidth;
3784
3785     
3786     // private
3787     var handleButton = function(button){
3788         dlg.hide();
3789         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3790     };
3791
3792     // private
3793     var handleHide = function(){
3794         if(opt && opt.cls){
3795             dlg.el.removeClass(opt.cls);
3796         }
3797         //if(waitTimer){
3798         //    Roo.TaskMgr.stop(waitTimer);
3799         //    waitTimer = null;
3800         //}
3801     };
3802
3803     // private
3804     var updateButtons = function(b){
3805         var width = 0;
3806         if(!b){
3807             buttons["ok"].hide();
3808             buttons["cancel"].hide();
3809             buttons["yes"].hide();
3810             buttons["no"].hide();
3811             dlg.footerEl.hide();
3812             
3813             return width;
3814         }
3815         dlg.footerEl.show();
3816         for(var k in buttons){
3817             if(typeof buttons[k] != "function"){
3818                 if(b[k]){
3819                     buttons[k].show();
3820                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3821                     width += buttons[k].el.getWidth()+15;
3822                 }else{
3823                     buttons[k].hide();
3824                 }
3825             }
3826         }
3827         return width;
3828     };
3829
3830     // private
3831     var handleEsc = function(d, k, e){
3832         if(opt && opt.closable !== false){
3833             dlg.hide();
3834         }
3835         if(e){
3836             e.stopEvent();
3837         }
3838     };
3839
3840     return {
3841         /**
3842          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3843          * @return {Roo.BasicDialog} The BasicDialog element
3844          */
3845         getDialog : function(){
3846            if(!dlg){
3847                 dlg = new Roo.bootstrap.Modal( {
3848                     //draggable: true,
3849                     //resizable:false,
3850                     //constraintoviewport:false,
3851                     //fixedcenter:true,
3852                     //collapsible : false,
3853                     //shim:true,
3854                     //modal: true,
3855                 //    width: 'auto',
3856                   //  height:100,
3857                     //buttonAlign:"center",
3858                     closeClick : function(){
3859                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3860                             handleButton("no");
3861                         }else{
3862                             handleButton("cancel");
3863                         }
3864                     }
3865                 });
3866                 dlg.render();
3867                 dlg.on("hide", handleHide);
3868                 mask = dlg.mask;
3869                 //dlg.addKeyListener(27, handleEsc);
3870                 buttons = {};
3871                 this.buttons = buttons;
3872                 var bt = this.buttonText;
3873                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3874                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3875                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3876                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3877                 //Roo.log(buttons);
3878                 bodyEl = dlg.bodyEl.createChild({
3879
3880                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3881                         '<textarea class="roo-mb-textarea"></textarea>' +
3882                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3883                 });
3884                 msgEl = bodyEl.dom.firstChild;
3885                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3886                 textboxEl.enableDisplayMode();
3887                 textboxEl.addKeyListener([10,13], function(){
3888                     if(dlg.isVisible() && opt && opt.buttons){
3889                         if(opt.buttons.ok){
3890                             handleButton("ok");
3891                         }else if(opt.buttons.yes){
3892                             handleButton("yes");
3893                         }
3894                     }
3895                 });
3896                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3897                 textareaEl.enableDisplayMode();
3898                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3899                 progressEl.enableDisplayMode();
3900                 
3901                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3902                 var pf = progressEl.dom.firstChild;
3903                 if (pf) {
3904                     pp = Roo.get(pf.firstChild);
3905                     pp.setHeight(pf.offsetHeight);
3906                 }
3907                 
3908             }
3909             return dlg;
3910         },
3911
3912         /**
3913          * Updates the message box body text
3914          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3915          * the XHTML-compliant non-breaking space character '&amp;#160;')
3916          * @return {Roo.MessageBox} This message box
3917          */
3918         updateText : function(text)
3919         {
3920             if(!dlg.isVisible() && !opt.width){
3921                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3922                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3923             }
3924             msgEl.innerHTML = text || '&#160;';
3925       
3926             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3927             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3928             var w = Math.max(
3929                     Math.min(opt.width || cw , this.maxWidth), 
3930                     Math.max(opt.minWidth || this.minWidth, bwidth)
3931             );
3932             if(opt.prompt){
3933                 activeTextEl.setWidth(w);
3934             }
3935             if(dlg.isVisible()){
3936                 dlg.fixedcenter = false;
3937             }
3938             // to big, make it scroll. = But as usual stupid IE does not support
3939             // !important..
3940             
3941             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3942                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3943                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3944             } else {
3945                 bodyEl.dom.style.height = '';
3946                 bodyEl.dom.style.overflowY = '';
3947             }
3948             if (cw > w) {
3949                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3950             } else {
3951                 bodyEl.dom.style.overflowX = '';
3952             }
3953             
3954             dlg.setContentSize(w, bodyEl.getHeight());
3955             if(dlg.isVisible()){
3956                 dlg.fixedcenter = true;
3957             }
3958             return this;
3959         },
3960
3961         /**
3962          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3963          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3964          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3965          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3966          * @return {Roo.MessageBox} This message box
3967          */
3968         updateProgress : function(value, text){
3969             if(text){
3970                 this.updateText(text);
3971             }
3972             
3973             if (pp) { // weird bug on my firefox - for some reason this is not defined
3974                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3975                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3976             }
3977             return this;
3978         },        
3979
3980         /**
3981          * Returns true if the message box is currently displayed
3982          * @return {Boolean} True if the message box is visible, else false
3983          */
3984         isVisible : function(){
3985             return dlg && dlg.isVisible();  
3986         },
3987
3988         /**
3989          * Hides the message box if it is displayed
3990          */
3991         hide : function(){
3992             if(this.isVisible()){
3993                 dlg.hide();
3994             }  
3995         },
3996
3997         /**
3998          * Displays a new message box, or reinitializes an existing message box, based on the config options
3999          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4000          * The following config object properties are supported:
4001          * <pre>
4002 Property    Type             Description
4003 ----------  ---------------  ------------------------------------------------------------------------------------
4004 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4005                                    closes (defaults to undefined)
4006 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4007                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4008 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4009                                    progress and wait dialogs will ignore this property and always hide the
4010                                    close button as they can only be closed programmatically.
4011 cls               String           A custom CSS class to apply to the message box element
4012 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4013                                    displayed (defaults to 75)
4014 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4015                                    function will be btn (the name of the button that was clicked, if applicable,
4016                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4017                                    Progress and wait dialogs will ignore this option since they do not respond to
4018                                    user actions and can only be closed programmatically, so any required function
4019                                    should be called by the same code after it closes the dialog.
4020 icon              String           A CSS class that provides a background image to be used as an icon for
4021                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4022 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4023 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4024 modal             Boolean          False to allow user interaction with the page while the message box is
4025                                    displayed (defaults to true)
4026 msg               String           A string that will replace the existing message box body text (defaults
4027                                    to the XHTML-compliant non-breaking space character '&#160;')
4028 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4029 progress          Boolean          True to display a progress bar (defaults to false)
4030 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4031 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4032 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4033 title             String           The title text
4034 value             String           The string value to set into the active textbox element if displayed
4035 wait              Boolean          True to display a progress bar (defaults to false)
4036 width             Number           The width of the dialog in pixels
4037 </pre>
4038          *
4039          * Example usage:
4040          * <pre><code>
4041 Roo.Msg.show({
4042    title: 'Address',
4043    msg: 'Please enter your address:',
4044    width: 300,
4045    buttons: Roo.MessageBox.OKCANCEL,
4046    multiline: true,
4047    fn: saveAddress,
4048    animEl: 'addAddressBtn'
4049 });
4050 </code></pre>
4051          * @param {Object} config Configuration options
4052          * @return {Roo.MessageBox} This message box
4053          */
4054         show : function(options)
4055         {
4056             
4057             // this causes nightmares if you show one dialog after another
4058             // especially on callbacks..
4059              
4060             if(this.isVisible()){
4061                 
4062                 this.hide();
4063                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4064                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4065                 Roo.log("New Dialog Message:" +  options.msg )
4066                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4067                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4068                 
4069             }
4070             var d = this.getDialog();
4071             opt = options;
4072             d.setTitle(opt.title || "&#160;");
4073             d.closeEl.setDisplayed(opt.closable !== false);
4074             activeTextEl = textboxEl;
4075             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4076             if(opt.prompt){
4077                 if(opt.multiline){
4078                     textboxEl.hide();
4079                     textareaEl.show();
4080                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4081                         opt.multiline : this.defaultTextHeight);
4082                     activeTextEl = textareaEl;
4083                 }else{
4084                     textboxEl.show();
4085                     textareaEl.hide();
4086                 }
4087             }else{
4088                 textboxEl.hide();
4089                 textareaEl.hide();
4090             }
4091             progressEl.setDisplayed(opt.progress === true);
4092             if (opt.progress) {
4093                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4094             }
4095             this.updateProgress(0);
4096             activeTextEl.dom.value = opt.value || "";
4097             if(opt.prompt){
4098                 dlg.setDefaultButton(activeTextEl);
4099             }else{
4100                 var bs = opt.buttons;
4101                 var db = null;
4102                 if(bs && bs.ok){
4103                     db = buttons["ok"];
4104                 }else if(bs && bs.yes){
4105                     db = buttons["yes"];
4106                 }
4107                 dlg.setDefaultButton(db);
4108             }
4109             bwidth = updateButtons(opt.buttons);
4110             this.updateText(opt.msg);
4111             if(opt.cls){
4112                 d.el.addClass(opt.cls);
4113             }
4114             d.proxyDrag = opt.proxyDrag === true;
4115             d.modal = opt.modal !== false;
4116             d.mask = opt.modal !== false ? mask : false;
4117             if(!d.isVisible()){
4118                 // force it to the end of the z-index stack so it gets a cursor in FF
4119                 document.body.appendChild(dlg.el.dom);
4120                 d.animateTarget = null;
4121                 d.show(options.animEl);
4122             }
4123             return this;
4124         },
4125
4126         /**
4127          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
4128          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
4129          * and closing the message box when the process is complete.
4130          * @param {String} title The title bar text
4131          * @param {String} msg The message box body text
4132          * @return {Roo.MessageBox} This message box
4133          */
4134         progress : function(title, msg){
4135             this.show({
4136                 title : title,
4137                 msg : msg,
4138                 buttons: false,
4139                 progress:true,
4140                 closable:false,
4141                 minWidth: this.minProgressWidth,
4142                 modal : true
4143             });
4144             return this;
4145         },
4146
4147         /**
4148          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
4149          * If a callback function is passed it will be called after the user clicks the button, and the
4150          * id of the button that was clicked will be passed as the only parameter to the callback
4151          * (could also be the top-right close button).
4152          * @param {String} title The title bar text
4153          * @param {String} msg The message box body text
4154          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4155          * @param {Object} scope (optional) The scope of the callback function
4156          * @return {Roo.MessageBox} This message box
4157          */
4158         alert : function(title, msg, fn, scope)
4159         {
4160             this.show({
4161                 title : title,
4162                 msg : msg,
4163                 buttons: this.OK,
4164                 fn: fn,
4165                 closable : false,
4166                 scope : scope,
4167                 modal : true
4168             });
4169             return this;
4170         },
4171
4172         /**
4173          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
4174          * interaction while waiting for a long-running process to complete that does not have defined intervals.
4175          * You are responsible for closing the message box when the process is complete.
4176          * @param {String} msg The message box body text
4177          * @param {String} title (optional) The title bar text
4178          * @return {Roo.MessageBox} This message box
4179          */
4180         wait : function(msg, title){
4181             this.show({
4182                 title : title,
4183                 msg : msg,
4184                 buttons: false,
4185                 closable:false,
4186                 progress:true,
4187                 modal:true,
4188                 width:300,
4189                 wait:true
4190             });
4191             waitTimer = Roo.TaskMgr.start({
4192                 run: function(i){
4193                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
4194                 },
4195                 interval: 1000
4196             });
4197             return this;
4198         },
4199
4200         /**
4201          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
4202          * If a callback function is passed it will be called after the user clicks either button, and the id of the
4203          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
4204          * @param {String} title The title bar text
4205          * @param {String} msg The message box body text
4206          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4207          * @param {Object} scope (optional) The scope of the callback function
4208          * @return {Roo.MessageBox} This message box
4209          */
4210         confirm : function(title, msg, fn, scope){
4211             this.show({
4212                 title : title,
4213                 msg : msg,
4214                 buttons: this.YESNO,
4215                 fn: fn,
4216                 scope : scope,
4217                 modal : true
4218             });
4219             return this;
4220         },
4221
4222         /**
4223          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
4224          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
4225          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
4226          * (could also be the top-right close button) and the text that was entered will be passed as the two
4227          * parameters to the callback.
4228          * @param {String} title The title bar text
4229          * @param {String} msg The message box body text
4230          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4231          * @param {Object} scope (optional) The scope of the callback function
4232          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
4233          * property, or the height in pixels to create the textbox (defaults to false / single-line)
4234          * @return {Roo.MessageBox} This message box
4235          */
4236         prompt : function(title, msg, fn, scope, multiline){
4237             this.show({
4238                 title : title,
4239                 msg : msg,
4240                 buttons: this.OKCANCEL,
4241                 fn: fn,
4242                 minWidth:250,
4243                 scope : scope,
4244                 prompt:true,
4245                 multiline: multiline,
4246                 modal : true
4247             });
4248             return this;
4249         },
4250
4251         /**
4252          * Button config that displays a single OK button
4253          * @type Object
4254          */
4255         OK : {ok:true},
4256         /**
4257          * Button config that displays Yes and No buttons
4258          * @type Object
4259          */
4260         YESNO : {yes:true, no:true},
4261         /**
4262          * Button config that displays OK and Cancel buttons
4263          * @type Object
4264          */
4265         OKCANCEL : {ok:true, cancel:true},
4266         /**
4267          * Button config that displays Yes, No and Cancel buttons
4268          * @type Object
4269          */
4270         YESNOCANCEL : {yes:true, no:true, cancel:true},
4271
4272         /**
4273          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
4274          * @type Number
4275          */
4276         defaultTextHeight : 75,
4277         /**
4278          * The maximum width in pixels of the message box (defaults to 600)
4279          * @type Number
4280          */
4281         maxWidth : 600,
4282         /**
4283          * The minimum width in pixels of the message box (defaults to 100)
4284          * @type Number
4285          */
4286         minWidth : 100,
4287         /**
4288          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
4289          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
4290          * @type Number
4291          */
4292         minProgressWidth : 250,
4293         /**
4294          * An object containing the default button text strings that can be overriden for localized language support.
4295          * Supported properties are: ok, cancel, yes and no.
4296          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
4297          * @type Object
4298          */
4299         buttonText : {
4300             ok : "OK",
4301             cancel : "Cancel",
4302             yes : "Yes",
4303             no : "No"
4304         }
4305     };
4306 }();
4307
4308 /**
4309  * Shorthand for {@link Roo.MessageBox}
4310  */
4311 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
4312 Roo.Msg = Roo.Msg || Roo.MessageBox;
4313 /*
4314  * - LGPL
4315  *
4316  * navbar
4317  * 
4318  */
4319
4320 /**
4321  * @class Roo.bootstrap.Navbar
4322  * @extends Roo.bootstrap.Component
4323  * Bootstrap Navbar class
4324
4325  * @constructor
4326  * Create a new Navbar
4327  * @param {Object} config The config object
4328  */
4329
4330
4331 Roo.bootstrap.Navbar = function(config){
4332     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
4333     this.addEvents({
4334         // raw events
4335         /**
4336          * @event beforetoggle
4337          * Fire before toggle the menu
4338          * @param {Roo.EventObject} e
4339          */
4340         "beforetoggle" : true
4341     });
4342 };
4343
4344 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
4345     
4346     
4347    
4348     // private
4349     navItems : false,
4350     loadMask : false,
4351     
4352     
4353     getAutoCreate : function(){
4354         
4355         
4356         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
4357         
4358     },
4359     
4360     initEvents :function ()
4361     {
4362         //Roo.log(this.el.select('.navbar-toggle',true));
4363         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
4364         
4365         var mark = {
4366             tag: "div",
4367             cls:"x-dlg-mask"
4368         };
4369         
4370         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
4371         
4372         var size = this.el.getSize();
4373         this.maskEl.setSize(size.width, size.height);
4374         this.maskEl.enableDisplayMode("block");
4375         this.maskEl.hide();
4376         
4377         if(this.loadMask){
4378             this.maskEl.show();
4379         }
4380     },
4381     
4382     
4383     getChildContainer : function()
4384     {
4385         if (this.el && this.el.select('.collapse').getCount()) {
4386             return this.el.select('.collapse',true).first();
4387         }
4388         
4389         return this.el;
4390     },
4391     
4392     mask : function()
4393     {
4394         this.maskEl.show();
4395     },
4396     
4397     unmask : function()
4398     {
4399         this.maskEl.hide();
4400     },
4401     onToggle : function()
4402     {
4403         
4404         if(this.fireEvent('beforetoggle', this) === false){
4405             return;
4406         }
4407         var ce = this.el.select('.navbar-collapse',true).first();
4408       
4409         if (!ce.hasClass('show')) {
4410            this.expand();
4411         } else {
4412             this.collapse();
4413         }
4414         
4415         
4416     
4417     },
4418     /**
4419      * Expand the navbar pulldown 
4420      */
4421     expand : function ()
4422     {
4423        
4424         var ce = this.el.select('.navbar-collapse',true).first();
4425         if (ce.hasClass('collapsing')) {
4426             return;
4427         }
4428         ce.dom.style.height = '';
4429                // show it...
4430         ce.addClass('in'); // old...
4431         ce.removeClass('collapse');
4432         ce.addClass('show');
4433         var h = ce.getHeight();
4434         Roo.log(h);
4435         ce.removeClass('show');
4436         // at this point we should be able to see it..
4437         ce.addClass('collapsing');
4438         
4439         ce.setHeight(0); // resize it ...
4440         ce.on('transitionend', function() {
4441             //Roo.log('done transition');
4442             ce.removeClass('collapsing');
4443             ce.addClass('show');
4444             ce.removeClass('collapse');
4445
4446             ce.dom.style.height = '';
4447         }, this, { single: true} );
4448         ce.setHeight(h);
4449         ce.dom.scrollTop = 0;
4450     },
4451     /**
4452      * Collapse the navbar pulldown 
4453      */
4454     collapse : function()
4455     {
4456          var ce = this.el.select('.navbar-collapse',true).first();
4457        
4458         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
4459             // it's collapsed or collapsing..
4460             return;
4461         }
4462         ce.removeClass('in'); // old...
4463         ce.setHeight(ce.getHeight());
4464         ce.removeClass('show');
4465         ce.addClass('collapsing');
4466         
4467         ce.on('transitionend', function() {
4468             ce.dom.style.height = '';
4469             ce.removeClass('collapsing');
4470             ce.addClass('collapse');
4471         }, this, { single: true} );
4472         ce.setHeight(0);
4473     }
4474     
4475     
4476     
4477 });
4478
4479
4480
4481  
4482
4483  /*
4484  * - LGPL
4485  *
4486  * navbar
4487  * 
4488  */
4489
4490 /**
4491  * @class Roo.bootstrap.NavSimplebar
4492  * @extends Roo.bootstrap.Navbar
4493  * Bootstrap Sidebar class
4494  *
4495  * @cfg {Boolean} inverse is inverted color
4496  * 
4497  * @cfg {String} type (nav | pills | tabs)
4498  * @cfg {Boolean} arrangement stacked | justified
4499  * @cfg {String} align (left | right) alignment
4500  * 
4501  * @cfg {Boolean} main (true|false) main nav bar? default false
4502  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4503  * 
4504  * @cfg {String} tag (header|footer|nav|div) default is nav 
4505
4506  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4507  * 
4508  * 
4509  * @constructor
4510  * Create a new Sidebar
4511  * @param {Object} config The config object
4512  */
4513
4514
4515 Roo.bootstrap.NavSimplebar = function(config){
4516     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4517 };
4518
4519 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4520     
4521     inverse: false,
4522     
4523     type: false,
4524     arrangement: '',
4525     align : false,
4526     
4527     weight : 'light',
4528     
4529     main : false,
4530     
4531     
4532     tag : false,
4533     
4534     
4535     getAutoCreate : function(){
4536         
4537         
4538         var cfg = {
4539             tag : this.tag || 'div',
4540             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
4541         };
4542         if (['light','white'].indexOf(this.weight) > -1) {
4543             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4544         }
4545         cfg.cls += ' bg-' + this.weight;
4546         
4547         if (this.inverse) {
4548             cfg.cls += ' navbar-inverse';
4549             
4550         }
4551         
4552         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4553         
4554         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
4555             return cfg;
4556         }
4557         
4558         
4559     
4560         
4561         cfg.cn = [
4562             {
4563                 cls: 'nav nav-' + this.xtype,
4564                 tag : 'ul'
4565             }
4566         ];
4567         
4568          
4569         this.type = this.type || 'nav';
4570         if (['tabs','pills'].indexOf(this.type) != -1) {
4571             cfg.cn[0].cls += ' nav-' + this.type
4572         
4573         
4574         } else {
4575             if (this.type!=='nav') {
4576                 Roo.log('nav type must be nav/tabs/pills')
4577             }
4578             cfg.cn[0].cls += ' navbar-nav'
4579         }
4580         
4581         
4582         
4583         
4584         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4585             cfg.cn[0].cls += ' nav-' + this.arrangement;
4586         }
4587         
4588         
4589         if (this.align === 'right') {
4590             cfg.cn[0].cls += ' navbar-right';
4591         }
4592         
4593         
4594         
4595         
4596         return cfg;
4597     
4598         
4599     }
4600     
4601     
4602     
4603 });
4604
4605
4606
4607  
4608
4609  
4610        /*
4611  * - LGPL
4612  *
4613  * navbar
4614  * navbar-fixed-top
4615  * navbar-expand-md  fixed-top 
4616  */
4617
4618 /**
4619  * @class Roo.bootstrap.NavHeaderbar
4620  * @extends Roo.bootstrap.NavSimplebar
4621  * Bootstrap Sidebar class
4622  *
4623  * @cfg {String} brand what is brand
4624  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4625  * @cfg {String} brand_href href of the brand
4626  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4627  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4628  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4629  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4630  * 
4631  * @constructor
4632  * Create a new Sidebar
4633  * @param {Object} config The config object
4634  */
4635
4636
4637 Roo.bootstrap.NavHeaderbar = function(config){
4638     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4639       
4640 };
4641
4642 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4643     
4644     position: '',
4645     brand: '',
4646     brand_href: false,
4647     srButton : true,
4648     autohide : false,
4649     desktopCenter : false,
4650    
4651     
4652     getAutoCreate : function(){
4653         
4654         var   cfg = {
4655             tag: this.nav || 'nav',
4656             cls: 'navbar navbar-expand-md',
4657             role: 'navigation',
4658             cn: []
4659         };
4660         
4661         var cn = cfg.cn;
4662         if (this.desktopCenter) {
4663             cn.push({cls : 'container', cn : []});
4664             cn = cn[0].cn;
4665         }
4666         
4667         if(this.srButton){
4668             var btn = {
4669                 tag: 'button',
4670                 type: 'button',
4671                 cls: 'navbar-toggle navbar-toggler',
4672                 'data-toggle': 'collapse',
4673                 cn: [
4674                     {
4675                         tag: 'span',
4676                         cls: 'sr-only',
4677                         html: 'Toggle navigation'
4678                     },
4679                     {
4680                         tag: 'span',
4681                         cls: 'icon-bar navbar-toggler-icon'
4682                     },
4683                     {
4684                         tag: 'span',
4685                         cls: 'icon-bar'
4686                     },
4687                     {
4688                         tag: 'span',
4689                         cls: 'icon-bar'
4690                     }
4691                 ]
4692             };
4693             
4694             cn.push( Roo.bootstrap.version == 4 ? btn : {
4695                 tag: 'div',
4696                 cls: 'navbar-header',
4697                 cn: [
4698                     btn
4699                 ]
4700             });
4701         }
4702         
4703         cn.push({
4704             tag: 'div',
4705             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
4706             cn : []
4707         });
4708         
4709         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4710         
4711         if (['light','white'].indexOf(this.weight) > -1) {
4712             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4713         }
4714         cfg.cls += ' bg-' + this.weight;
4715         
4716         
4717         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4718             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4719             
4720             // tag can override this..
4721             
4722             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4723         }
4724         
4725         if (this.brand !== '') {
4726             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4727             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4728                 tag: 'a',
4729                 href: this.brand_href ? this.brand_href : '#',
4730                 cls: 'navbar-brand',
4731                 cn: [
4732                 this.brand
4733                 ]
4734             });
4735         }
4736         
4737         if(this.main){
4738             cfg.cls += ' main-nav';
4739         }
4740         
4741         
4742         return cfg;
4743
4744         
4745     },
4746     getHeaderChildContainer : function()
4747     {
4748         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4749             return this.el.select('.navbar-header',true).first();
4750         }
4751         
4752         return this.getChildContainer();
4753     },
4754     
4755     getChildContainer : function()
4756     {
4757          
4758         return this.el.select('.roo-navbar-collapse',true).first();
4759          
4760         
4761     },
4762     
4763     initEvents : function()
4764     {
4765         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4766         
4767         if (this.autohide) {
4768             
4769             var prevScroll = 0;
4770             var ft = this.el;
4771             
4772             Roo.get(document).on('scroll',function(e) {
4773                 var ns = Roo.get(document).getScroll().top;
4774                 var os = prevScroll;
4775                 prevScroll = ns;
4776                 
4777                 if(ns > os){
4778                     ft.removeClass('slideDown');
4779                     ft.addClass('slideUp');
4780                     return;
4781                 }
4782                 ft.removeClass('slideUp');
4783                 ft.addClass('slideDown');
4784                  
4785               
4786           },this);
4787         }
4788     }    
4789     
4790 });
4791
4792
4793
4794  
4795
4796  /*
4797  * - LGPL
4798  *
4799  * navbar
4800  * 
4801  */
4802
4803 /**
4804  * @class Roo.bootstrap.NavSidebar
4805  * @extends Roo.bootstrap.Navbar
4806  * Bootstrap Sidebar class
4807  * 
4808  * @constructor
4809  * Create a new Sidebar
4810  * @param {Object} config The config object
4811  */
4812
4813
4814 Roo.bootstrap.NavSidebar = function(config){
4815     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4816 };
4817
4818 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4819     
4820     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4821     
4822     getAutoCreate : function(){
4823         
4824         
4825         return  {
4826             tag: 'div',
4827             cls: 'sidebar sidebar-nav'
4828         };
4829     
4830         
4831     }
4832     
4833     
4834     
4835 });
4836
4837
4838
4839  
4840
4841  /*
4842  * - LGPL
4843  *
4844  * nav group
4845  * 
4846  */
4847
4848 /**
4849  * @class Roo.bootstrap.NavGroup
4850  * @extends Roo.bootstrap.Component
4851  * Bootstrap NavGroup class
4852  * @cfg {String} align (left|right)
4853  * @cfg {Boolean} inverse
4854  * @cfg {String} type (nav|pills|tab) default nav
4855  * @cfg {String} navId - reference Id for navbar.
4856
4857  * 
4858  * @constructor
4859  * Create a new nav group
4860  * @param {Object} config The config object
4861  */
4862
4863 Roo.bootstrap.NavGroup = function(config){
4864     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4865     this.navItems = [];
4866    
4867     Roo.bootstrap.NavGroup.register(this);
4868      this.addEvents({
4869         /**
4870              * @event changed
4871              * Fires when the active item changes
4872              * @param {Roo.bootstrap.NavGroup} this
4873              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4874              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4875          */
4876         'changed': true
4877      });
4878     
4879 };
4880
4881 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4882     
4883     align: '',
4884     inverse: false,
4885     form: false,
4886     type: 'nav',
4887     navId : '',
4888     // private
4889     
4890     navItems : false, 
4891     
4892     getAutoCreate : function()
4893     {
4894         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4895         
4896         cfg = {
4897             tag : 'ul',
4898             cls: 'nav' 
4899         };
4900         if (Roo.bootstrap.version == 4) {
4901             if (['tabs','pills'].indexOf(this.type) != -1) {
4902                 cfg.cls += ' nav-' + this.type; 
4903             } else {
4904                 // trying to remove so header bar can right align top?
4905                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
4906                     // do not use on header bar... 
4907                     cfg.cls += ' navbar-nav';
4908                 }
4909             }
4910             
4911         } else {
4912             if (['tabs','pills'].indexOf(this.type) != -1) {
4913                 cfg.cls += ' nav-' + this.type
4914             } else {
4915                 if (this.type !== 'nav') {
4916                     Roo.log('nav type must be nav/tabs/pills')
4917                 }
4918                 cfg.cls += ' navbar-nav'
4919             }
4920         }
4921         
4922         if (this.parent() && this.parent().sidebar) {
4923             cfg = {
4924                 tag: 'ul',
4925                 cls: 'dashboard-menu sidebar-menu'
4926             };
4927             
4928             return cfg;
4929         }
4930         
4931         if (this.form === true) {
4932             cfg = {
4933                 tag: 'form',
4934                 cls: 'navbar-form form-inline'
4935             };
4936             //nav navbar-right ml-md-auto
4937             if (this.align === 'right') {
4938                 cfg.cls += ' navbar-right ml-md-auto';
4939             } else {
4940                 cfg.cls += ' navbar-left';
4941             }
4942         }
4943         
4944         if (this.align === 'right') {
4945             cfg.cls += ' navbar-right ml-md-auto';
4946         } else {
4947             cfg.cls += ' mr-auto';
4948         }
4949         
4950         if (this.inverse) {
4951             cfg.cls += ' navbar-inverse';
4952             
4953         }
4954         
4955         
4956         return cfg;
4957     },
4958     /**
4959     * sets the active Navigation item
4960     * @param {Roo.bootstrap.NavItem} the new current navitem
4961     */
4962     setActiveItem : function(item)
4963     {
4964         var prev = false;
4965         Roo.each(this.navItems, function(v){
4966             if (v == item) {
4967                 return ;
4968             }
4969             if (v.isActive()) {
4970                 v.setActive(false, true);
4971                 prev = v;
4972                 
4973             }
4974             
4975         });
4976
4977         item.setActive(true, true);
4978         this.fireEvent('changed', this, item, prev);
4979         
4980         
4981     },
4982     /**
4983     * gets the active Navigation item
4984     * @return {Roo.bootstrap.NavItem} the current navitem
4985     */
4986     getActive : function()
4987     {
4988         
4989         var prev = false;
4990         Roo.each(this.navItems, function(v){
4991             
4992             if (v.isActive()) {
4993                 prev = v;
4994                 
4995             }
4996             
4997         });
4998         return prev;
4999     },
5000     
5001     indexOfNav : function()
5002     {
5003         
5004         var prev = false;
5005         Roo.each(this.navItems, function(v,i){
5006             
5007             if (v.isActive()) {
5008                 prev = i;
5009                 
5010             }
5011             
5012         });
5013         return prev;
5014     },
5015     /**
5016     * adds a Navigation item
5017     * @param {Roo.bootstrap.NavItem} the navitem to add
5018     */
5019     addItem : function(cfg)
5020     {
5021         if (this.form && Roo.bootstrap.version == 4) {
5022             cfg.tag = 'div';
5023         }
5024         var cn = new Roo.bootstrap.NavItem(cfg);
5025         this.register(cn);
5026         cn.parentId = this.id;
5027         cn.onRender(this.el, null);
5028         return cn;
5029     },
5030     /**
5031     * register a Navigation item
5032     * @param {Roo.bootstrap.NavItem} the navitem to add
5033     */
5034     register : function(item)
5035     {
5036         this.navItems.push( item);
5037         item.navId = this.navId;
5038     
5039     },
5040     
5041     /**
5042     * clear all the Navigation item
5043     */
5044    
5045     clearAll : function()
5046     {
5047         this.navItems = [];
5048         this.el.dom.innerHTML = '';
5049     },
5050     
5051     getNavItem: function(tabId)
5052     {
5053         var ret = false;
5054         Roo.each(this.navItems, function(e) {
5055             if (e.tabId == tabId) {
5056                ret =  e;
5057                return false;
5058             }
5059             return true;
5060             
5061         });
5062         return ret;
5063     },
5064     
5065     setActiveNext : function()
5066     {
5067         var i = this.indexOfNav(this.getActive());
5068         if (i > this.navItems.length) {
5069             return;
5070         }
5071         this.setActiveItem(this.navItems[i+1]);
5072     },
5073     setActivePrev : function()
5074     {
5075         var i = this.indexOfNav(this.getActive());
5076         if (i  < 1) {
5077             return;
5078         }
5079         this.setActiveItem(this.navItems[i-1]);
5080     },
5081     clearWasActive : function(except) {
5082         Roo.each(this.navItems, function(e) {
5083             if (e.tabId != except.tabId && e.was_active) {
5084                e.was_active = false;
5085                return false;
5086             }
5087             return true;
5088             
5089         });
5090     },
5091     getWasActive : function ()
5092     {
5093         var r = false;
5094         Roo.each(this.navItems, function(e) {
5095             if (e.was_active) {
5096                r = e;
5097                return false;
5098             }
5099             return true;
5100             
5101         });
5102         return r;
5103     }
5104     
5105     
5106 });
5107
5108  
5109 Roo.apply(Roo.bootstrap.NavGroup, {
5110     
5111     groups: {},
5112      /**
5113     * register a Navigation Group
5114     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5115     */
5116     register : function(navgrp)
5117     {
5118         this.groups[navgrp.navId] = navgrp;
5119         
5120     },
5121     /**
5122     * fetch a Navigation Group based on the navigation ID
5123     * @param {string} the navgroup to add
5124     * @returns {Roo.bootstrap.NavGroup} the navgroup 
5125     */
5126     get: function(navId) {
5127         if (typeof(this.groups[navId]) == 'undefined') {
5128             return false;
5129             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
5130         }
5131         return this.groups[navId] ;
5132     }
5133     
5134     
5135     
5136 });
5137
5138  /*
5139  * - LGPL
5140  *
5141  * row
5142  * 
5143  */
5144
5145 /**
5146  * @class Roo.bootstrap.NavItem
5147  * @extends Roo.bootstrap.Component
5148  * Bootstrap Navbar.NavItem class
5149  * @cfg {String} href  link to
5150  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
5151
5152  * @cfg {String} html content of button
5153  * @cfg {String} badge text inside badge
5154  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
5155  * @cfg {String} glyphicon DEPRICATED - use fa
5156  * @cfg {String} icon DEPRICATED - use fa
5157  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
5158  * @cfg {Boolean} active Is item active
5159  * @cfg {Boolean} disabled Is item disabled
5160  
5161  * @cfg {Boolean} preventDefault (true | false) default false
5162  * @cfg {String} tabId the tab that this item activates.
5163  * @cfg {String} tagtype (a|span) render as a href or span?
5164  * @cfg {Boolean} animateRef (true|false) link to element default false  
5165   
5166  * @constructor
5167  * Create a new Navbar Item
5168  * @param {Object} config The config object
5169  */
5170 Roo.bootstrap.NavItem = function(config){
5171     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
5172     this.addEvents({
5173         // raw events
5174         /**
5175          * @event click
5176          * The raw click event for the entire grid.
5177          * @param {Roo.EventObject} e
5178          */
5179         "click" : true,
5180          /**
5181             * @event changed
5182             * Fires when the active item active state changes
5183             * @param {Roo.bootstrap.NavItem} this
5184             * @param {boolean} state the new state
5185              
5186          */
5187         'changed': true,
5188         /**
5189             * @event scrollto
5190             * Fires when scroll to element
5191             * @param {Roo.bootstrap.NavItem} this
5192             * @param {Object} options
5193             * @param {Roo.EventObject} e
5194              
5195          */
5196         'scrollto': true
5197     });
5198    
5199 };
5200
5201 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
5202     
5203     href: false,
5204     html: '',
5205     badge: '',
5206     icon: false,
5207     fa : false,
5208     glyphicon: false,
5209     active: false,
5210     preventDefault : false,
5211     tabId : false,
5212     tagtype : 'a',
5213     tag: 'li',
5214     disabled : false,
5215     animateRef : false,
5216     was_active : false,
5217     button_weight : '',
5218     button_outline : false,
5219     
5220     navLink: false,
5221     
5222     getAutoCreate : function(){
5223          
5224         var cfg = {
5225             tag: this.tag,
5226             cls: 'nav-item'
5227         };
5228         
5229         if (this.active) {
5230             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
5231         }
5232         if (this.disabled) {
5233             cfg.cls += ' disabled';
5234         }
5235         
5236         // BS4 only?
5237         if (this.button_weight.length) {
5238             cfg.tag = this.href ? 'a' : 'button';
5239             cfg.html = this.html || '';
5240             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
5241             if (this.href) {
5242                 cfg.href = this.href;
5243             }
5244             if (this.fa) {
5245                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
5246             }
5247             
5248             // menu .. should add dropdown-menu class - so no need for carat..
5249             
5250             if (this.badge !== '') {
5251                  
5252                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5253             }
5254             return cfg;
5255         }
5256         
5257         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
5258             cfg.cn = [
5259                 {
5260                     tag: this.tagtype,
5261                     href : this.href || "#",
5262                     html: this.html || ''
5263                 }
5264             ];
5265             if (this.tagtype == 'a') {
5266                 cfg.cn[0].cls = 'nav-link';
5267             }
5268             if (this.icon) {
5269                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
5270             }
5271             if (this.fa) {
5272                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
5273             }
5274             if(this.glyphicon) {
5275                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
5276             }
5277             
5278             if (this.menu) {
5279                 
5280                 cfg.cn[0].html += " <span class='caret'></span>";
5281              
5282             }
5283             
5284             if (this.badge !== '') {
5285                  
5286                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5287             }
5288         }
5289         
5290         
5291         
5292         return cfg;
5293     },
5294     onRender : function(ct, position)
5295     {
5296        // Roo.log("Call onRender: " + this.xtype);
5297         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
5298             this.tag = 'div';
5299         }
5300         
5301         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
5302         this.navLink = this.el.select('.nav-link',true).first();
5303         return ret;
5304     },
5305       
5306     
5307     initEvents: function() 
5308     {
5309         if (typeof (this.menu) != 'undefined') {
5310             this.menu.parentType = this.xtype;
5311             this.menu.triggerEl = this.el;
5312             this.menu = this.addxtype(Roo.apply({}, this.menu));
5313         }
5314         
5315         this.el.select('a',true).on('click', this.onClick, this);
5316         
5317         if(this.tagtype == 'span'){
5318             this.el.select('span',true).on('click', this.onClick, this);
5319         }
5320        
5321         // at this point parent should be available..
5322         this.parent().register(this);
5323     },
5324     
5325     onClick : function(e)
5326     {
5327         if (e.getTarget('.dropdown-menu-item')) {
5328             // did you click on a menu itemm.... - then don't trigger onclick..
5329             return;
5330         }
5331         
5332         if(
5333                 this.preventDefault || 
5334                 this.href == '#' 
5335         ){
5336             Roo.log("NavItem - prevent Default?");
5337             e.preventDefault();
5338         }
5339         
5340         if (this.disabled) {
5341             return;
5342         }
5343         
5344         var tg = Roo.bootstrap.TabGroup.get(this.navId);
5345         if (tg && tg.transition) {
5346             Roo.log("waiting for the transitionend");
5347             return;
5348         }
5349         
5350         
5351         
5352         //Roo.log("fire event clicked");
5353         if(this.fireEvent('click', this, e) === false){
5354             return;
5355         };
5356         
5357         if(this.tagtype == 'span'){
5358             return;
5359         }
5360         
5361         //Roo.log(this.href);
5362         var ael = this.el.select('a',true).first();
5363         //Roo.log(ael);
5364         
5365         if(ael && this.animateRef && this.href.indexOf('#') > -1){
5366             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
5367             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
5368                 return; // ignore... - it's a 'hash' to another page.
5369             }
5370             Roo.log("NavItem - prevent Default?");
5371             e.preventDefault();
5372             this.scrollToElement(e);
5373         }
5374         
5375         
5376         var p =  this.parent();
5377    
5378         if (['tabs','pills'].indexOf(p.type)!==-1) {
5379             if (typeof(p.setActiveItem) !== 'undefined') {
5380                 p.setActiveItem(this);
5381             }
5382         }
5383         
5384         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
5385         if (p.parentType == 'NavHeaderbar' && !this.menu) {
5386             // remove the collapsed menu expand...
5387             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
5388         }
5389     },
5390     
5391     isActive: function () {
5392         return this.active
5393     },
5394     setActive : function(state, fire, is_was_active)
5395     {
5396         if (this.active && !state && this.navId) {
5397             this.was_active = true;
5398             var nv = Roo.bootstrap.NavGroup.get(this.navId);
5399             if (nv) {
5400                 nv.clearWasActive(this);
5401             }
5402             
5403         }
5404         this.active = state;
5405         
5406         if (!state ) {
5407             this.el.removeClass('active');
5408             this.navLink ? this.navLink.removeClass('active') : false;
5409         } else if (!this.el.hasClass('active')) {
5410             
5411             this.el.addClass('active');
5412             if (Roo.bootstrap.version == 4 && this.navLink ) {
5413                 this.navLink.addClass('active');
5414             }
5415             
5416         }
5417         if (fire) {
5418             this.fireEvent('changed', this, state);
5419         }
5420         
5421         // show a panel if it's registered and related..
5422         
5423         if (!this.navId || !this.tabId || !state || is_was_active) {
5424             return;
5425         }
5426         
5427         var tg = Roo.bootstrap.TabGroup.get(this.navId);
5428         if (!tg) {
5429             return;
5430         }
5431         var pan = tg.getPanelByName(this.tabId);
5432         if (!pan) {
5433             return;
5434         }
5435         // if we can not flip to new panel - go back to old nav highlight..
5436         if (false == tg.showPanel(pan)) {
5437             var nv = Roo.bootstrap.NavGroup.get(this.navId);
5438             if (nv) {
5439                 var onav = nv.getWasActive();
5440                 if (onav) {
5441                     onav.setActive(true, false, true);
5442                 }
5443             }
5444             
5445         }
5446         
5447         
5448         
5449     },
5450      // this should not be here...
5451     setDisabled : function(state)
5452     {
5453         this.disabled = state;
5454         if (!state ) {
5455             this.el.removeClass('disabled');
5456         } else if (!this.el.hasClass('disabled')) {
5457             this.el.addClass('disabled');
5458         }
5459         
5460     },
5461     
5462     /**
5463      * Fetch the element to display the tooltip on.
5464      * @return {Roo.Element} defaults to this.el
5465      */
5466     tooltipEl : function()
5467     {
5468         return this.el.select('' + this.tagtype + '', true).first();
5469     },
5470     
5471     scrollToElement : function(e)
5472     {
5473         var c = document.body;
5474         
5475         /*
5476          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
5477          */
5478         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
5479             c = document.documentElement;
5480         }
5481         
5482         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
5483         
5484         if(!target){
5485             return;
5486         }
5487
5488         var o = target.calcOffsetsTo(c);
5489         
5490         var options = {
5491             target : target,
5492             value : o[1]
5493         };
5494         
5495         this.fireEvent('scrollto', this, options, e);
5496         
5497         Roo.get(c).scrollTo('top', options.value, true);
5498         
5499         return;
5500     }
5501 });
5502  
5503
5504  /*
5505  * - LGPL
5506  *
5507  * sidebar item
5508  *
5509  *  li
5510  *    <span> icon </span>
5511  *    <span> text </span>
5512  *    <span>badge </span>
5513  */
5514
5515 /**
5516  * @class Roo.bootstrap.NavSidebarItem
5517  * @extends Roo.bootstrap.NavItem
5518  * Bootstrap Navbar.NavSidebarItem class
5519  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5520  * {Boolean} open is the menu open
5521  * {Boolean} buttonView use button as the tigger el rather that a (default false)
5522  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5523  * {String} buttonSize (sm|md|lg)the extra classes for the button
5524  * {Boolean} showArrow show arrow next to the text (default true)
5525  * @constructor
5526  * Create a new Navbar Button
5527  * @param {Object} config The config object
5528  */
5529 Roo.bootstrap.NavSidebarItem = function(config){
5530     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5531     this.addEvents({
5532         // raw events
5533         /**
5534          * @event click
5535          * The raw click event for the entire grid.
5536          * @param {Roo.EventObject} e
5537          */
5538         "click" : true,
5539          /**
5540             * @event changed
5541             * Fires when the active item active state changes
5542             * @param {Roo.bootstrap.NavSidebarItem} this
5543             * @param {boolean} state the new state
5544              
5545          */
5546         'changed': true
5547     });
5548    
5549 };
5550
5551 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
5552     
5553     badgeWeight : 'default',
5554     
5555     open: false,
5556     
5557     buttonView : false,
5558     
5559     buttonWeight : 'default',
5560     
5561     buttonSize : 'md',
5562     
5563     showArrow : true,
5564     
5565     getAutoCreate : function(){
5566         
5567         
5568         var a = {
5569                 tag: 'a',
5570                 href : this.href || '#',
5571                 cls: '',
5572                 html : '',
5573                 cn : []
5574         };
5575         
5576         if(this.buttonView){
5577             a = {
5578                 tag: 'button',
5579                 href : this.href || '#',
5580                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5581                 html : this.html,
5582                 cn : []
5583             };
5584         }
5585         
5586         var cfg = {
5587             tag: 'li',
5588             cls: '',
5589             cn: [ a ]
5590         };
5591         
5592         if (this.active) {
5593             cfg.cls += ' active';
5594         }
5595         
5596         if (this.disabled) {
5597             cfg.cls += ' disabled';
5598         }
5599         if (this.open) {
5600             cfg.cls += ' open x-open';
5601         }
5602         // left icon..
5603         if (this.glyphicon || this.icon) {
5604             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5605             a.cn.push({ tag : 'i', cls : c }) ;
5606         }
5607         
5608         if(!this.buttonView){
5609             var span = {
5610                 tag: 'span',
5611                 html : this.html || ''
5612             };
5613
5614             a.cn.push(span);
5615             
5616         }
5617         
5618         if (this.badge !== '') {
5619             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5620         }
5621         
5622         if (this.menu) {
5623             
5624             if(this.showArrow){
5625                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5626             }
5627             
5628             a.cls += ' dropdown-toggle treeview' ;
5629         }
5630         
5631         return cfg;
5632     },
5633     
5634     initEvents : function()
5635     { 
5636         if (typeof (this.menu) != 'undefined') {
5637             this.menu.parentType = this.xtype;
5638             this.menu.triggerEl = this.el;
5639             this.menu = this.addxtype(Roo.apply({}, this.menu));
5640         }
5641         
5642         this.el.on('click', this.onClick, this);
5643         
5644         if(this.badge !== ''){
5645             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5646         }
5647         
5648     },
5649     
5650     onClick : function(e)
5651     {
5652         if(this.disabled){
5653             e.preventDefault();
5654             return;
5655         }
5656         
5657         if(this.preventDefault){
5658             e.preventDefault();
5659         }
5660         
5661         this.fireEvent('click', this, e);
5662     },
5663     
5664     disable : function()
5665     {
5666         this.setDisabled(true);
5667     },
5668     
5669     enable : function()
5670     {
5671         this.setDisabled(false);
5672     },
5673     
5674     setDisabled : function(state)
5675     {
5676         if(this.disabled == state){
5677             return;
5678         }
5679         
5680         this.disabled = state;
5681         
5682         if (state) {
5683             this.el.addClass('disabled');
5684             return;
5685         }
5686         
5687         this.el.removeClass('disabled');
5688         
5689         return;
5690     },
5691     
5692     setActive : function(state)
5693     {
5694         if(this.active == state){
5695             return;
5696         }
5697         
5698         this.active = state;
5699         
5700         if (state) {
5701             this.el.addClass('active');
5702             return;
5703         }
5704         
5705         this.el.removeClass('active');
5706         
5707         return;
5708     },
5709     
5710     isActive: function () 
5711     {
5712         return this.active;
5713     },
5714     
5715     setBadge : function(str)
5716     {
5717         if(!this.badgeEl){
5718             return;
5719         }
5720         
5721         this.badgeEl.dom.innerHTML = str;
5722     }
5723     
5724    
5725      
5726  
5727 });
5728  
5729
5730  /*
5731  * - LGPL
5732  *
5733  * row
5734  * 
5735  */
5736
5737 /**
5738  * @class Roo.bootstrap.Row
5739  * @extends Roo.bootstrap.Component
5740  * Bootstrap Row class (contains columns...)
5741  * 
5742  * @constructor
5743  * Create a new Row
5744  * @param {Object} config The config object
5745  */
5746
5747 Roo.bootstrap.Row = function(config){
5748     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5749 };
5750
5751 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5752     
5753     getAutoCreate : function(){
5754        return {
5755             cls: 'row clearfix'
5756        };
5757     }
5758     
5759     
5760 });
5761
5762  
5763
5764  /*
5765  * - LGPL
5766  *
5767  * pagination
5768  * 
5769  */
5770
5771 /**
5772  * @class Roo.bootstrap.Pagination
5773  * @extends Roo.bootstrap.Component
5774  * Bootstrap Pagination class
5775  * @cfg {String} size xs | sm | md | lg
5776  * @cfg {Boolean} inverse false | true
5777  * 
5778  * @constructor
5779  * Create a new Pagination
5780  * @param {Object} config The config object
5781  */
5782
5783 Roo.bootstrap.Pagination = function(config){
5784     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5785 };
5786
5787 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5788     
5789     cls: false,
5790     size: false,
5791     inverse: false,
5792     
5793     getAutoCreate : function(){
5794         var cfg = {
5795             tag: 'ul',
5796                 cls: 'pagination'
5797         };
5798         if (this.inverse) {
5799             cfg.cls += ' inverse';
5800         }
5801         if (this.html) {
5802             cfg.html=this.html;
5803         }
5804         if (this.cls) {
5805             cfg.cls += " " + this.cls;
5806         }
5807         return cfg;
5808     }
5809    
5810 });
5811
5812  
5813
5814  /*
5815  * - LGPL
5816  *
5817  * Pagination item
5818  * 
5819  */
5820
5821
5822 /**
5823  * @class Roo.bootstrap.PaginationItem
5824  * @extends Roo.bootstrap.Component
5825  * Bootstrap PaginationItem class
5826  * @cfg {String} html text
5827  * @cfg {String} href the link
5828  * @cfg {Boolean} preventDefault (true | false) default true
5829  * @cfg {Boolean} active (true | false) default false
5830  * @cfg {Boolean} disabled default false
5831  * 
5832  * 
5833  * @constructor
5834  * Create a new PaginationItem
5835  * @param {Object} config The config object
5836  */
5837
5838
5839 Roo.bootstrap.PaginationItem = function(config){
5840     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5841     this.addEvents({
5842         // raw events
5843         /**
5844          * @event click
5845          * The raw click event for the entire grid.
5846          * @param {Roo.EventObject} e
5847          */
5848         "click" : true
5849     });
5850 };
5851
5852 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5853     
5854     href : false,
5855     html : false,
5856     preventDefault: true,
5857     active : false,
5858     cls : false,
5859     disabled: false,
5860     
5861     getAutoCreate : function(){
5862         var cfg= {
5863             tag: 'li',
5864             cn: [
5865                 {
5866                     tag : 'a',
5867                     href : this.href ? this.href : '#',
5868                     html : this.html ? this.html : ''
5869                 }
5870             ]
5871         };
5872         
5873         if(this.cls){
5874             cfg.cls = this.cls;
5875         }
5876         
5877         if(this.disabled){
5878             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5879         }
5880         
5881         if(this.active){
5882             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5883         }
5884         
5885         return cfg;
5886     },
5887     
5888     initEvents: function() {
5889         
5890         this.el.on('click', this.onClick, this);
5891         
5892     },
5893     onClick : function(e)
5894     {
5895         Roo.log('PaginationItem on click ');
5896         if(this.preventDefault){
5897             e.preventDefault();
5898         }
5899         
5900         if(this.disabled){
5901             return;
5902         }
5903         
5904         this.fireEvent('click', this, e);
5905     }
5906    
5907 });
5908
5909  
5910
5911  /*
5912  * - LGPL
5913  *
5914  * slider
5915  * 
5916  */
5917
5918
5919 /**
5920  * @class Roo.bootstrap.Slider
5921  * @extends Roo.bootstrap.Component
5922  * Bootstrap Slider class
5923  *    
5924  * @constructor
5925  * Create a new Slider
5926  * @param {Object} config The config object
5927  */
5928
5929 Roo.bootstrap.Slider = function(config){
5930     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5931 };
5932
5933 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5934     
5935     getAutoCreate : function(){
5936         
5937         var cfg = {
5938             tag: 'div',
5939             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5940             cn: [
5941                 {
5942                     tag: 'a',
5943                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5944                 }
5945             ]
5946         };
5947         
5948         return cfg;
5949     }
5950    
5951 });
5952
5953  /*
5954  * Based on:
5955  * Ext JS Library 1.1.1
5956  * Copyright(c) 2006-2007, Ext JS, LLC.
5957  *
5958  * Originally Released Under LGPL - original licence link has changed is not relivant.
5959  *
5960  * Fork - LGPL
5961  * <script type="text/javascript">
5962  */
5963  
5964
5965 /**
5966  * @class Roo.grid.ColumnModel
5967  * @extends Roo.util.Observable
5968  * This is the default implementation of a ColumnModel used by the Grid. It defines
5969  * the columns in the grid.
5970  * <br>Usage:<br>
5971  <pre><code>
5972  var colModel = new Roo.grid.ColumnModel([
5973         {header: "Ticker", width: 60, sortable: true, locked: true},
5974         {header: "Company Name", width: 150, sortable: true},
5975         {header: "Market Cap.", width: 100, sortable: true},
5976         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5977         {header: "Employees", width: 100, sortable: true, resizable: false}
5978  ]);
5979  </code></pre>
5980  * <p>
5981  
5982  * The config options listed for this class are options which may appear in each
5983  * individual column definition.
5984  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5985  * @constructor
5986  * @param {Object} config An Array of column config objects. See this class's
5987  * config objects for details.
5988 */
5989 Roo.grid.ColumnModel = function(config){
5990         /**
5991      * The config passed into the constructor
5992      */
5993     this.config = config;
5994     this.lookup = {};
5995
5996     // if no id, create one
5997     // if the column does not have a dataIndex mapping,
5998     // map it to the order it is in the config
5999     for(var i = 0, len = config.length; i < len; i++){
6000         var c = config[i];
6001         if(typeof c.dataIndex == "undefined"){
6002             c.dataIndex = i;
6003         }
6004         if(typeof c.renderer == "string"){
6005             c.renderer = Roo.util.Format[c.renderer];
6006         }
6007         if(typeof c.id == "undefined"){
6008             c.id = Roo.id();
6009         }
6010         if(c.editor && c.editor.xtype){
6011             c.editor  = Roo.factory(c.editor, Roo.grid);
6012         }
6013         if(c.editor && c.editor.isFormField){
6014             c.editor = new Roo.grid.GridEditor(c.editor);
6015         }
6016         this.lookup[c.id] = c;
6017     }
6018
6019     /**
6020      * The width of columns which have no width specified (defaults to 100)
6021      * @type Number
6022      */
6023     this.defaultWidth = 100;
6024
6025     /**
6026      * Default sortable of columns which have no sortable specified (defaults to false)
6027      * @type Boolean
6028      */
6029     this.defaultSortable = false;
6030
6031     this.addEvents({
6032         /**
6033              * @event widthchange
6034              * Fires when the width of a column changes.
6035              * @param {ColumnModel} this
6036              * @param {Number} columnIndex The column index
6037              * @param {Number} newWidth The new width
6038              */
6039             "widthchange": true,
6040         /**
6041              * @event headerchange
6042              * Fires when the text of a header changes.
6043              * @param {ColumnModel} this
6044              * @param {Number} columnIndex The column index
6045              * @param {Number} newText The new header text
6046              */
6047             "headerchange": true,
6048         /**
6049              * @event hiddenchange
6050              * Fires when a column is hidden or "unhidden".
6051              * @param {ColumnModel} this
6052              * @param {Number} columnIndex The column index
6053              * @param {Boolean} hidden true if hidden, false otherwise
6054              */
6055             "hiddenchange": true,
6056             /**
6057          * @event columnmoved
6058          * Fires when a column is moved.
6059          * @param {ColumnModel} this
6060          * @param {Number} oldIndex
6061          * @param {Number} newIndex
6062          */
6063         "columnmoved" : true,
6064         /**
6065          * @event columlockchange
6066          * Fires when a column's locked state is changed
6067          * @param {ColumnModel} this
6068          * @param {Number} colIndex
6069          * @param {Boolean} locked true if locked
6070          */
6071         "columnlockchange" : true
6072     });
6073     Roo.grid.ColumnModel.superclass.constructor.call(this);
6074 };
6075 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6076     /**
6077      * @cfg {String} header The header text to display in the Grid view.
6078      */
6079     /**
6080      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6081      * {@link Roo.data.Record} definition from which to draw the column's value. If not
6082      * specified, the column's index is used as an index into the Record's data Array.
6083      */
6084     /**
6085      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6086      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6087      */
6088     /**
6089      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6090      * Defaults to the value of the {@link #defaultSortable} property.
6091      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6092      */
6093     /**
6094      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
6095      */
6096     /**
6097      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
6098      */
6099     /**
6100      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6101      */
6102     /**
6103      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6104      */
6105     /**
6106      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6107      * given the cell's data value. See {@link #setRenderer}. If not specified, the
6108      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6109      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6110      */
6111        /**
6112      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
6113      */
6114     /**
6115      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
6116      */
6117     /**
6118      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
6119      */
6120     /**
6121      * @cfg {String} cursor (Optional)
6122      */
6123     /**
6124      * @cfg {String} tooltip (Optional)
6125      */
6126     /**
6127      * @cfg {Number} xs (Optional)
6128      */
6129     /**
6130      * @cfg {Number} sm (Optional)
6131      */
6132     /**
6133      * @cfg {Number} md (Optional)
6134      */
6135     /**
6136      * @cfg {Number} lg (Optional)
6137      */
6138     /**
6139      * Returns the id of the column at the specified index.
6140      * @param {Number} index The column index
6141      * @return {String} the id
6142      */
6143     getColumnId : function(index){
6144         return this.config[index].id;
6145     },
6146
6147     /**
6148      * Returns the column for a specified id.
6149      * @param {String} id The column id
6150      * @return {Object} the column
6151      */
6152     getColumnById : function(id){
6153         return this.lookup[id];
6154     },
6155
6156     
6157     /**
6158      * Returns the column for a specified dataIndex.
6159      * @param {String} dataIndex The column dataIndex
6160      * @return {Object|Boolean} the column or false if not found
6161      */
6162     getColumnByDataIndex: function(dataIndex){
6163         var index = this.findColumnIndex(dataIndex);
6164         return index > -1 ? this.config[index] : false;
6165     },
6166     
6167     /**
6168      * Returns the index for a specified column id.
6169      * @param {String} id The column id
6170      * @return {Number} the index, or -1 if not found
6171      */
6172     getIndexById : function(id){
6173         for(var i = 0, len = this.config.length; i < len; i++){
6174             if(this.config[i].id == id){
6175                 return i;
6176             }
6177         }
6178         return -1;
6179     },
6180     
6181     /**
6182      * Returns the index for a specified column dataIndex.
6183      * @param {String} dataIndex The column dataIndex
6184      * @return {Number} the index, or -1 if not found
6185      */
6186     
6187     findColumnIndex : function(dataIndex){
6188         for(var i = 0, len = this.config.length; i < len; i++){
6189             if(this.config[i].dataIndex == dataIndex){
6190                 return i;
6191             }
6192         }
6193         return -1;
6194     },
6195     
6196     
6197     moveColumn : function(oldIndex, newIndex){
6198         var c = this.config[oldIndex];
6199         this.config.splice(oldIndex, 1);
6200         this.config.splice(newIndex, 0, c);
6201         this.dataMap = null;
6202         this.fireEvent("columnmoved", this, oldIndex, newIndex);
6203     },
6204
6205     isLocked : function(colIndex){
6206         return this.config[colIndex].locked === true;
6207     },
6208
6209     setLocked : function(colIndex, value, suppressEvent){
6210         if(this.isLocked(colIndex) == value){
6211             return;
6212         }
6213         this.config[colIndex].locked = value;
6214         if(!suppressEvent){
6215             this.fireEvent("columnlockchange", this, colIndex, value);
6216         }
6217     },
6218
6219     getTotalLockedWidth : function(){
6220         var totalWidth = 0;
6221         for(var i = 0; i < this.config.length; i++){
6222             if(this.isLocked(i) && !this.isHidden(i)){
6223                 this.totalWidth += this.getColumnWidth(i);
6224             }
6225         }
6226         return totalWidth;
6227     },
6228
6229     getLockedCount : function(){
6230         for(var i = 0, len = this.config.length; i < len; i++){
6231             if(!this.isLocked(i)){
6232                 return i;
6233             }
6234         }
6235         
6236         return this.config.length;
6237     },
6238
6239     /**
6240      * Returns the number of columns.
6241      * @return {Number}
6242      */
6243     getColumnCount : function(visibleOnly){
6244         if(visibleOnly === true){
6245             var c = 0;
6246             for(var i = 0, len = this.config.length; i < len; i++){
6247                 if(!this.isHidden(i)){
6248                     c++;
6249                 }
6250             }
6251             return c;
6252         }
6253         return this.config.length;
6254     },
6255
6256     /**
6257      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
6258      * @param {Function} fn
6259      * @param {Object} scope (optional)
6260      * @return {Array} result
6261      */
6262     getColumnsBy : function(fn, scope){
6263         var r = [];
6264         for(var i = 0, len = this.config.length; i < len; i++){
6265             var c = this.config[i];
6266             if(fn.call(scope||this, c, i) === true){
6267                 r[r.length] = c;
6268             }
6269         }
6270         return r;
6271     },
6272
6273     /**
6274      * Returns true if the specified column is sortable.
6275      * @param {Number} col The column index
6276      * @return {Boolean}
6277      */
6278     isSortable : function(col){
6279         if(typeof this.config[col].sortable == "undefined"){
6280             return this.defaultSortable;
6281         }
6282         return this.config[col].sortable;
6283     },
6284
6285     /**
6286      * Returns the rendering (formatting) function defined for the column.
6287      * @param {Number} col The column index.
6288      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
6289      */
6290     getRenderer : function(col){
6291         if(!this.config[col].renderer){
6292             return Roo.grid.ColumnModel.defaultRenderer;
6293         }
6294         return this.config[col].renderer;
6295     },
6296
6297     /**
6298      * Sets the rendering (formatting) function for a column.
6299      * @param {Number} col The column index
6300      * @param {Function} fn The function to use to process the cell's raw data
6301      * to return HTML markup for the grid view. The render function is called with
6302      * the following parameters:<ul>
6303      * <li>Data value.</li>
6304      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
6305      * <li>css A CSS style string to apply to the table cell.</li>
6306      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
6307      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
6308      * <li>Row index</li>
6309      * <li>Column index</li>
6310      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
6311      */
6312     setRenderer : function(col, fn){
6313         this.config[col].renderer = fn;
6314     },
6315
6316     /**
6317      * Returns the width for the specified column.
6318      * @param {Number} col The column index
6319      * @return {Number}
6320      */
6321     getColumnWidth : function(col){
6322         return this.config[col].width * 1 || this.defaultWidth;
6323     },
6324
6325     /**
6326      * Sets the width for a column.
6327      * @param {Number} col The column index
6328      * @param {Number} width The new width
6329      */
6330     setColumnWidth : function(col, width, suppressEvent){
6331         this.config[col].width = width;
6332         this.totalWidth = null;
6333         if(!suppressEvent){
6334              this.fireEvent("widthchange", this, col, width);
6335         }
6336     },
6337
6338     /**
6339      * Returns the total width of all columns.
6340      * @param {Boolean} includeHidden True to include hidden column widths
6341      * @return {Number}
6342      */
6343     getTotalWidth : function(includeHidden){
6344         if(!this.totalWidth){
6345             this.totalWidth = 0;
6346             for(var i = 0, len = this.config.length; i < len; i++){
6347                 if(includeHidden || !this.isHidden(i)){
6348                     this.totalWidth += this.getColumnWidth(i);
6349                 }
6350             }
6351         }
6352         return this.totalWidth;
6353     },
6354
6355     /**
6356      * Returns the header for the specified column.
6357      * @param {Number} col The column index
6358      * @return {String}
6359      */
6360     getColumnHeader : function(col){
6361         return this.config[col].header;
6362     },
6363
6364     /**
6365      * Sets the header for a column.
6366      * @param {Number} col The column index
6367      * @param {String} header The new header
6368      */
6369     setColumnHeader : function(col, header){
6370         this.config[col].header = header;
6371         this.fireEvent("headerchange", this, col, header);
6372     },
6373
6374     /**
6375      * Returns the tooltip for the specified column.
6376      * @param {Number} col The column index
6377      * @return {String}
6378      */
6379     getColumnTooltip : function(col){
6380             return this.config[col].tooltip;
6381     },
6382     /**
6383      * Sets the tooltip for a column.
6384      * @param {Number} col The column index
6385      * @param {String} tooltip The new tooltip
6386      */
6387     setColumnTooltip : function(col, tooltip){
6388             this.config[col].tooltip = tooltip;
6389     },
6390
6391     /**
6392      * Returns the dataIndex for the specified column.
6393      * @param {Number} col The column index
6394      * @return {Number}
6395      */
6396     getDataIndex : function(col){
6397         return this.config[col].dataIndex;
6398     },
6399
6400     /**
6401      * Sets the dataIndex for a column.
6402      * @param {Number} col The column index
6403      * @param {Number} dataIndex The new dataIndex
6404      */
6405     setDataIndex : function(col, dataIndex){
6406         this.config[col].dataIndex = dataIndex;
6407     },
6408
6409     
6410     
6411     /**
6412      * Returns true if the cell is editable.
6413      * @param {Number} colIndex The column index
6414      * @param {Number} rowIndex The row index - this is nto actually used..?
6415      * @return {Boolean}
6416      */
6417     isCellEditable : function(colIndex, rowIndex){
6418         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6419     },
6420
6421     /**
6422      * Returns the editor defined for the cell/column.
6423      * return false or null to disable editing.
6424      * @param {Number} colIndex The column index
6425      * @param {Number} rowIndex The row index
6426      * @return {Object}
6427      */
6428     getCellEditor : function(colIndex, rowIndex){
6429         return this.config[colIndex].editor;
6430     },
6431
6432     /**
6433      * Sets if a column is editable.
6434      * @param {Number} col The column index
6435      * @param {Boolean} editable True if the column is editable
6436      */
6437     setEditable : function(col, editable){
6438         this.config[col].editable = editable;
6439     },
6440
6441
6442     /**
6443      * Returns true if the column is hidden.
6444      * @param {Number} colIndex The column index
6445      * @return {Boolean}
6446      */
6447     isHidden : function(colIndex){
6448         return this.config[colIndex].hidden;
6449     },
6450
6451
6452     /**
6453      * Returns true if the column width cannot be changed
6454      */
6455     isFixed : function(colIndex){
6456         return this.config[colIndex].fixed;
6457     },
6458
6459     /**
6460      * Returns true if the column can be resized
6461      * @return {Boolean}
6462      */
6463     isResizable : function(colIndex){
6464         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6465     },
6466     /**
6467      * Sets if a column is hidden.
6468      * @param {Number} colIndex The column index
6469      * @param {Boolean} hidden True if the column is hidden
6470      */
6471     setHidden : function(colIndex, hidden){
6472         this.config[colIndex].hidden = hidden;
6473         this.totalWidth = null;
6474         this.fireEvent("hiddenchange", this, colIndex, hidden);
6475     },
6476
6477     /**
6478      * Sets the editor for a column.
6479      * @param {Number} col The column index
6480      * @param {Object} editor The editor object
6481      */
6482     setEditor : function(col, editor){
6483         this.config[col].editor = editor;
6484     }
6485 });
6486
6487 Roo.grid.ColumnModel.defaultRenderer = function(value)
6488 {
6489     if(typeof value == "object") {
6490         return value;
6491     }
6492         if(typeof value == "string" && value.length < 1){
6493             return "&#160;";
6494         }
6495     
6496         return String.format("{0}", value);
6497 };
6498
6499 // Alias for backwards compatibility
6500 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6501 /*
6502  * Based on:
6503  * Ext JS Library 1.1.1
6504  * Copyright(c) 2006-2007, Ext JS, LLC.
6505  *
6506  * Originally Released Under LGPL - original licence link has changed is not relivant.
6507  *
6508  * Fork - LGPL
6509  * <script type="text/javascript">
6510  */
6511  
6512 /**
6513  * @class Roo.LoadMask
6514  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6515  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6516  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6517  * element's UpdateManager load indicator and will be destroyed after the initial load.
6518  * @constructor
6519  * Create a new LoadMask
6520  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6521  * @param {Object} config The config object
6522  */
6523 Roo.LoadMask = function(el, config){
6524     this.el = Roo.get(el);
6525     Roo.apply(this, config);
6526     if(this.store){
6527         this.store.on('beforeload', this.onBeforeLoad, this);
6528         this.store.on('load', this.onLoad, this);
6529         this.store.on('loadexception', this.onLoadException, this);
6530         this.removeMask = false;
6531     }else{
6532         var um = this.el.getUpdateManager();
6533         um.showLoadIndicator = false; // disable the default indicator
6534         um.on('beforeupdate', this.onBeforeLoad, this);
6535         um.on('update', this.onLoad, this);
6536         um.on('failure', this.onLoad, this);
6537         this.removeMask = true;
6538     }
6539 };
6540
6541 Roo.LoadMask.prototype = {
6542     /**
6543      * @cfg {Boolean} removeMask
6544      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6545      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6546      */
6547     /**
6548      * @cfg {String} msg
6549      * The text to display in a centered loading message box (defaults to 'Loading...')
6550      */
6551     msg : 'Loading...',
6552     /**
6553      * @cfg {String} msgCls
6554      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6555      */
6556     msgCls : 'x-mask-loading',
6557
6558     /**
6559      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6560      * @type Boolean
6561      */
6562     disabled: false,
6563
6564     /**
6565      * Disables the mask to prevent it from being displayed
6566      */
6567     disable : function(){
6568        this.disabled = true;
6569     },
6570
6571     /**
6572      * Enables the mask so that it can be displayed
6573      */
6574     enable : function(){
6575         this.disabled = false;
6576     },
6577     
6578     onLoadException : function()
6579     {
6580         Roo.log(arguments);
6581         
6582         if (typeof(arguments[3]) != 'undefined') {
6583             Roo.MessageBox.alert("Error loading",arguments[3]);
6584         } 
6585         /*
6586         try {
6587             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6588                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6589             }   
6590         } catch(e) {
6591             
6592         }
6593         */
6594     
6595         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6596     },
6597     // private
6598     onLoad : function()
6599     {
6600         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6601     },
6602
6603     // private
6604     onBeforeLoad : function(){
6605         if(!this.disabled){
6606             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6607         }
6608     },
6609
6610     // private
6611     destroy : function(){
6612         if(this.store){
6613             this.store.un('beforeload', this.onBeforeLoad, this);
6614             this.store.un('load', this.onLoad, this);
6615             this.store.un('loadexception', this.onLoadException, this);
6616         }else{
6617             var um = this.el.getUpdateManager();
6618             um.un('beforeupdate', this.onBeforeLoad, this);
6619             um.un('update', this.onLoad, this);
6620             um.un('failure', this.onLoad, this);
6621         }
6622     }
6623 };/*
6624  * - LGPL
6625  *
6626  * table
6627  * 
6628  */
6629
6630 /**
6631  * @class Roo.bootstrap.Table
6632  * @extends Roo.bootstrap.Component
6633  * Bootstrap Table class
6634  * @cfg {String} cls table class
6635  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6636  * @cfg {String} bgcolor Specifies the background color for a table
6637  * @cfg {Number} border Specifies whether the table cells should have borders or not
6638  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6639  * @cfg {Number} cellspacing Specifies the space between cells
6640  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6641  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6642  * @cfg {String} sortable Specifies that the table should be sortable
6643  * @cfg {String} summary Specifies a summary of the content of a table
6644  * @cfg {Number} width Specifies the width of a table
6645  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6646  * 
6647  * @cfg {boolean} striped Should the rows be alternative striped
6648  * @cfg {boolean} bordered Add borders to the table
6649  * @cfg {boolean} hover Add hover highlighting
6650  * @cfg {boolean} condensed Format condensed
6651  * @cfg {boolean} responsive Format condensed
6652  * @cfg {Boolean} loadMask (true|false) default false
6653  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6654  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6655  * @cfg {Boolean} rowSelection (true|false) default false
6656  * @cfg {Boolean} cellSelection (true|false) default false
6657  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6658  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6659  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6660  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6661  
6662  * 
6663  * @constructor
6664  * Create a new Table
6665  * @param {Object} config The config object
6666  */
6667
6668 Roo.bootstrap.Table = function(config){
6669     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6670     
6671   
6672     
6673     // BC...
6674     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6675     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6676     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6677     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6678     
6679     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6680     if (this.sm) {
6681         this.sm.grid = this;
6682         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6683         this.sm = this.selModel;
6684         this.sm.xmodule = this.xmodule || false;
6685     }
6686     
6687     if (this.cm && typeof(this.cm.config) == 'undefined') {
6688         this.colModel = new Roo.grid.ColumnModel(this.cm);
6689         this.cm = this.colModel;
6690         this.cm.xmodule = this.xmodule || false;
6691     }
6692     if (this.store) {
6693         this.store= Roo.factory(this.store, Roo.data);
6694         this.ds = this.store;
6695         this.ds.xmodule = this.xmodule || false;
6696          
6697     }
6698     if (this.footer && this.store) {
6699         this.footer.dataSource = this.ds;
6700         this.footer = Roo.factory(this.footer);
6701     }
6702     
6703     /** @private */
6704     this.addEvents({
6705         /**
6706          * @event cellclick
6707          * Fires when a cell is clicked
6708          * @param {Roo.bootstrap.Table} this
6709          * @param {Roo.Element} el
6710          * @param {Number} rowIndex
6711          * @param {Number} columnIndex
6712          * @param {Roo.EventObject} e
6713          */
6714         "cellclick" : true,
6715         /**
6716          * @event celldblclick
6717          * Fires when a cell is double clicked
6718          * @param {Roo.bootstrap.Table} this
6719          * @param {Roo.Element} el
6720          * @param {Number} rowIndex
6721          * @param {Number} columnIndex
6722          * @param {Roo.EventObject} e
6723          */
6724         "celldblclick" : true,
6725         /**
6726          * @event rowclick
6727          * Fires when a row is clicked
6728          * @param {Roo.bootstrap.Table} this
6729          * @param {Roo.Element} el
6730          * @param {Number} rowIndex
6731          * @param {Roo.EventObject} e
6732          */
6733         "rowclick" : true,
6734         /**
6735          * @event rowdblclick
6736          * Fires when a row is double clicked
6737          * @param {Roo.bootstrap.Table} this
6738          * @param {Roo.Element} el
6739          * @param {Number} rowIndex
6740          * @param {Roo.EventObject} e
6741          */
6742         "rowdblclick" : true,
6743         /**
6744          * @event mouseover
6745          * Fires when a mouseover occur
6746          * @param {Roo.bootstrap.Table} this
6747          * @param {Roo.Element} el
6748          * @param {Number} rowIndex
6749          * @param {Number} columnIndex
6750          * @param {Roo.EventObject} e
6751          */
6752         "mouseover" : true,
6753         /**
6754          * @event mouseout
6755          * Fires when a mouseout occur
6756          * @param {Roo.bootstrap.Table} this
6757          * @param {Roo.Element} el
6758          * @param {Number} rowIndex
6759          * @param {Number} columnIndex
6760          * @param {Roo.EventObject} e
6761          */
6762         "mouseout" : true,
6763         /**
6764          * @event rowclass
6765          * Fires when a row is rendered, so you can change add a style to it.
6766          * @param {Roo.bootstrap.Table} this
6767          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6768          */
6769         'rowclass' : true,
6770           /**
6771          * @event rowsrendered
6772          * Fires when all the  rows have been rendered
6773          * @param {Roo.bootstrap.Table} this
6774          */
6775         'rowsrendered' : true,
6776         /**
6777          * @event contextmenu
6778          * The raw contextmenu event for the entire grid.
6779          * @param {Roo.EventObject} e
6780          */
6781         "contextmenu" : true,
6782         /**
6783          * @event rowcontextmenu
6784          * Fires when a row is right clicked
6785          * @param {Roo.bootstrap.Table} this
6786          * @param {Number} rowIndex
6787          * @param {Roo.EventObject} e
6788          */
6789         "rowcontextmenu" : true,
6790         /**
6791          * @event cellcontextmenu
6792          * Fires when a cell is right clicked
6793          * @param {Roo.bootstrap.Table} this
6794          * @param {Number} rowIndex
6795          * @param {Number} cellIndex
6796          * @param {Roo.EventObject} e
6797          */
6798          "cellcontextmenu" : true,
6799          /**
6800          * @event headercontextmenu
6801          * Fires when a header is right clicked
6802          * @param {Roo.bootstrap.Table} this
6803          * @param {Number} columnIndex
6804          * @param {Roo.EventObject} e
6805          */
6806         "headercontextmenu" : true
6807     });
6808 };
6809
6810 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6811     
6812     cls: false,
6813     align: false,
6814     bgcolor: false,
6815     border: false,
6816     cellpadding: false,
6817     cellspacing: false,
6818     frame: false,
6819     rules: false,
6820     sortable: false,
6821     summary: false,
6822     width: false,
6823     striped : false,
6824     scrollBody : false,
6825     bordered: false,
6826     hover:  false,
6827     condensed : false,
6828     responsive : false,
6829     sm : false,
6830     cm : false,
6831     store : false,
6832     loadMask : false,
6833     footerShow : true,
6834     headerShow : true,
6835   
6836     rowSelection : false,
6837     cellSelection : false,
6838     layout : false,
6839     
6840     // Roo.Element - the tbody
6841     mainBody: false,
6842     // Roo.Element - thead element
6843     mainHead: false,
6844     
6845     container: false, // used by gridpanel...
6846     
6847     lazyLoad : false,
6848     
6849     CSS : Roo.util.CSS,
6850     
6851     auto_hide_footer : false,
6852     
6853     getAutoCreate : function()
6854     {
6855         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6856         
6857         cfg = {
6858             tag: 'table',
6859             cls : 'table',
6860             cn : []
6861         };
6862         if (this.scrollBody) {
6863             cfg.cls += ' table-body-fixed';
6864         }    
6865         if (this.striped) {
6866             cfg.cls += ' table-striped';
6867         }
6868         
6869         if (this.hover) {
6870             cfg.cls += ' table-hover';
6871         }
6872         if (this.bordered) {
6873             cfg.cls += ' table-bordered';
6874         }
6875         if (this.condensed) {
6876             cfg.cls += ' table-condensed';
6877         }
6878         if (this.responsive) {
6879             cfg.cls += ' table-responsive';
6880         }
6881         
6882         if (this.cls) {
6883             cfg.cls+=  ' ' +this.cls;
6884         }
6885         
6886         // this lot should be simplifed...
6887         var _t = this;
6888         var cp = [
6889             'align',
6890             'bgcolor',
6891             'border',
6892             'cellpadding',
6893             'cellspacing',
6894             'frame',
6895             'rules',
6896             'sortable',
6897             'summary',
6898             'width'
6899         ].forEach(function(k) {
6900             if (_t[k]) {
6901                 cfg[k] = _t[k];
6902             }
6903         });
6904         
6905         
6906         if (this.layout) {
6907             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6908         }
6909         
6910         if(this.store || this.cm){
6911             if(this.headerShow){
6912                 cfg.cn.push(this.renderHeader());
6913             }
6914             
6915             cfg.cn.push(this.renderBody());
6916             
6917             if(this.footerShow){
6918                 cfg.cn.push(this.renderFooter());
6919             }
6920             // where does this come from?
6921             //cfg.cls+=  ' TableGrid';
6922         }
6923         
6924         return { cn : [ cfg ] };
6925     },
6926     
6927     initEvents : function()
6928     {   
6929         if(!this.store || !this.cm){
6930             return;
6931         }
6932         if (this.selModel) {
6933             this.selModel.initEvents();
6934         }
6935         
6936         
6937         //Roo.log('initEvents with ds!!!!');
6938         
6939         this.mainBody = this.el.select('tbody', true).first();
6940         this.mainHead = this.el.select('thead', true).first();
6941         this.mainFoot = this.el.select('tfoot', true).first();
6942         
6943         
6944         
6945         var _this = this;
6946         
6947         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6948             e.on('click', _this.sort, _this);
6949         });
6950         
6951         this.mainBody.on("click", this.onClick, this);
6952         this.mainBody.on("dblclick", this.onDblClick, this);
6953         
6954         // why is this done????? = it breaks dialogs??
6955         //this.parent().el.setStyle('position', 'relative');
6956         
6957         
6958         if (this.footer) {
6959             this.footer.parentId = this.id;
6960             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6961             
6962             if(this.lazyLoad){
6963                 this.el.select('tfoot tr td').first().addClass('hide');
6964             }
6965         } 
6966         
6967         if(this.loadMask) {
6968             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6969         }
6970         
6971         this.store.on('load', this.onLoad, this);
6972         this.store.on('beforeload', this.onBeforeLoad, this);
6973         this.store.on('update', this.onUpdate, this);
6974         this.store.on('add', this.onAdd, this);
6975         this.store.on("clear", this.clear, this);
6976         
6977         this.el.on("contextmenu", this.onContextMenu, this);
6978         
6979         this.mainBody.on('scroll', this.onBodyScroll, this);
6980         
6981         this.cm.on("headerchange", this.onHeaderChange, this);
6982         
6983         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6984         
6985     },
6986     
6987     onContextMenu : function(e, t)
6988     {
6989         this.processEvent("contextmenu", e);
6990     },
6991     
6992     processEvent : function(name, e)
6993     {
6994         if (name != 'touchstart' ) {
6995             this.fireEvent(name, e);    
6996         }
6997         
6998         var t = e.getTarget();
6999         
7000         var cell = Roo.get(t);
7001         
7002         if(!cell){
7003             return;
7004         }
7005         
7006         if(cell.findParent('tfoot', false, true)){
7007             return;
7008         }
7009         
7010         if(cell.findParent('thead', false, true)){
7011             
7012             if(e.getTarget().nodeName.toLowerCase() != 'th'){
7013                 cell = Roo.get(t).findParent('th', false, true);
7014                 if (!cell) {
7015                     Roo.log("failed to find th in thead?");
7016                     Roo.log(e.getTarget());
7017                     return;
7018                 }
7019             }
7020             
7021             var cellIndex = cell.dom.cellIndex;
7022             
7023             var ename = name == 'touchstart' ? 'click' : name;
7024             this.fireEvent("header" + ename, this, cellIndex, e);
7025             
7026             return;
7027         }
7028         
7029         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7030             cell = Roo.get(t).findParent('td', false, true);
7031             if (!cell) {
7032                 Roo.log("failed to find th in tbody?");
7033                 Roo.log(e.getTarget());
7034                 return;
7035             }
7036         }
7037         
7038         var row = cell.findParent('tr', false, true);
7039         var cellIndex = cell.dom.cellIndex;
7040         var rowIndex = row.dom.rowIndex - 1;
7041         
7042         if(row !== false){
7043             
7044             this.fireEvent("row" + name, this, rowIndex, e);
7045             
7046             if(cell !== false){
7047             
7048                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7049             }
7050         }
7051         
7052     },
7053     
7054     onMouseover : function(e, el)
7055     {
7056         var cell = Roo.get(el);
7057         
7058         if(!cell){
7059             return;
7060         }
7061         
7062         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7063             cell = cell.findParent('td', false, true);
7064         }
7065         
7066         var row = cell.findParent('tr', false, true);
7067         var cellIndex = cell.dom.cellIndex;
7068         var rowIndex = row.dom.rowIndex - 1; // start from 0
7069         
7070         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7071         
7072     },
7073     
7074     onMouseout : function(e, el)
7075     {
7076         var cell = Roo.get(el);
7077         
7078         if(!cell){
7079             return;
7080         }
7081         
7082         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7083             cell = cell.findParent('td', false, true);
7084         }
7085         
7086         var row = cell.findParent('tr', false, true);
7087         var cellIndex = cell.dom.cellIndex;
7088         var rowIndex = row.dom.rowIndex - 1; // start from 0
7089         
7090         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7091         
7092     },
7093     
7094     onClick : function(e, el)
7095     {
7096         var cell = Roo.get(el);
7097         
7098         if(!cell || (!this.cellSelection && !this.rowSelection)){
7099             return;
7100         }
7101         
7102         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7103             cell = cell.findParent('td', false, true);
7104         }
7105         
7106         if(!cell || typeof(cell) == 'undefined'){
7107             return;
7108         }
7109         
7110         var row = cell.findParent('tr', false, true);
7111         
7112         if(!row || typeof(row) == 'undefined'){
7113             return;
7114         }
7115         
7116         var cellIndex = cell.dom.cellIndex;
7117         var rowIndex = this.getRowIndex(row);
7118         
7119         // why??? - should these not be based on SelectionModel?
7120         if(this.cellSelection){
7121             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7122         }
7123         
7124         if(this.rowSelection){
7125             this.fireEvent('rowclick', this, row, rowIndex, e);
7126         }
7127         
7128         
7129     },
7130         
7131     onDblClick : function(e,el)
7132     {
7133         var cell = Roo.get(el);
7134         
7135         if(!cell || (!this.cellSelection && !this.rowSelection)){
7136             return;
7137         }
7138         
7139         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7140             cell = cell.findParent('td', false, true);
7141         }
7142         
7143         if(!cell || typeof(cell) == 'undefined'){
7144             return;
7145         }
7146         
7147         var row = cell.findParent('tr', false, true);
7148         
7149         if(!row || typeof(row) == 'undefined'){
7150             return;
7151         }
7152         
7153         var cellIndex = cell.dom.cellIndex;
7154         var rowIndex = this.getRowIndex(row);
7155         
7156         if(this.cellSelection){
7157             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7158         }
7159         
7160         if(this.rowSelection){
7161             this.fireEvent('rowdblclick', this, row, rowIndex, e);
7162         }
7163     },
7164     
7165     sort : function(e,el)
7166     {
7167         var col = Roo.get(el);
7168         
7169         if(!col.hasClass('sortable')){
7170             return;
7171         }
7172         
7173         var sort = col.attr('sort');
7174         var dir = 'ASC';
7175         
7176         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7177             dir = 'DESC';
7178         }
7179         
7180         this.store.sortInfo = {field : sort, direction : dir};
7181         
7182         if (this.footer) {
7183             Roo.log("calling footer first");
7184             this.footer.onClick('first');
7185         } else {
7186         
7187             this.store.load({ params : { start : 0 } });
7188         }
7189     },
7190     
7191     renderHeader : function()
7192     {
7193         var header = {
7194             tag: 'thead',
7195             cn : []
7196         };
7197         
7198         var cm = this.cm;
7199         this.totalWidth = 0;
7200         
7201         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7202             
7203             var config = cm.config[i];
7204             
7205             var c = {
7206                 tag: 'th',
7207                 cls : 'x-hcol-' + i,
7208                 style : '',
7209                 html: cm.getColumnHeader(i)
7210             };
7211             
7212             var hh = '';
7213             
7214             if(typeof(config.sortable) != 'undefined' && config.sortable){
7215                 c.cls = 'sortable';
7216                 c.html = '<i class="glyphicon"></i>' + c.html;
7217             }
7218             
7219             // could use BS4 hidden-..-down 
7220             
7221             if(typeof(config.lgHeader) != 'undefined'){
7222                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
7223             }
7224             
7225             if(typeof(config.mdHeader) != 'undefined'){
7226                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
7227             }
7228             
7229             if(typeof(config.smHeader) != 'undefined'){
7230                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
7231             }
7232             
7233             if(typeof(config.xsHeader) != 'undefined'){
7234                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
7235             }
7236             
7237             if(hh.length){
7238                 c.html = hh;
7239             }
7240             
7241             if(typeof(config.tooltip) != 'undefined'){
7242                 c.tooltip = config.tooltip;
7243             }
7244             
7245             if(typeof(config.colspan) != 'undefined'){
7246                 c.colspan = config.colspan;
7247             }
7248             
7249             if(typeof(config.hidden) != 'undefined' && config.hidden){
7250                 c.style += ' display:none;';
7251             }
7252             
7253             if(typeof(config.dataIndex) != 'undefined'){
7254                 c.sort = config.dataIndex;
7255             }
7256             
7257            
7258             
7259             if(typeof(config.align) != 'undefined' && config.align.length){
7260                 c.style += ' text-align:' + config.align + ';';
7261             }
7262             
7263             if(typeof(config.width) != 'undefined'){
7264                 c.style += ' width:' + config.width + 'px;';
7265                 this.totalWidth += config.width;
7266             } else {
7267                 this.totalWidth += 100; // assume minimum of 100 per column?
7268             }
7269             
7270             if(typeof(config.cls) != 'undefined'){
7271                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
7272             }
7273             
7274             ['xs','sm','md','lg'].map(function(size){
7275                 
7276                 if(typeof(config[size]) == 'undefined'){
7277                     return;
7278                 }
7279                  
7280                 if (!config[size]) { // 0 = hidden
7281                     // BS 4 '0' is treated as hide that column and below.
7282                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
7283                     return;
7284                 }
7285                 
7286                 c.cls += ' col-' + size + '-' + config[size] + (
7287                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7288                 );
7289                 
7290                 
7291             });
7292             
7293             header.cn.push(c)
7294         }
7295         
7296         return header;
7297     },
7298     
7299     renderBody : function()
7300     {
7301         var body = {
7302             tag: 'tbody',
7303             cn : [
7304                 {
7305                     tag: 'tr',
7306                     cn : [
7307                         {
7308                             tag : 'td',
7309                             colspan :  this.cm.getColumnCount()
7310                         }
7311                     ]
7312                 }
7313             ]
7314         };
7315         
7316         return body;
7317     },
7318     
7319     renderFooter : function()
7320     {
7321         var footer = {
7322             tag: 'tfoot',
7323             cn : [
7324                 {
7325                     tag: 'tr',
7326                     cn : [
7327                         {
7328                             tag : 'td',
7329                             colspan :  this.cm.getColumnCount()
7330                         }
7331                     ]
7332                 }
7333             ]
7334         };
7335         
7336         return footer;
7337     },
7338     
7339     
7340     
7341     onLoad : function()
7342     {
7343 //        Roo.log('ds onload');
7344         this.clear();
7345         
7346         var _this = this;
7347         var cm = this.cm;
7348         var ds = this.store;
7349         
7350         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7351             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
7352             if (_this.store.sortInfo) {
7353                     
7354                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
7355                     e.select('i', true).addClass(['glyphicon-arrow-up']);
7356                 }
7357                 
7358                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
7359                     e.select('i', true).addClass(['glyphicon-arrow-down']);
7360                 }
7361             }
7362         });
7363         
7364         var tbody =  this.mainBody;
7365               
7366         if(ds.getCount() > 0){
7367             ds.data.each(function(d,rowIndex){
7368                 var row =  this.renderRow(cm, ds, rowIndex);
7369                 
7370                 tbody.createChild(row);
7371                 
7372                 var _this = this;
7373                 
7374                 if(row.cellObjects.length){
7375                     Roo.each(row.cellObjects, function(r){
7376                         _this.renderCellObject(r);
7377                     })
7378                 }
7379                 
7380             }, this);
7381         }
7382         
7383         var tfoot = this.el.select('tfoot', true).first();
7384         
7385         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
7386             
7387             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
7388             
7389             var total = this.ds.getTotalCount();
7390             
7391             if(this.footer.pageSize < total){
7392                 this.mainFoot.show();
7393             }
7394         }
7395         
7396         Roo.each(this.el.select('tbody td', true).elements, function(e){
7397             e.on('mouseover', _this.onMouseover, _this);
7398         });
7399         
7400         Roo.each(this.el.select('tbody td', true).elements, function(e){
7401             e.on('mouseout', _this.onMouseout, _this);
7402         });
7403         this.fireEvent('rowsrendered', this);
7404         
7405         this.autoSize();
7406     },
7407     
7408     
7409     onUpdate : function(ds,record)
7410     {
7411         this.refreshRow(record);
7412         this.autoSize();
7413     },
7414     
7415     onRemove : function(ds, record, index, isUpdate){
7416         if(isUpdate !== true){
7417             this.fireEvent("beforerowremoved", this, index, record);
7418         }
7419         var bt = this.mainBody.dom;
7420         
7421         var rows = this.el.select('tbody > tr', true).elements;
7422         
7423         if(typeof(rows[index]) != 'undefined'){
7424             bt.removeChild(rows[index].dom);
7425         }
7426         
7427 //        if(bt.rows[index]){
7428 //            bt.removeChild(bt.rows[index]);
7429 //        }
7430         
7431         if(isUpdate !== true){
7432             //this.stripeRows(index);
7433             //this.syncRowHeights(index, index);
7434             //this.layout();
7435             this.fireEvent("rowremoved", this, index, record);
7436         }
7437     },
7438     
7439     onAdd : function(ds, records, rowIndex)
7440     {
7441         //Roo.log('on Add called');
7442         // - note this does not handle multiple adding very well..
7443         var bt = this.mainBody.dom;
7444         for (var i =0 ; i < records.length;i++) {
7445             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7446             //Roo.log(records[i]);
7447             //Roo.log(this.store.getAt(rowIndex+i));
7448             this.insertRow(this.store, rowIndex + i, false);
7449             return;
7450         }
7451         
7452     },
7453     
7454     
7455     refreshRow : function(record){
7456         var ds = this.store, index;
7457         if(typeof record == 'number'){
7458             index = record;
7459             record = ds.getAt(index);
7460         }else{
7461             index = ds.indexOf(record);
7462         }
7463         this.insertRow(ds, index, true);
7464         this.autoSize();
7465         this.onRemove(ds, record, index+1, true);
7466         this.autoSize();
7467         //this.syncRowHeights(index, index);
7468         //this.layout();
7469         this.fireEvent("rowupdated", this, index, record);
7470     },
7471     
7472     insertRow : function(dm, rowIndex, isUpdate){
7473         
7474         if(!isUpdate){
7475             this.fireEvent("beforerowsinserted", this, rowIndex);
7476         }
7477             //var s = this.getScrollState();
7478         var row = this.renderRow(this.cm, this.store, rowIndex);
7479         // insert before rowIndex..
7480         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7481         
7482         var _this = this;
7483                 
7484         if(row.cellObjects.length){
7485             Roo.each(row.cellObjects, function(r){
7486                 _this.renderCellObject(r);
7487             })
7488         }
7489             
7490         if(!isUpdate){
7491             this.fireEvent("rowsinserted", this, rowIndex);
7492             //this.syncRowHeights(firstRow, lastRow);
7493             //this.stripeRows(firstRow);
7494             //this.layout();
7495         }
7496         
7497     },
7498     
7499     
7500     getRowDom : function(rowIndex)
7501     {
7502         var rows = this.el.select('tbody > tr', true).elements;
7503         
7504         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7505         
7506     },
7507     // returns the object tree for a tr..
7508   
7509     
7510     renderRow : function(cm, ds, rowIndex) 
7511     {
7512         var d = ds.getAt(rowIndex);
7513         
7514         var row = {
7515             tag : 'tr',
7516             cls : 'x-row-' + rowIndex,
7517             cn : []
7518         };
7519             
7520         var cellObjects = [];
7521         
7522         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7523             var config = cm.config[i];
7524             
7525             var renderer = cm.getRenderer(i);
7526             var value = '';
7527             var id = false;
7528             
7529             if(typeof(renderer) !== 'undefined'){
7530                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7531             }
7532             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7533             // and are rendered into the cells after the row is rendered - using the id for the element.
7534             
7535             if(typeof(value) === 'object'){
7536                 id = Roo.id();
7537                 cellObjects.push({
7538                     container : id,
7539                     cfg : value 
7540                 })
7541             }
7542             
7543             var rowcfg = {
7544                 record: d,
7545                 rowIndex : rowIndex,
7546                 colIndex : i,
7547                 rowClass : ''
7548             };
7549
7550             this.fireEvent('rowclass', this, rowcfg);
7551             
7552             var td = {
7553                 tag: 'td',
7554                 cls : rowcfg.rowClass + ' x-col-' + i,
7555                 style: '',
7556                 html: (typeof(value) === 'object') ? '' : value
7557             };
7558             
7559             if (id) {
7560                 td.id = id;
7561             }
7562             
7563             if(typeof(config.colspan) != 'undefined'){
7564                 td.colspan = config.colspan;
7565             }
7566             
7567             if(typeof(config.hidden) != 'undefined' && config.hidden){
7568                 td.style += ' display:none;';
7569             }
7570             
7571             if(typeof(config.align) != 'undefined' && config.align.length){
7572                 td.style += ' text-align:' + config.align + ';';
7573             }
7574             if(typeof(config.valign) != 'undefined' && config.valign.length){
7575                 td.style += ' vertical-align:' + config.valign + ';';
7576             }
7577             
7578             if(typeof(config.width) != 'undefined'){
7579                 td.style += ' width:' +  config.width + 'px;';
7580             }
7581             
7582             if(typeof(config.cursor) != 'undefined'){
7583                 td.style += ' cursor:' +  config.cursor + ';';
7584             }
7585             
7586             if(typeof(config.cls) != 'undefined'){
7587                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7588             }
7589             
7590             ['xs','sm','md','lg'].map(function(size){
7591                 
7592                 if(typeof(config[size]) == 'undefined'){
7593                     return;
7594                 }
7595                 
7596                 
7597                   
7598                 if (!config[size]) { // 0 = hidden
7599                     // BS 4 '0' is treated as hide that column and below.
7600                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
7601                     return;
7602                 }
7603                 
7604                 td.cls += ' col-' + size + '-' + config[size] + (
7605                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
7606                 );
7607                  
7608
7609             });
7610             
7611             row.cn.push(td);
7612            
7613         }
7614         
7615         row.cellObjects = cellObjects;
7616         
7617         return row;
7618           
7619     },
7620     
7621     
7622     
7623     onBeforeLoad : function()
7624     {
7625         
7626     },
7627      /**
7628      * Remove all rows
7629      */
7630     clear : function()
7631     {
7632         this.el.select('tbody', true).first().dom.innerHTML = '';
7633     },
7634     /**
7635      * Show or hide a row.
7636      * @param {Number} rowIndex to show or hide
7637      * @param {Boolean} state hide
7638      */
7639     setRowVisibility : function(rowIndex, state)
7640     {
7641         var bt = this.mainBody.dom;
7642         
7643         var rows = this.el.select('tbody > tr', true).elements;
7644         
7645         if(typeof(rows[rowIndex]) == 'undefined'){
7646             return;
7647         }
7648         rows[rowIndex].dom.style.display = state ? '' : 'none';
7649     },
7650     
7651     
7652     getSelectionModel : function(){
7653         if(!this.selModel){
7654             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7655         }
7656         return this.selModel;
7657     },
7658     /*
7659      * Render the Roo.bootstrap object from renderder
7660      */
7661     renderCellObject : function(r)
7662     {
7663         var _this = this;
7664         
7665         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7666         
7667         var t = r.cfg.render(r.container);
7668         
7669         if(r.cfg.cn){
7670             Roo.each(r.cfg.cn, function(c){
7671                 var child = {
7672                     container: t.getChildContainer(),
7673                     cfg: c
7674                 };
7675                 _this.renderCellObject(child);
7676             })
7677         }
7678     },
7679     
7680     getRowIndex : function(row)
7681     {
7682         var rowIndex = -1;
7683         
7684         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7685             if(el != row){
7686                 return;
7687             }
7688             
7689             rowIndex = index;
7690         });
7691         
7692         return rowIndex;
7693     },
7694      /**
7695      * Returns the grid's underlying element = used by panel.Grid
7696      * @return {Element} The element
7697      */
7698     getGridEl : function(){
7699         return this.el;
7700     },
7701      /**
7702      * Forces a resize - used by panel.Grid
7703      * @return {Element} The element
7704      */
7705     autoSize : function()
7706     {
7707         //var ctr = Roo.get(this.container.dom.parentElement);
7708         var ctr = Roo.get(this.el.dom);
7709         
7710         var thd = this.getGridEl().select('thead',true).first();
7711         var tbd = this.getGridEl().select('tbody', true).first();
7712         var tfd = this.getGridEl().select('tfoot', true).first();
7713         
7714         var cw = ctr.getWidth();
7715         
7716         if (tbd) {
7717             
7718             tbd.setWidth(ctr.getWidth());
7719             // if the body has a max height - and then scrolls - we should perhaps set up the height here
7720             // this needs fixing for various usage - currently only hydra job advers I think..
7721             //tdb.setHeight(
7722             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7723             //); 
7724             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7725             cw -= barsize;
7726         }
7727         cw = Math.max(cw, this.totalWidth);
7728         this.getGridEl().select('tr',true).setWidth(cw);
7729         // resize 'expandable coloumn?
7730         
7731         return; // we doe not have a view in this design..
7732         
7733     },
7734     onBodyScroll: function()
7735     {
7736         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7737         if(this.mainHead){
7738             this.mainHead.setStyle({
7739                 'position' : 'relative',
7740                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7741             });
7742         }
7743         
7744         if(this.lazyLoad){
7745             
7746             var scrollHeight = this.mainBody.dom.scrollHeight;
7747             
7748             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7749             
7750             var height = this.mainBody.getHeight();
7751             
7752             if(scrollHeight - height == scrollTop) {
7753                 
7754                 var total = this.ds.getTotalCount();
7755                 
7756                 if(this.footer.cursor + this.footer.pageSize < total){
7757                     
7758                     this.footer.ds.load({
7759                         params : {
7760                             start : this.footer.cursor + this.footer.pageSize,
7761                             limit : this.footer.pageSize
7762                         },
7763                         add : true
7764                     });
7765                 }
7766             }
7767             
7768         }
7769     },
7770     
7771     onHeaderChange : function()
7772     {
7773         var header = this.renderHeader();
7774         var table = this.el.select('table', true).first();
7775         
7776         this.mainHead.remove();
7777         this.mainHead = table.createChild(header, this.mainBody, false);
7778     },
7779     
7780     onHiddenChange : function(colModel, colIndex, hidden)
7781     {
7782         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7783         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7784         
7785         this.CSS.updateRule(thSelector, "display", "");
7786         this.CSS.updateRule(tdSelector, "display", "");
7787         
7788         if(hidden){
7789             this.CSS.updateRule(thSelector, "display", "none");
7790             this.CSS.updateRule(tdSelector, "display", "none");
7791         }
7792         
7793         this.onHeaderChange();
7794         this.onLoad();
7795     },
7796     
7797     setColumnWidth: function(col_index, width)
7798     {
7799         // width = "md-2 xs-2..."
7800         if(!this.colModel.config[col_index]) {
7801             return;
7802         }
7803         
7804         var w = width.split(" ");
7805         
7806         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7807         
7808         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7809         
7810         
7811         for(var j = 0; j < w.length; j++) {
7812             
7813             if(!w[j]) {
7814                 continue;
7815             }
7816             
7817             var size_cls = w[j].split("-");
7818             
7819             if(!Number.isInteger(size_cls[1] * 1)) {
7820                 continue;
7821             }
7822             
7823             if(!this.colModel.config[col_index][size_cls[0]]) {
7824                 continue;
7825             }
7826             
7827             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7828                 continue;
7829             }
7830             
7831             h_row[0].classList.replace(
7832                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7833                 "col-"+size_cls[0]+"-"+size_cls[1]
7834             );
7835             
7836             for(var i = 0; i < rows.length; i++) {
7837                 
7838                 var size_cls = w[j].split("-");
7839                 
7840                 if(!Number.isInteger(size_cls[1] * 1)) {
7841                     continue;
7842                 }
7843                 
7844                 if(!this.colModel.config[col_index][size_cls[0]]) {
7845                     continue;
7846                 }
7847                 
7848                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7849                     continue;
7850                 }
7851                 
7852                 rows[i].classList.replace(
7853                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7854                     "col-"+size_cls[0]+"-"+size_cls[1]
7855                 );
7856             }
7857             
7858             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7859         }
7860     }
7861 });
7862
7863  
7864
7865  /*
7866  * - LGPL
7867  *
7868  * table cell
7869  * 
7870  */
7871
7872 /**
7873  * @class Roo.bootstrap.TableCell
7874  * @extends Roo.bootstrap.Component
7875  * Bootstrap TableCell class
7876  * @cfg {String} html cell contain text
7877  * @cfg {String} cls cell class
7878  * @cfg {String} tag cell tag (td|th) default td
7879  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7880  * @cfg {String} align Aligns the content in a cell
7881  * @cfg {String} axis Categorizes cells
7882  * @cfg {String} bgcolor Specifies the background color of a cell
7883  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7884  * @cfg {Number} colspan Specifies the number of columns a cell should span
7885  * @cfg {String} headers Specifies one or more header cells a cell is related to
7886  * @cfg {Number} height Sets the height of a cell
7887  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7888  * @cfg {Number} rowspan Sets the number of rows a cell should span
7889  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7890  * @cfg {String} valign Vertical aligns the content in a cell
7891  * @cfg {Number} width Specifies the width of a cell
7892  * 
7893  * @constructor
7894  * Create a new TableCell
7895  * @param {Object} config The config object
7896  */
7897
7898 Roo.bootstrap.TableCell = function(config){
7899     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7900 };
7901
7902 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7903     
7904     html: false,
7905     cls: false,
7906     tag: false,
7907     abbr: false,
7908     align: false,
7909     axis: false,
7910     bgcolor: false,
7911     charoff: false,
7912     colspan: false,
7913     headers: false,
7914     height: false,
7915     nowrap: false,
7916     rowspan: false,
7917     scope: false,
7918     valign: false,
7919     width: false,
7920     
7921     
7922     getAutoCreate : function(){
7923         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7924         
7925         cfg = {
7926             tag: 'td'
7927         };
7928         
7929         if(this.tag){
7930             cfg.tag = this.tag;
7931         }
7932         
7933         if (this.html) {
7934             cfg.html=this.html
7935         }
7936         if (this.cls) {
7937             cfg.cls=this.cls
7938         }
7939         if (this.abbr) {
7940             cfg.abbr=this.abbr
7941         }
7942         if (this.align) {
7943             cfg.align=this.align
7944         }
7945         if (this.axis) {
7946             cfg.axis=this.axis
7947         }
7948         if (this.bgcolor) {
7949             cfg.bgcolor=this.bgcolor
7950         }
7951         if (this.charoff) {
7952             cfg.charoff=this.charoff
7953         }
7954         if (this.colspan) {
7955             cfg.colspan=this.colspan
7956         }
7957         if (this.headers) {
7958             cfg.headers=this.headers
7959         }
7960         if (this.height) {
7961             cfg.height=this.height
7962         }
7963         if (this.nowrap) {
7964             cfg.nowrap=this.nowrap
7965         }
7966         if (this.rowspan) {
7967             cfg.rowspan=this.rowspan
7968         }
7969         if (this.scope) {
7970             cfg.scope=this.scope
7971         }
7972         if (this.valign) {
7973             cfg.valign=this.valign
7974         }
7975         if (this.width) {
7976             cfg.width=this.width
7977         }
7978         
7979         
7980         return cfg;
7981     }
7982    
7983 });
7984
7985  
7986
7987  /*
7988  * - LGPL
7989  *
7990  * table row
7991  * 
7992  */
7993
7994 /**
7995  * @class Roo.bootstrap.TableRow
7996  * @extends Roo.bootstrap.Component
7997  * Bootstrap TableRow class
7998  * @cfg {String} cls row class
7999  * @cfg {String} align Aligns the content in a table row
8000  * @cfg {String} bgcolor Specifies a background color for a table row
8001  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8002  * @cfg {String} valign Vertical aligns the content in a table row
8003  * 
8004  * @constructor
8005  * Create a new TableRow
8006  * @param {Object} config The config object
8007  */
8008
8009 Roo.bootstrap.TableRow = function(config){
8010     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8011 };
8012
8013 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
8014     
8015     cls: false,
8016     align: false,
8017     bgcolor: false,
8018     charoff: false,
8019     valign: false,
8020     
8021     getAutoCreate : function(){
8022         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8023         
8024         cfg = {
8025             tag: 'tr'
8026         };
8027             
8028         if(this.cls){
8029             cfg.cls = this.cls;
8030         }
8031         if(this.align){
8032             cfg.align = this.align;
8033         }
8034         if(this.bgcolor){
8035             cfg.bgcolor = this.bgcolor;
8036         }
8037         if(this.charoff){
8038             cfg.charoff = this.charoff;
8039         }
8040         if(this.valign){
8041             cfg.valign = this.valign;
8042         }
8043         
8044         return cfg;
8045     }
8046    
8047 });
8048
8049  
8050
8051  /*
8052  * - LGPL
8053  *
8054  * table body
8055  * 
8056  */
8057
8058 /**
8059  * @class Roo.bootstrap.TableBody
8060  * @extends Roo.bootstrap.Component
8061  * Bootstrap TableBody class
8062  * @cfg {String} cls element class
8063  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8064  * @cfg {String} align Aligns the content inside the element
8065  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8066  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8067  * 
8068  * @constructor
8069  * Create a new TableBody
8070  * @param {Object} config The config object
8071  */
8072
8073 Roo.bootstrap.TableBody = function(config){
8074     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8075 };
8076
8077 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
8078     
8079     cls: false,
8080     tag: false,
8081     align: false,
8082     charoff: false,
8083     valign: false,
8084     
8085     getAutoCreate : function(){
8086         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8087         
8088         cfg = {
8089             tag: 'tbody'
8090         };
8091             
8092         if (this.cls) {
8093             cfg.cls=this.cls
8094         }
8095         if(this.tag){
8096             cfg.tag = this.tag;
8097         }
8098         
8099         if(this.align){
8100             cfg.align = this.align;
8101         }
8102         if(this.charoff){
8103             cfg.charoff = this.charoff;
8104         }
8105         if(this.valign){
8106             cfg.valign = this.valign;
8107         }
8108         
8109         return cfg;
8110     }
8111     
8112     
8113 //    initEvents : function()
8114 //    {
8115 //        
8116 //        if(!this.store){
8117 //            return;
8118 //        }
8119 //        
8120 //        this.store = Roo.factory(this.store, Roo.data);
8121 //        this.store.on('load', this.onLoad, this);
8122 //        
8123 //        this.store.load();
8124 //        
8125 //    },
8126 //    
8127 //    onLoad: function () 
8128 //    {   
8129 //        this.fireEvent('load', this);
8130 //    }
8131 //    
8132 //   
8133 });
8134
8135  
8136
8137  /*
8138  * Based on:
8139  * Ext JS Library 1.1.1
8140  * Copyright(c) 2006-2007, Ext JS, LLC.
8141  *
8142  * Originally Released Under LGPL - original licence link has changed is not relivant.
8143  *
8144  * Fork - LGPL
8145  * <script type="text/javascript">
8146  */
8147
8148 // as we use this in bootstrap.
8149 Roo.namespace('Roo.form');
8150  /**
8151  * @class Roo.form.Action
8152  * Internal Class used to handle form actions
8153  * @constructor
8154  * @param {Roo.form.BasicForm} el The form element or its id
8155  * @param {Object} config Configuration options
8156  */
8157
8158  
8159  
8160 // define the action interface
8161 Roo.form.Action = function(form, options){
8162     this.form = form;
8163     this.options = options || {};
8164 };
8165 /**
8166  * Client Validation Failed
8167  * @const 
8168  */
8169 Roo.form.Action.CLIENT_INVALID = 'client';
8170 /**
8171  * Server Validation Failed
8172  * @const 
8173  */
8174 Roo.form.Action.SERVER_INVALID = 'server';
8175  /**
8176  * Connect to Server Failed
8177  * @const 
8178  */
8179 Roo.form.Action.CONNECT_FAILURE = 'connect';
8180 /**
8181  * Reading Data from Server Failed
8182  * @const 
8183  */
8184 Roo.form.Action.LOAD_FAILURE = 'load';
8185
8186 Roo.form.Action.prototype = {
8187     type : 'default',
8188     failureType : undefined,
8189     response : undefined,
8190     result : undefined,
8191
8192     // interface method
8193     run : function(options){
8194
8195     },
8196
8197     // interface method
8198     success : function(response){
8199
8200     },
8201
8202     // interface method
8203     handleResponse : function(response){
8204
8205     },
8206
8207     // default connection failure
8208     failure : function(response){
8209         
8210         this.response = response;
8211         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8212         this.form.afterAction(this, false);
8213     },
8214
8215     processResponse : function(response){
8216         this.response = response;
8217         if(!response.responseText){
8218             return true;
8219         }
8220         this.result = this.handleResponse(response);
8221         return this.result;
8222     },
8223
8224     // utility functions used internally
8225     getUrl : function(appendParams){
8226         var url = this.options.url || this.form.url || this.form.el.dom.action;
8227         if(appendParams){
8228             var p = this.getParams();
8229             if(p){
8230                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
8231             }
8232         }
8233         return url;
8234     },
8235
8236     getMethod : function(){
8237         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
8238     },
8239
8240     getParams : function(){
8241         var bp = this.form.baseParams;
8242         var p = this.options.params;
8243         if(p){
8244             if(typeof p == "object"){
8245                 p = Roo.urlEncode(Roo.applyIf(p, bp));
8246             }else if(typeof p == 'string' && bp){
8247                 p += '&' + Roo.urlEncode(bp);
8248             }
8249         }else if(bp){
8250             p = Roo.urlEncode(bp);
8251         }
8252         return p;
8253     },
8254
8255     createCallback : function(){
8256         return {
8257             success: this.success,
8258             failure: this.failure,
8259             scope: this,
8260             timeout: (this.form.timeout*1000),
8261             upload: this.form.fileUpload ? this.success : undefined
8262         };
8263     }
8264 };
8265
8266 Roo.form.Action.Submit = function(form, options){
8267     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
8268 };
8269
8270 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
8271     type : 'submit',
8272
8273     haveProgress : false,
8274     uploadComplete : false,
8275     
8276     // uploadProgress indicator.
8277     uploadProgress : function()
8278     {
8279         if (!this.form.progressUrl) {
8280             return;
8281         }
8282         
8283         if (!this.haveProgress) {
8284             Roo.MessageBox.progress("Uploading", "Uploading");
8285         }
8286         if (this.uploadComplete) {
8287            Roo.MessageBox.hide();
8288            return;
8289         }
8290         
8291         this.haveProgress = true;
8292    
8293         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
8294         
8295         var c = new Roo.data.Connection();
8296         c.request({
8297             url : this.form.progressUrl,
8298             params: {
8299                 id : uid
8300             },
8301             method: 'GET',
8302             success : function(req){
8303                //console.log(data);
8304                 var rdata = false;
8305                 var edata;
8306                 try  {
8307                    rdata = Roo.decode(req.responseText)
8308                 } catch (e) {
8309                     Roo.log("Invalid data from server..");
8310                     Roo.log(edata);
8311                     return;
8312                 }
8313                 if (!rdata || !rdata.success) {
8314                     Roo.log(rdata);
8315                     Roo.MessageBox.alert(Roo.encode(rdata));
8316                     return;
8317                 }
8318                 var data = rdata.data;
8319                 
8320                 if (this.uploadComplete) {
8321                    Roo.MessageBox.hide();
8322                    return;
8323                 }
8324                    
8325                 if (data){
8326                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
8327                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
8328                     );
8329                 }
8330                 this.uploadProgress.defer(2000,this);
8331             },
8332        
8333             failure: function(data) {
8334                 Roo.log('progress url failed ');
8335                 Roo.log(data);
8336             },
8337             scope : this
8338         });
8339            
8340     },
8341     
8342     
8343     run : function()
8344     {
8345         // run get Values on the form, so it syncs any secondary forms.
8346         this.form.getValues();
8347         
8348         var o = this.options;
8349         var method = this.getMethod();
8350         var isPost = method == 'POST';
8351         if(o.clientValidation === false || this.form.isValid()){
8352             
8353             if (this.form.progressUrl) {
8354                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
8355                     (new Date() * 1) + '' + Math.random());
8356                     
8357             } 
8358             
8359             
8360             Roo.Ajax.request(Roo.apply(this.createCallback(), {
8361                 form:this.form.el.dom,
8362                 url:this.getUrl(!isPost),
8363                 method: method,
8364                 params:isPost ? this.getParams() : null,
8365                 isUpload: this.form.fileUpload,
8366                 formData : this.form.formData
8367             }));
8368             
8369             this.uploadProgress();
8370
8371         }else if (o.clientValidation !== false){ // client validation failed
8372             this.failureType = Roo.form.Action.CLIENT_INVALID;
8373             this.form.afterAction(this, false);
8374         }
8375     },
8376
8377     success : function(response)
8378     {
8379         this.uploadComplete= true;
8380         if (this.haveProgress) {
8381             Roo.MessageBox.hide();
8382         }
8383         
8384         
8385         var result = this.processResponse(response);
8386         if(result === true || result.success){
8387             this.form.afterAction(this, true);
8388             return;
8389         }
8390         if(result.errors){
8391             this.form.markInvalid(result.errors);
8392             this.failureType = Roo.form.Action.SERVER_INVALID;
8393         }
8394         this.form.afterAction(this, false);
8395     },
8396     failure : function(response)
8397     {
8398         this.uploadComplete= true;
8399         if (this.haveProgress) {
8400             Roo.MessageBox.hide();
8401         }
8402         
8403         this.response = response;
8404         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8405         this.form.afterAction(this, false);
8406     },
8407     
8408     handleResponse : function(response){
8409         if(this.form.errorReader){
8410             var rs = this.form.errorReader.read(response);
8411             var errors = [];
8412             if(rs.records){
8413                 for(var i = 0, len = rs.records.length; i < len; i++) {
8414                     var r = rs.records[i];
8415                     errors[i] = r.data;
8416                 }
8417             }
8418             if(errors.length < 1){
8419                 errors = null;
8420             }
8421             return {
8422                 success : rs.success,
8423                 errors : errors
8424             };
8425         }
8426         var ret = false;
8427         try {
8428             ret = Roo.decode(response.responseText);
8429         } catch (e) {
8430             ret = {
8431                 success: false,
8432                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8433                 errors : []
8434             };
8435         }
8436         return ret;
8437         
8438     }
8439 });
8440
8441
8442 Roo.form.Action.Load = function(form, options){
8443     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8444     this.reader = this.form.reader;
8445 };
8446
8447 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8448     type : 'load',
8449
8450     run : function(){
8451         
8452         Roo.Ajax.request(Roo.apply(
8453                 this.createCallback(), {
8454                     method:this.getMethod(),
8455                     url:this.getUrl(false),
8456                     params:this.getParams()
8457         }));
8458     },
8459
8460     success : function(response){
8461         
8462         var result = this.processResponse(response);
8463         if(result === true || !result.success || !result.data){
8464             this.failureType = Roo.form.Action.LOAD_FAILURE;
8465             this.form.afterAction(this, false);
8466             return;
8467         }
8468         this.form.clearInvalid();
8469         this.form.setValues(result.data);
8470         this.form.afterAction(this, true);
8471     },
8472
8473     handleResponse : function(response){
8474         if(this.form.reader){
8475             var rs = this.form.reader.read(response);
8476             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8477             return {
8478                 success : rs.success,
8479                 data : data
8480             };
8481         }
8482         return Roo.decode(response.responseText);
8483     }
8484 });
8485
8486 Roo.form.Action.ACTION_TYPES = {
8487     'load' : Roo.form.Action.Load,
8488     'submit' : Roo.form.Action.Submit
8489 };/*
8490  * - LGPL
8491  *
8492  * form
8493  *
8494  */
8495
8496 /**
8497  * @class Roo.bootstrap.Form
8498  * @extends Roo.bootstrap.Component
8499  * Bootstrap Form class
8500  * @cfg {String} method  GET | POST (default POST)
8501  * @cfg {String} labelAlign top | left (default top)
8502  * @cfg {String} align left  | right - for navbars
8503  * @cfg {Boolean} loadMask load mask when submit (default true)
8504
8505  *
8506  * @constructor
8507  * Create a new Form
8508  * @param {Object} config The config object
8509  */
8510
8511
8512 Roo.bootstrap.Form = function(config){
8513     
8514     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8515     
8516     Roo.bootstrap.Form.popover.apply();
8517     
8518     this.addEvents({
8519         /**
8520          * @event clientvalidation
8521          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8522          * @param {Form} this
8523          * @param {Boolean} valid true if the form has passed client-side validation
8524          */
8525         clientvalidation: true,
8526         /**
8527          * @event beforeaction
8528          * Fires before any action is performed. Return false to cancel the action.
8529          * @param {Form} this
8530          * @param {Action} action The action to be performed
8531          */
8532         beforeaction: true,
8533         /**
8534          * @event actionfailed
8535          * Fires when an action fails.
8536          * @param {Form} this
8537          * @param {Action} action The action that failed
8538          */
8539         actionfailed : true,
8540         /**
8541          * @event actioncomplete
8542          * Fires when an action is completed.
8543          * @param {Form} this
8544          * @param {Action} action The action that completed
8545          */
8546         actioncomplete : true
8547     });
8548 };
8549
8550 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8551
8552      /**
8553      * @cfg {String} method
8554      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8555      */
8556     method : 'POST',
8557     /**
8558      * @cfg {String} url
8559      * The URL to use for form actions if one isn't supplied in the action options.
8560      */
8561     /**
8562      * @cfg {Boolean} fileUpload
8563      * Set to true if this form is a file upload.
8564      */
8565
8566     /**
8567      * @cfg {Object} baseParams
8568      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8569      */
8570
8571     /**
8572      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8573      */
8574     timeout: 30,
8575     /**
8576      * @cfg {Sting} align (left|right) for navbar forms
8577      */
8578     align : 'left',
8579
8580     // private
8581     activeAction : null,
8582
8583     /**
8584      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8585      * element by passing it or its id or mask the form itself by passing in true.
8586      * @type Mixed
8587      */
8588     waitMsgTarget : false,
8589
8590     loadMask : true,
8591     
8592     /**
8593      * @cfg {Boolean} errorMask (true|false) default false
8594      */
8595     errorMask : false,
8596     
8597     /**
8598      * @cfg {Number} maskOffset Default 100
8599      */
8600     maskOffset : 100,
8601     
8602     /**
8603      * @cfg {Boolean} maskBody
8604      */
8605     maskBody : false,
8606
8607     getAutoCreate : function(){
8608
8609         var cfg = {
8610             tag: 'form',
8611             method : this.method || 'POST',
8612             id : this.id || Roo.id(),
8613             cls : ''
8614         };
8615         if (this.parent().xtype.match(/^Nav/)) {
8616             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8617
8618         }
8619
8620         if (this.labelAlign == 'left' ) {
8621             cfg.cls += ' form-horizontal';
8622         }
8623
8624
8625         return cfg;
8626     },
8627     initEvents : function()
8628     {
8629         this.el.on('submit', this.onSubmit, this);
8630         // this was added as random key presses on the form where triggering form submit.
8631         this.el.on('keypress', function(e) {
8632             if (e.getCharCode() != 13) {
8633                 return true;
8634             }
8635             // we might need to allow it for textareas.. and some other items.
8636             // check e.getTarget().
8637
8638             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8639                 return true;
8640             }
8641
8642             Roo.log("keypress blocked");
8643
8644             e.preventDefault();
8645             return false;
8646         });
8647         
8648     },
8649     // private
8650     onSubmit : function(e){
8651         e.stopEvent();
8652     },
8653
8654      /**
8655      * Returns true if client-side validation on the form is successful.
8656      * @return Boolean
8657      */
8658     isValid : function(){
8659         var items = this.getItems();
8660         var valid = true;
8661         var target = false;
8662         
8663         items.each(function(f){
8664             
8665             if(f.validate()){
8666                 return;
8667             }
8668             
8669             Roo.log('invalid field: ' + f.name);
8670             
8671             valid = false;
8672
8673             if(!target && f.el.isVisible(true)){
8674                 target = f;
8675             }
8676            
8677         });
8678         
8679         if(this.errorMask && !valid){
8680             Roo.bootstrap.Form.popover.mask(this, target);
8681         }
8682         
8683         return valid;
8684     },
8685     
8686     /**
8687      * Returns true if any fields in this form have changed since their original load.
8688      * @return Boolean
8689      */
8690     isDirty : function(){
8691         var dirty = false;
8692         var items = this.getItems();
8693         items.each(function(f){
8694            if(f.isDirty()){
8695                dirty = true;
8696                return false;
8697            }
8698            return true;
8699         });
8700         return dirty;
8701     },
8702      /**
8703      * Performs a predefined action (submit or load) or custom actions you define on this form.
8704      * @param {String} actionName The name of the action type
8705      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8706      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8707      * accept other config options):
8708      * <pre>
8709 Property          Type             Description
8710 ----------------  ---------------  ----------------------------------------------------------------------------------
8711 url               String           The url for the action (defaults to the form's url)
8712 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8713 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8714 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8715                                    validate the form on the client (defaults to false)
8716      * </pre>
8717      * @return {BasicForm} this
8718      */
8719     doAction : function(action, options){
8720         if(typeof action == 'string'){
8721             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8722         }
8723         if(this.fireEvent('beforeaction', this, action) !== false){
8724             this.beforeAction(action);
8725             action.run.defer(100, action);
8726         }
8727         return this;
8728     },
8729
8730     // private
8731     beforeAction : function(action){
8732         var o = action.options;
8733         
8734         if(this.loadMask){
8735             
8736             if(this.maskBody){
8737                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8738             } else {
8739                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8740             }
8741         }
8742         // not really supported yet.. ??
8743
8744         //if(this.waitMsgTarget === true){
8745         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8746         //}else if(this.waitMsgTarget){
8747         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8748         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8749         //}else {
8750         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8751        // }
8752
8753     },
8754
8755     // private
8756     afterAction : function(action, success){
8757         this.activeAction = null;
8758         var o = action.options;
8759
8760         if(this.loadMask){
8761             
8762             if(this.maskBody){
8763                 Roo.get(document.body).unmask();
8764             } else {
8765                 this.el.unmask();
8766             }
8767         }
8768         
8769         //if(this.waitMsgTarget === true){
8770 //            this.el.unmask();
8771         //}else if(this.waitMsgTarget){
8772         //    this.waitMsgTarget.unmask();
8773         //}else{
8774         //    Roo.MessageBox.updateProgress(1);
8775         //    Roo.MessageBox.hide();
8776        // }
8777         //
8778         if(success){
8779             if(o.reset){
8780                 this.reset();
8781             }
8782             Roo.callback(o.success, o.scope, [this, action]);
8783             this.fireEvent('actioncomplete', this, action);
8784
8785         }else{
8786
8787             // failure condition..
8788             // we have a scenario where updates need confirming.
8789             // eg. if a locking scenario exists..
8790             // we look for { errors : { needs_confirm : true }} in the response.
8791             if (
8792                 (typeof(action.result) != 'undefined')  &&
8793                 (typeof(action.result.errors) != 'undefined')  &&
8794                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8795            ){
8796                 var _t = this;
8797                 Roo.log("not supported yet");
8798                  /*
8799
8800                 Roo.MessageBox.confirm(
8801                     "Change requires confirmation",
8802                     action.result.errorMsg,
8803                     function(r) {
8804                         if (r != 'yes') {
8805                             return;
8806                         }
8807                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8808                     }
8809
8810                 );
8811                 */
8812
8813
8814                 return;
8815             }
8816
8817             Roo.callback(o.failure, o.scope, [this, action]);
8818             // show an error message if no failed handler is set..
8819             if (!this.hasListener('actionfailed')) {
8820                 Roo.log("need to add dialog support");
8821                 /*
8822                 Roo.MessageBox.alert("Error",
8823                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8824                         action.result.errorMsg :
8825                         "Saving Failed, please check your entries or try again"
8826                 );
8827                 */
8828             }
8829
8830             this.fireEvent('actionfailed', this, action);
8831         }
8832
8833     },
8834     /**
8835      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8836      * @param {String} id The value to search for
8837      * @return Field
8838      */
8839     findField : function(id){
8840         var items = this.getItems();
8841         var field = items.get(id);
8842         if(!field){
8843              items.each(function(f){
8844                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8845                     field = f;
8846                     return false;
8847                 }
8848                 return true;
8849             });
8850         }
8851         return field || null;
8852     },
8853      /**
8854      * Mark fields in this form invalid in bulk.
8855      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8856      * @return {BasicForm} this
8857      */
8858     markInvalid : function(errors){
8859         if(errors instanceof Array){
8860             for(var i = 0, len = errors.length; i < len; i++){
8861                 var fieldError = errors[i];
8862                 var f = this.findField(fieldError.id);
8863                 if(f){
8864                     f.markInvalid(fieldError.msg);
8865                 }
8866             }
8867         }else{
8868             var field, id;
8869             for(id in errors){
8870                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8871                     field.markInvalid(errors[id]);
8872                 }
8873             }
8874         }
8875         //Roo.each(this.childForms || [], function (f) {
8876         //    f.markInvalid(errors);
8877         //});
8878
8879         return this;
8880     },
8881
8882     /**
8883      * Set values for fields in this form in bulk.
8884      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8885      * @return {BasicForm} this
8886      */
8887     setValues : function(values){
8888         if(values instanceof Array){ // array of objects
8889             for(var i = 0, len = values.length; i < len; i++){
8890                 var v = values[i];
8891                 var f = this.findField(v.id);
8892                 if(f){
8893                     f.setValue(v.value);
8894                     if(this.trackResetOnLoad){
8895                         f.originalValue = f.getValue();
8896                     }
8897                 }
8898             }
8899         }else{ // object hash
8900             var field, id;
8901             for(id in values){
8902                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8903
8904                     if (field.setFromData &&
8905                         field.valueField &&
8906                         field.displayField &&
8907                         // combos' with local stores can
8908                         // be queried via setValue()
8909                         // to set their value..
8910                         (field.store && !field.store.isLocal)
8911                         ) {
8912                         // it's a combo
8913                         var sd = { };
8914                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8915                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8916                         field.setFromData(sd);
8917
8918                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8919                         
8920                         field.setFromData(values);
8921                         
8922                     } else {
8923                         field.setValue(values[id]);
8924                     }
8925
8926
8927                     if(this.trackResetOnLoad){
8928                         field.originalValue = field.getValue();
8929                     }
8930                 }
8931             }
8932         }
8933
8934         //Roo.each(this.childForms || [], function (f) {
8935         //    f.setValues(values);
8936         //});
8937
8938         return this;
8939     },
8940
8941     /**
8942      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8943      * they are returned as an array.
8944      * @param {Boolean} asString
8945      * @return {Object}
8946      */
8947     getValues : function(asString){
8948         //if (this.childForms) {
8949             // copy values from the child forms
8950         //    Roo.each(this.childForms, function (f) {
8951         //        this.setValues(f.getValues());
8952         //    }, this);
8953         //}
8954
8955
8956
8957         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8958         if(asString === true){
8959             return fs;
8960         }
8961         return Roo.urlDecode(fs);
8962     },
8963
8964     /**
8965      * Returns the fields in this form as an object with key/value pairs.
8966      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8967      * @return {Object}
8968      */
8969     getFieldValues : function(with_hidden)
8970     {
8971         var items = this.getItems();
8972         var ret = {};
8973         items.each(function(f){
8974             
8975             if (!f.getName()) {
8976                 return;
8977             }
8978             
8979             var v = f.getValue();
8980             
8981             if (f.inputType =='radio') {
8982                 if (typeof(ret[f.getName()]) == 'undefined') {
8983                     ret[f.getName()] = ''; // empty..
8984                 }
8985
8986                 if (!f.el.dom.checked) {
8987                     return;
8988
8989                 }
8990                 v = f.el.dom.value;
8991
8992             }
8993             
8994             if(f.xtype == 'MoneyField'){
8995                 ret[f.currencyName] = f.getCurrency();
8996             }
8997
8998             // not sure if this supported any more..
8999             if ((typeof(v) == 'object') && f.getRawValue) {
9000                 v = f.getRawValue() ; // dates..
9001             }
9002             // combo boxes where name != hiddenName...
9003             if (f.name !== false && f.name != '' && f.name != f.getName()) {
9004                 ret[f.name] = f.getRawValue();
9005             }
9006             ret[f.getName()] = v;
9007         });
9008
9009         return ret;
9010     },
9011
9012     /**
9013      * Clears all invalid messages in this form.
9014      * @return {BasicForm} this
9015      */
9016     clearInvalid : function(){
9017         var items = this.getItems();
9018
9019         items.each(function(f){
9020            f.clearInvalid();
9021         });
9022
9023         return this;
9024     },
9025
9026     /**
9027      * Resets this form.
9028      * @return {BasicForm} this
9029      */
9030     reset : function(){
9031         var items = this.getItems();
9032         items.each(function(f){
9033             f.reset();
9034         });
9035
9036         Roo.each(this.childForms || [], function (f) {
9037             f.reset();
9038         });
9039
9040
9041         return this;
9042     },
9043     
9044     getItems : function()
9045     {
9046         var r=new Roo.util.MixedCollection(false, function(o){
9047             return o.id || (o.id = Roo.id());
9048         });
9049         var iter = function(el) {
9050             if (el.inputEl) {
9051                 r.add(el);
9052             }
9053             if (!el.items) {
9054                 return;
9055             }
9056             Roo.each(el.items,function(e) {
9057                 iter(e);
9058             });
9059         };
9060
9061         iter(this);
9062         return r;
9063     },
9064     
9065     hideFields : function(items)
9066     {
9067         Roo.each(items, function(i){
9068             
9069             var f = this.findField(i);
9070             
9071             if(!f){
9072                 return;
9073             }
9074             
9075             f.hide();
9076             
9077         }, this);
9078     },
9079     
9080     showFields : function(items)
9081     {
9082         Roo.each(items, function(i){
9083             
9084             var f = this.findField(i);
9085             
9086             if(!f){
9087                 return;
9088             }
9089             
9090             f.show();
9091             
9092         }, this);
9093     }
9094
9095 });
9096
9097 Roo.apply(Roo.bootstrap.Form, {
9098     
9099     popover : {
9100         
9101         padding : 5,
9102         
9103         isApplied : false,
9104         
9105         isMasked : false,
9106         
9107         form : false,
9108         
9109         target : false,
9110         
9111         toolTip : false,
9112         
9113         intervalID : false,
9114         
9115         maskEl : false,
9116         
9117         apply : function()
9118         {
9119             if(this.isApplied){
9120                 return;
9121             }
9122             
9123             this.maskEl = {
9124                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9125                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9126                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9127                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9128             };
9129             
9130             this.maskEl.top.enableDisplayMode("block");
9131             this.maskEl.left.enableDisplayMode("block");
9132             this.maskEl.bottom.enableDisplayMode("block");
9133             this.maskEl.right.enableDisplayMode("block");
9134             
9135             this.toolTip = new Roo.bootstrap.Tooltip({
9136                 cls : 'roo-form-error-popover',
9137                 alignment : {
9138                     'left' : ['r-l', [-2,0], 'right'],
9139                     'right' : ['l-r', [2,0], 'left'],
9140                     'bottom' : ['tl-bl', [0,2], 'top'],
9141                     'top' : [ 'bl-tl', [0,-2], 'bottom']
9142                 }
9143             });
9144             
9145             this.toolTip.render(Roo.get(document.body));
9146
9147             this.toolTip.el.enableDisplayMode("block");
9148             
9149             Roo.get(document.body).on('click', function(){
9150                 this.unmask();
9151             }, this);
9152             
9153             Roo.get(document.body).on('touchstart', function(){
9154                 this.unmask();
9155             }, this);
9156             
9157             this.isApplied = true
9158         },
9159         
9160         mask : function(form, target)
9161         {
9162             this.form = form;
9163             
9164             this.target = target;
9165             
9166             if(!this.form.errorMask || !target.el){
9167                 return;
9168             }
9169             
9170             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9171             
9172             Roo.log(scrollable);
9173             
9174             var ot = this.target.el.calcOffsetsTo(scrollable);
9175             
9176             var scrollTo = ot[1] - this.form.maskOffset;
9177             
9178             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9179             
9180             scrollable.scrollTo('top', scrollTo);
9181             
9182             var box = this.target.el.getBox();
9183             Roo.log(box);
9184             var zIndex = Roo.bootstrap.Modal.zIndex++;
9185
9186             
9187             this.maskEl.top.setStyle('position', 'absolute');
9188             this.maskEl.top.setStyle('z-index', zIndex);
9189             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9190             this.maskEl.top.setLeft(0);
9191             this.maskEl.top.setTop(0);
9192             this.maskEl.top.show();
9193             
9194             this.maskEl.left.setStyle('position', 'absolute');
9195             this.maskEl.left.setStyle('z-index', zIndex);
9196             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9197             this.maskEl.left.setLeft(0);
9198             this.maskEl.left.setTop(box.y - this.padding);
9199             this.maskEl.left.show();
9200
9201             this.maskEl.bottom.setStyle('position', 'absolute');
9202             this.maskEl.bottom.setStyle('z-index', zIndex);
9203             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9204             this.maskEl.bottom.setLeft(0);
9205             this.maskEl.bottom.setTop(box.bottom + this.padding);
9206             this.maskEl.bottom.show();
9207
9208             this.maskEl.right.setStyle('position', 'absolute');
9209             this.maskEl.right.setStyle('z-index', zIndex);
9210             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9211             this.maskEl.right.setLeft(box.right + this.padding);
9212             this.maskEl.right.setTop(box.y - this.padding);
9213             this.maskEl.right.show();
9214
9215             this.toolTip.bindEl = this.target.el;
9216
9217             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
9218
9219             var tip = this.target.blankText;
9220
9221             if(this.target.getValue() !== '' ) {
9222                 
9223                 if (this.target.invalidText.length) {
9224                     tip = this.target.invalidText;
9225                 } else if (this.target.regexText.length){
9226                     tip = this.target.regexText;
9227                 }
9228             }
9229
9230             this.toolTip.show(tip);
9231
9232             this.intervalID = window.setInterval(function() {
9233                 Roo.bootstrap.Form.popover.unmask();
9234             }, 10000);
9235
9236             window.onwheel = function(){ return false;};
9237             
9238             (function(){ this.isMasked = true; }).defer(500, this);
9239             
9240         },
9241         
9242         unmask : function()
9243         {
9244             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
9245                 return;
9246             }
9247             
9248             this.maskEl.top.setStyle('position', 'absolute');
9249             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
9250             this.maskEl.top.hide();
9251
9252             this.maskEl.left.setStyle('position', 'absolute');
9253             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
9254             this.maskEl.left.hide();
9255
9256             this.maskEl.bottom.setStyle('position', 'absolute');
9257             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
9258             this.maskEl.bottom.hide();
9259
9260             this.maskEl.right.setStyle('position', 'absolute');
9261             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
9262             this.maskEl.right.hide();
9263             
9264             this.toolTip.hide();
9265             
9266             this.toolTip.el.hide();
9267             
9268             window.onwheel = function(){ return true;};
9269             
9270             if(this.intervalID){
9271                 window.clearInterval(this.intervalID);
9272                 this.intervalID = false;
9273             }
9274             
9275             this.isMasked = false;
9276             
9277         }
9278         
9279     }
9280     
9281 });
9282
9283 /*
9284  * Based on:
9285  * Ext JS Library 1.1.1
9286  * Copyright(c) 2006-2007, Ext JS, LLC.
9287  *
9288  * Originally Released Under LGPL - original licence link has changed is not relivant.
9289  *
9290  * Fork - LGPL
9291  * <script type="text/javascript">
9292  */
9293 /**
9294  * @class Roo.form.VTypes
9295  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
9296  * @singleton
9297  */
9298 Roo.form.VTypes = function(){
9299     // closure these in so they are only created once.
9300     var alpha = /^[a-zA-Z_]+$/;
9301     var alphanum = /^[a-zA-Z0-9_]+$/;
9302     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
9303     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
9304
9305     // All these messages and functions are configurable
9306     return {
9307         /**
9308          * The function used to validate email addresses
9309          * @param {String} value The email address
9310          */
9311         'email' : function(v){
9312             return email.test(v);
9313         },
9314         /**
9315          * The error text to display when the email validation function returns false
9316          * @type String
9317          */
9318         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
9319         /**
9320          * The keystroke filter mask to be applied on email input
9321          * @type RegExp
9322          */
9323         'emailMask' : /[a-z0-9_\.\-@]/i,
9324
9325         /**
9326          * The function used to validate URLs
9327          * @param {String} value The URL
9328          */
9329         'url' : function(v){
9330             return url.test(v);
9331         },
9332         /**
9333          * The error text to display when the url validation function returns false
9334          * @type String
9335          */
9336         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
9337         
9338         /**
9339          * The function used to validate alpha values
9340          * @param {String} value The value
9341          */
9342         'alpha' : function(v){
9343             return alpha.test(v);
9344         },
9345         /**
9346          * The error text to display when the alpha validation function returns false
9347          * @type String
9348          */
9349         'alphaText' : 'This field should only contain letters and _',
9350         /**
9351          * The keystroke filter mask to be applied on alpha input
9352          * @type RegExp
9353          */
9354         'alphaMask' : /[a-z_]/i,
9355
9356         /**
9357          * The function used to validate alphanumeric values
9358          * @param {String} value The value
9359          */
9360         'alphanum' : function(v){
9361             return alphanum.test(v);
9362         },
9363         /**
9364          * The error text to display when the alphanumeric validation function returns false
9365          * @type String
9366          */
9367         'alphanumText' : 'This field should only contain letters, numbers and _',
9368         /**
9369          * The keystroke filter mask to be applied on alphanumeric input
9370          * @type RegExp
9371          */
9372         'alphanumMask' : /[a-z0-9_]/i
9373     };
9374 }();/*
9375  * - LGPL
9376  *
9377  * Input
9378  * 
9379  */
9380
9381 /**
9382  * @class Roo.bootstrap.Input
9383  * @extends Roo.bootstrap.Component
9384  * Bootstrap Input class
9385  * @cfg {Boolean} disabled is it disabled
9386  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
9387  * @cfg {String} name name of the input
9388  * @cfg {string} fieldLabel - the label associated
9389  * @cfg {string} placeholder - placeholder to put in text.
9390  * @cfg {string}  before - input group add on before
9391  * @cfg {string} after - input group add on after
9392  * @cfg {string} size - (lg|sm) or leave empty..
9393  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
9394  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
9395  * @cfg {Number} md colspan out of 12 for computer-sized screens
9396  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
9397  * @cfg {string} value default value of the input
9398  * @cfg {Number} labelWidth set the width of label 
9399  * @cfg {Number} labellg set the width of label (1-12)
9400  * @cfg {Number} labelmd set the width of label (1-12)
9401  * @cfg {Number} labelsm set the width of label (1-12)
9402  * @cfg {Number} labelxs set the width of label (1-12)
9403  * @cfg {String} labelAlign (top|left)
9404  * @cfg {Boolean} readOnly Specifies that the field should be read-only
9405  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9406  * @cfg {String} indicatorpos (left|right) default left
9407  * @cfg {String} capture (user|camera) use for file input only. (default empty)
9408  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9409
9410  * @cfg {String} align (left|center|right) Default left
9411  * @cfg {Boolean} forceFeedback (true|false) Default false
9412  * 
9413  * @constructor
9414  * Create a new Input
9415  * @param {Object} config The config object
9416  */
9417
9418 Roo.bootstrap.Input = function(config){
9419     
9420     Roo.bootstrap.Input.superclass.constructor.call(this, config);
9421     
9422     this.addEvents({
9423         /**
9424          * @event focus
9425          * Fires when this field receives input focus.
9426          * @param {Roo.form.Field} this
9427          */
9428         focus : true,
9429         /**
9430          * @event blur
9431          * Fires when this field loses input focus.
9432          * @param {Roo.form.Field} this
9433          */
9434         blur : true,
9435         /**
9436          * @event specialkey
9437          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9438          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9439          * @param {Roo.form.Field} this
9440          * @param {Roo.EventObject} e The event object
9441          */
9442         specialkey : true,
9443         /**
9444          * @event change
9445          * Fires just before the field blurs if the field value has changed.
9446          * @param {Roo.form.Field} this
9447          * @param {Mixed} newValue The new value
9448          * @param {Mixed} oldValue The original value
9449          */
9450         change : true,
9451         /**
9452          * @event invalid
9453          * Fires after the field has been marked as invalid.
9454          * @param {Roo.form.Field} this
9455          * @param {String} msg The validation message
9456          */
9457         invalid : true,
9458         /**
9459          * @event valid
9460          * Fires after the field has been validated with no errors.
9461          * @param {Roo.form.Field} this
9462          */
9463         valid : true,
9464          /**
9465          * @event keyup
9466          * Fires after the key up
9467          * @param {Roo.form.Field} this
9468          * @param {Roo.EventObject}  e The event Object
9469          */
9470         keyup : true
9471     });
9472 };
9473
9474 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9475      /**
9476      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9477       automatic validation (defaults to "keyup").
9478      */
9479     validationEvent : "keyup",
9480      /**
9481      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9482      */
9483     validateOnBlur : true,
9484     /**
9485      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9486      */
9487     validationDelay : 250,
9488      /**
9489      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9490      */
9491     focusClass : "x-form-focus",  // not needed???
9492     
9493        
9494     /**
9495      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9496      */
9497     invalidClass : "has-warning",
9498     
9499     /**
9500      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9501      */
9502     validClass : "has-success",
9503     
9504     /**
9505      * @cfg {Boolean} hasFeedback (true|false) default true
9506      */
9507     hasFeedback : true,
9508     
9509     /**
9510      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9511      */
9512     invalidFeedbackClass : "glyphicon-warning-sign",
9513     
9514     /**
9515      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9516      */
9517     validFeedbackClass : "glyphicon-ok",
9518     
9519     /**
9520      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9521      */
9522     selectOnFocus : false,
9523     
9524      /**
9525      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9526      */
9527     maskRe : null,
9528        /**
9529      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9530      */
9531     vtype : null,
9532     
9533       /**
9534      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9535      */
9536     disableKeyFilter : false,
9537     
9538        /**
9539      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9540      */
9541     disabled : false,
9542      /**
9543      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9544      */
9545     allowBlank : true,
9546     /**
9547      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9548      */
9549     blankText : "Please complete this mandatory field",
9550     
9551      /**
9552      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9553      */
9554     minLength : 0,
9555     /**
9556      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9557      */
9558     maxLength : Number.MAX_VALUE,
9559     /**
9560      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9561      */
9562     minLengthText : "The minimum length for this field is {0}",
9563     /**
9564      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9565      */
9566     maxLengthText : "The maximum length for this field is {0}",
9567   
9568     
9569     /**
9570      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9571      * If available, this function will be called only after the basic validators all return true, and will be passed the
9572      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9573      */
9574     validator : null,
9575     /**
9576      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9577      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9578      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9579      */
9580     regex : null,
9581     /**
9582      * @cfg {String} regexText -- Depricated - use Invalid Text
9583      */
9584     regexText : "",
9585     
9586     /**
9587      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9588      */
9589     invalidText : "",
9590     
9591     
9592     
9593     autocomplete: false,
9594     
9595     
9596     fieldLabel : '',
9597     inputType : 'text',
9598     
9599     name : false,
9600     placeholder: false,
9601     before : false,
9602     after : false,
9603     size : false,
9604     hasFocus : false,
9605     preventMark: false,
9606     isFormField : true,
9607     value : '',
9608     labelWidth : 2,
9609     labelAlign : false,
9610     readOnly : false,
9611     align : false,
9612     formatedValue : false,
9613     forceFeedback : false,
9614     
9615     indicatorpos : 'left',
9616     
9617     labellg : 0,
9618     labelmd : 0,
9619     labelsm : 0,
9620     labelxs : 0,
9621     
9622     capture : '',
9623     accept : '',
9624     
9625     parentLabelAlign : function()
9626     {
9627         var parent = this;
9628         while (parent.parent()) {
9629             parent = parent.parent();
9630             if (typeof(parent.labelAlign) !='undefined') {
9631                 return parent.labelAlign;
9632             }
9633         }
9634         return 'left';
9635         
9636     },
9637     
9638     getAutoCreate : function()
9639     {
9640         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9641         
9642         var id = Roo.id();
9643         
9644         var cfg = {};
9645         
9646         if(this.inputType != 'hidden'){
9647             cfg.cls = 'form-group' //input-group
9648         }
9649         
9650         var input =  {
9651             tag: 'input',
9652             id : id,
9653             type : this.inputType,
9654             value : this.value,
9655             cls : 'form-control',
9656             placeholder : this.placeholder || '',
9657             autocomplete : this.autocomplete || 'new-password'
9658         };
9659         
9660         if(this.capture.length){
9661             input.capture = this.capture;
9662         }
9663         
9664         if(this.accept.length){
9665             input.accept = this.accept + "/*";
9666         }
9667         
9668         if(this.align){
9669             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9670         }
9671         
9672         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9673             input.maxLength = this.maxLength;
9674         }
9675         
9676         if (this.disabled) {
9677             input.disabled=true;
9678         }
9679         
9680         if (this.readOnly) {
9681             input.readonly=true;
9682         }
9683         
9684         if (this.name) {
9685             input.name = this.name;
9686         }
9687         
9688         if (this.size) {
9689             input.cls += ' input-' + this.size;
9690         }
9691         
9692         var settings=this;
9693         ['xs','sm','md','lg'].map(function(size){
9694             if (settings[size]) {
9695                 cfg.cls += ' col-' + size + '-' + settings[size];
9696             }
9697         });
9698         
9699         var inputblock = input;
9700         
9701         var feedback = {
9702             tag: 'span',
9703             cls: 'glyphicon form-control-feedback'
9704         };
9705             
9706         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9707             
9708             inputblock = {
9709                 cls : 'has-feedback',
9710                 cn :  [
9711                     input,
9712                     feedback
9713                 ] 
9714             };  
9715         }
9716         
9717         if (this.before || this.after) {
9718             
9719             inputblock = {
9720                 cls : 'input-group',
9721                 cn :  [] 
9722             };
9723             
9724             if (this.before && typeof(this.before) == 'string') {
9725                 
9726                 inputblock.cn.push({
9727                     tag :'span',
9728                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9729                     html : this.before
9730                 });
9731             }
9732             if (this.before && typeof(this.before) == 'object') {
9733                 this.before = Roo.factory(this.before);
9734                 
9735                 inputblock.cn.push({
9736                     tag :'span',
9737                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9738                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9739                 });
9740             }
9741             
9742             inputblock.cn.push(input);
9743             
9744             if (this.after && typeof(this.after) == 'string') {
9745                 inputblock.cn.push({
9746                     tag :'span',
9747                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9748                     html : this.after
9749                 });
9750             }
9751             if (this.after && typeof(this.after) == 'object') {
9752                 this.after = Roo.factory(this.after);
9753                 
9754                 inputblock.cn.push({
9755                     tag :'span',
9756                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9757                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9758                 });
9759             }
9760             
9761             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9762                 inputblock.cls += ' has-feedback';
9763                 inputblock.cn.push(feedback);
9764             }
9765         };
9766         var indicator = {
9767             tag : 'i',
9768             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9769             tooltip : 'This field is required'
9770         };
9771         if (Roo.bootstrap.version == 4) {
9772             indicator = {
9773                 tag : 'i',
9774                 style : 'display-none'
9775             };
9776         }
9777         if (align ==='left' && this.fieldLabel.length) {
9778             
9779             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9780             
9781             cfg.cn = [
9782                 indicator,
9783                 {
9784                     tag: 'label',
9785                     'for' :  id,
9786                     cls : 'control-label col-form-label',
9787                     html : this.fieldLabel
9788
9789                 },
9790                 {
9791                     cls : "", 
9792                     cn: [
9793                         inputblock
9794                     ]
9795                 }
9796             ];
9797             
9798             var labelCfg = cfg.cn[1];
9799             var contentCfg = cfg.cn[2];
9800             
9801             if(this.indicatorpos == 'right'){
9802                 cfg.cn = [
9803                     {
9804                         tag: 'label',
9805                         'for' :  id,
9806                         cls : 'control-label col-form-label',
9807                         cn : [
9808                             {
9809                                 tag : 'span',
9810                                 html : this.fieldLabel
9811                             },
9812                             indicator
9813                         ]
9814                     },
9815                     {
9816                         cls : "",
9817                         cn: [
9818                             inputblock
9819                         ]
9820                     }
9821
9822                 ];
9823                 
9824                 labelCfg = cfg.cn[0];
9825                 contentCfg = cfg.cn[1];
9826             
9827             }
9828             
9829             if(this.labelWidth > 12){
9830                 labelCfg.style = "width: " + this.labelWidth + 'px';
9831             }
9832             
9833             if(this.labelWidth < 13 && this.labelmd == 0){
9834                 this.labelmd = this.labelWidth;
9835             }
9836             
9837             if(this.labellg > 0){
9838                 labelCfg.cls += ' col-lg-' + this.labellg;
9839                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9840             }
9841             
9842             if(this.labelmd > 0){
9843                 labelCfg.cls += ' col-md-' + this.labelmd;
9844                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9845             }
9846             
9847             if(this.labelsm > 0){
9848                 labelCfg.cls += ' col-sm-' + this.labelsm;
9849                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9850             }
9851             
9852             if(this.labelxs > 0){
9853                 labelCfg.cls += ' col-xs-' + this.labelxs;
9854                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9855             }
9856             
9857             
9858         } else if ( this.fieldLabel.length) {
9859                 
9860             cfg.cn = [
9861                 {
9862                     tag : 'i',
9863                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9864                     tooltip : 'This field is required'
9865                 },
9866                 {
9867                     tag: 'label',
9868                    //cls : 'input-group-addon',
9869                     html : this.fieldLabel
9870
9871                 },
9872
9873                inputblock
9874
9875            ];
9876            
9877            if(this.indicatorpos == 'right'){
9878                 
9879                 cfg.cn = [
9880                     {
9881                         tag: 'label',
9882                        //cls : 'input-group-addon',
9883                         html : this.fieldLabel
9884
9885                     },
9886                     {
9887                         tag : 'i',
9888                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9889                         tooltip : 'This field is required'
9890                     },
9891
9892                    inputblock
9893
9894                ];
9895
9896             }
9897
9898         } else {
9899             
9900             cfg.cn = [
9901
9902                     inputblock
9903
9904             ];
9905                 
9906                 
9907         };
9908         
9909         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9910            cfg.cls += ' navbar-form';
9911         }
9912         
9913         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9914             // on BS4 we do this only if not form 
9915             cfg.cls += ' navbar-form';
9916             cfg.tag = 'li';
9917         }
9918         
9919         return cfg;
9920         
9921     },
9922     /**
9923      * return the real input element.
9924      */
9925     inputEl: function ()
9926     {
9927         return this.el.select('input.form-control',true).first();
9928     },
9929     
9930     tooltipEl : function()
9931     {
9932         return this.inputEl();
9933     },
9934     
9935     indicatorEl : function()
9936     {
9937         if (Roo.bootstrap.version == 4) {
9938             return false; // not enabled in v4 yet.
9939         }
9940         
9941         var indicator = this.el.select('i.roo-required-indicator',true).first();
9942         
9943         if(!indicator){
9944             return false;
9945         }
9946         
9947         return indicator;
9948         
9949     },
9950     
9951     setDisabled : function(v)
9952     {
9953         var i  = this.inputEl().dom;
9954         if (!v) {
9955             i.removeAttribute('disabled');
9956             return;
9957             
9958         }
9959         i.setAttribute('disabled','true');
9960     },
9961     initEvents : function()
9962     {
9963           
9964         this.inputEl().on("keydown" , this.fireKey,  this);
9965         this.inputEl().on("focus", this.onFocus,  this);
9966         this.inputEl().on("blur", this.onBlur,  this);
9967         
9968         this.inputEl().relayEvent('keyup', this);
9969         
9970         this.indicator = this.indicatorEl();
9971         
9972         if(this.indicator){
9973             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9974         }
9975  
9976         // reference to original value for reset
9977         this.originalValue = this.getValue();
9978         //Roo.form.TextField.superclass.initEvents.call(this);
9979         if(this.validationEvent == 'keyup'){
9980             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9981             this.inputEl().on('keyup', this.filterValidation, this);
9982         }
9983         else if(this.validationEvent !== false){
9984             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9985         }
9986         
9987         if(this.selectOnFocus){
9988             this.on("focus", this.preFocus, this);
9989             
9990         }
9991         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9992             this.inputEl().on("keypress", this.filterKeys, this);
9993         } else {
9994             this.inputEl().relayEvent('keypress', this);
9995         }
9996        /* if(this.grow){
9997             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9998             this.el.on("click", this.autoSize,  this);
9999         }
10000         */
10001         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10002             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10003         }
10004         
10005         if (typeof(this.before) == 'object') {
10006             this.before.render(this.el.select('.roo-input-before',true).first());
10007         }
10008         if (typeof(this.after) == 'object') {
10009             this.after.render(this.el.select('.roo-input-after',true).first());
10010         }
10011         
10012         this.inputEl().on('change', this.onChange, this);
10013         
10014     },
10015     filterValidation : function(e){
10016         if(!e.isNavKeyPress()){
10017             this.validationTask.delay(this.validationDelay);
10018         }
10019     },
10020      /**
10021      * Validates the field value
10022      * @return {Boolean} True if the value is valid, else false
10023      */
10024     validate : function(){
10025         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10026         if(this.disabled || this.validateValue(this.getRawValue())){
10027             this.markValid();
10028             return true;
10029         }
10030         
10031         this.markInvalid();
10032         return false;
10033     },
10034     
10035     
10036     /**
10037      * Validates a value according to the field's validation rules and marks the field as invalid
10038      * if the validation fails
10039      * @param {Mixed} value The value to validate
10040      * @return {Boolean} True if the value is valid, else false
10041      */
10042     validateValue : function(value)
10043     {
10044         if(this.getVisibilityEl().hasClass('hidden')){
10045             return true;
10046         }
10047         
10048         if(value.length < 1)  { // if it's blank
10049             if(this.allowBlank){
10050                 return true;
10051             }
10052             return false;
10053         }
10054         
10055         if(value.length < this.minLength){
10056             return false;
10057         }
10058         if(value.length > this.maxLength){
10059             return false;
10060         }
10061         if(this.vtype){
10062             var vt = Roo.form.VTypes;
10063             if(!vt[this.vtype](value, this)){
10064                 return false;
10065             }
10066         }
10067         if(typeof this.validator == "function"){
10068             var msg = this.validator(value);
10069             if(msg !== true){
10070                 return false;
10071             }
10072             if (typeof(msg) == 'string') {
10073                 this.invalidText = msg;
10074             }
10075         }
10076         
10077         if(this.regex && !this.regex.test(value)){
10078             return false;
10079         }
10080         
10081         return true;
10082     },
10083     
10084      // private
10085     fireKey : function(e){
10086         //Roo.log('field ' + e.getKey());
10087         if(e.isNavKeyPress()){
10088             this.fireEvent("specialkey", this, e);
10089         }
10090     },
10091     focus : function (selectText){
10092         if(this.rendered){
10093             this.inputEl().focus();
10094             if(selectText === true){
10095                 this.inputEl().dom.select();
10096             }
10097         }
10098         return this;
10099     } ,
10100     
10101     onFocus : function(){
10102         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10103            // this.el.addClass(this.focusClass);
10104         }
10105         if(!this.hasFocus){
10106             this.hasFocus = true;
10107             this.startValue = this.getValue();
10108             this.fireEvent("focus", this);
10109         }
10110     },
10111     
10112     beforeBlur : Roo.emptyFn,
10113
10114     
10115     // private
10116     onBlur : function(){
10117         this.beforeBlur();
10118         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10119             //this.el.removeClass(this.focusClass);
10120         }
10121         this.hasFocus = false;
10122         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10123             this.validate();
10124         }
10125         var v = this.getValue();
10126         if(String(v) !== String(this.startValue)){
10127             this.fireEvent('change', this, v, this.startValue);
10128         }
10129         this.fireEvent("blur", this);
10130     },
10131     
10132     onChange : function(e)
10133     {
10134         var v = this.getValue();
10135         if(String(v) !== String(this.startValue)){
10136             this.fireEvent('change', this, v, this.startValue);
10137         }
10138         
10139     },
10140     
10141     /**
10142      * Resets the current field value to the originally loaded value and clears any validation messages
10143      */
10144     reset : function(){
10145         this.setValue(this.originalValue);
10146         this.validate();
10147     },
10148      /**
10149      * Returns the name of the field
10150      * @return {Mixed} name The name field
10151      */
10152     getName: function(){
10153         return this.name;
10154     },
10155      /**
10156      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
10157      * @return {Mixed} value The field value
10158      */
10159     getValue : function(){
10160         
10161         var v = this.inputEl().getValue();
10162         
10163         return v;
10164     },
10165     /**
10166      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
10167      * @return {Mixed} value The field value
10168      */
10169     getRawValue : function(){
10170         var v = this.inputEl().getValue();
10171         
10172         return v;
10173     },
10174     
10175     /**
10176      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
10177      * @param {Mixed} value The value to set
10178      */
10179     setRawValue : function(v){
10180         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10181     },
10182     
10183     selectText : function(start, end){
10184         var v = this.getRawValue();
10185         if(v.length > 0){
10186             start = start === undefined ? 0 : start;
10187             end = end === undefined ? v.length : end;
10188             var d = this.inputEl().dom;
10189             if(d.setSelectionRange){
10190                 d.setSelectionRange(start, end);
10191             }else if(d.createTextRange){
10192                 var range = d.createTextRange();
10193                 range.moveStart("character", start);
10194                 range.moveEnd("character", v.length-end);
10195                 range.select();
10196             }
10197         }
10198     },
10199     
10200     /**
10201      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
10202      * @param {Mixed} value The value to set
10203      */
10204     setValue : function(v){
10205         this.value = v;
10206         if(this.rendered){
10207             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10208             this.validate();
10209         }
10210     },
10211     
10212     /*
10213     processValue : function(value){
10214         if(this.stripCharsRe){
10215             var newValue = value.replace(this.stripCharsRe, '');
10216             if(newValue !== value){
10217                 this.setRawValue(newValue);
10218                 return newValue;
10219             }
10220         }
10221         return value;
10222     },
10223   */
10224     preFocus : function(){
10225         
10226         if(this.selectOnFocus){
10227             this.inputEl().dom.select();
10228         }
10229     },
10230     filterKeys : function(e){
10231         var k = e.getKey();
10232         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
10233             return;
10234         }
10235         var c = e.getCharCode(), cc = String.fromCharCode(c);
10236         if(Roo.isIE && (e.isSpecialKey() || !cc)){
10237             return;
10238         }
10239         if(!this.maskRe.test(cc)){
10240             e.stopEvent();
10241         }
10242     },
10243      /**
10244      * Clear any invalid styles/messages for this field
10245      */
10246     clearInvalid : function(){
10247         
10248         if(!this.el || this.preventMark){ // not rendered
10249             return;
10250         }
10251         
10252         
10253         this.el.removeClass([this.invalidClass, 'is-invalid']);
10254         
10255         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10256             
10257             var feedback = this.el.select('.form-control-feedback', true).first();
10258             
10259             if(feedback){
10260                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10261             }
10262             
10263         }
10264         
10265         if(this.indicator){
10266             this.indicator.removeClass('visible');
10267             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10268         }
10269         
10270         this.fireEvent('valid', this);
10271     },
10272     
10273      /**
10274      * Mark this field as valid
10275      */
10276     markValid : function()
10277     {
10278         if(!this.el  || this.preventMark){ // not rendered...
10279             return;
10280         }
10281         
10282         this.el.removeClass([this.invalidClass, this.validClass]);
10283         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10284
10285         var feedback = this.el.select('.form-control-feedback', true).first();
10286             
10287         if(feedback){
10288             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10289         }
10290         
10291         if(this.indicator){
10292             this.indicator.removeClass('visible');
10293             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10294         }
10295         
10296         if(this.disabled){
10297             return;
10298         }
10299         
10300         if(this.allowBlank && !this.getRawValue().length){
10301             return;
10302         }
10303         if (Roo.bootstrap.version == 3) {
10304             this.el.addClass(this.validClass);
10305         } else {
10306             this.inputEl().addClass('is-valid');
10307         }
10308
10309         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10310             
10311             var feedback = this.el.select('.form-control-feedback', true).first();
10312             
10313             if(feedback){
10314                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10315                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10316             }
10317             
10318         }
10319         
10320         this.fireEvent('valid', this);
10321     },
10322     
10323      /**
10324      * Mark this field as invalid
10325      * @param {String} msg The validation message
10326      */
10327     markInvalid : function(msg)
10328     {
10329         if(!this.el  || this.preventMark){ // not rendered
10330             return;
10331         }
10332         
10333         this.el.removeClass([this.invalidClass, this.validClass]);
10334         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10335         
10336         var feedback = this.el.select('.form-control-feedback', true).first();
10337             
10338         if(feedback){
10339             this.el.select('.form-control-feedback', true).first().removeClass(
10340                     [this.invalidFeedbackClass, this.validFeedbackClass]);
10341         }
10342
10343         if(this.disabled){
10344             return;
10345         }
10346         
10347         if(this.allowBlank && !this.getRawValue().length){
10348             return;
10349         }
10350         
10351         if(this.indicator){
10352             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10353             this.indicator.addClass('visible');
10354         }
10355         if (Roo.bootstrap.version == 3) {
10356             this.el.addClass(this.invalidClass);
10357         } else {
10358             this.inputEl().addClass('is-invalid');
10359         }
10360         
10361         
10362         
10363         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10364             
10365             var feedback = this.el.select('.form-control-feedback', true).first();
10366             
10367             if(feedback){
10368                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10369                 
10370                 if(this.getValue().length || this.forceFeedback){
10371                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10372                 }
10373                 
10374             }
10375             
10376         }
10377         
10378         this.fireEvent('invalid', this, msg);
10379     },
10380     // private
10381     SafariOnKeyDown : function(event)
10382     {
10383         // this is a workaround for a password hang bug on chrome/ webkit.
10384         if (this.inputEl().dom.type != 'password') {
10385             return;
10386         }
10387         
10388         var isSelectAll = false;
10389         
10390         if(this.inputEl().dom.selectionEnd > 0){
10391             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
10392         }
10393         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
10394             event.preventDefault();
10395             this.setValue('');
10396             return;
10397         }
10398         
10399         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10400             
10401             event.preventDefault();
10402             // this is very hacky as keydown always get's upper case.
10403             //
10404             var cc = String.fromCharCode(event.getCharCode());
10405             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
10406             
10407         }
10408     },
10409     adjustWidth : function(tag, w){
10410         tag = tag.toLowerCase();
10411         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10412             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10413                 if(tag == 'input'){
10414                     return w + 2;
10415                 }
10416                 if(tag == 'textarea'){
10417                     return w-2;
10418                 }
10419             }else if(Roo.isOpera){
10420                 if(tag == 'input'){
10421                     return w + 2;
10422                 }
10423                 if(tag == 'textarea'){
10424                     return w-2;
10425                 }
10426             }
10427         }
10428         return w;
10429     },
10430     
10431     setFieldLabel : function(v)
10432     {
10433         if(!this.rendered){
10434             return;
10435         }
10436         
10437         if(this.indicatorEl()){
10438             var ar = this.el.select('label > span',true);
10439             
10440             if (ar.elements.length) {
10441                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10442                 this.fieldLabel = v;
10443                 return;
10444             }
10445             
10446             var br = this.el.select('label',true);
10447             
10448             if(br.elements.length) {
10449                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10450                 this.fieldLabel = v;
10451                 return;
10452             }
10453             
10454             Roo.log('Cannot Found any of label > span || label in input');
10455             return;
10456         }
10457         
10458         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10459         this.fieldLabel = v;
10460         
10461         
10462     }
10463 });
10464
10465  
10466 /*
10467  * - LGPL
10468  *
10469  * Input
10470  * 
10471  */
10472
10473 /**
10474  * @class Roo.bootstrap.TextArea
10475  * @extends Roo.bootstrap.Input
10476  * Bootstrap TextArea class
10477  * @cfg {Number} cols Specifies the visible width of a text area
10478  * @cfg {Number} rows Specifies the visible number of lines in a text area
10479  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10480  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10481  * @cfg {string} html text
10482  * 
10483  * @constructor
10484  * Create a new TextArea
10485  * @param {Object} config The config object
10486  */
10487
10488 Roo.bootstrap.TextArea = function(config){
10489     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10490    
10491 };
10492
10493 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10494      
10495     cols : false,
10496     rows : 5,
10497     readOnly : false,
10498     warp : 'soft',
10499     resize : false,
10500     value: false,
10501     html: false,
10502     
10503     getAutoCreate : function(){
10504         
10505         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10506         
10507         var id = Roo.id();
10508         
10509         var cfg = {};
10510         
10511         if(this.inputType != 'hidden'){
10512             cfg.cls = 'form-group' //input-group
10513         }
10514         
10515         var input =  {
10516             tag: 'textarea',
10517             id : id,
10518             warp : this.warp,
10519             rows : this.rows,
10520             value : this.value || '',
10521             html: this.html || '',
10522             cls : 'form-control',
10523             placeholder : this.placeholder || '' 
10524             
10525         };
10526         
10527         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10528             input.maxLength = this.maxLength;
10529         }
10530         
10531         if(this.resize){
10532             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10533         }
10534         
10535         if(this.cols){
10536             input.cols = this.cols;
10537         }
10538         
10539         if (this.readOnly) {
10540             input.readonly = true;
10541         }
10542         
10543         if (this.name) {
10544             input.name = this.name;
10545         }
10546         
10547         if (this.size) {
10548             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10549         }
10550         
10551         var settings=this;
10552         ['xs','sm','md','lg'].map(function(size){
10553             if (settings[size]) {
10554                 cfg.cls += ' col-' + size + '-' + settings[size];
10555             }
10556         });
10557         
10558         var inputblock = input;
10559         
10560         if(this.hasFeedback && !this.allowBlank){
10561             
10562             var feedback = {
10563                 tag: 'span',
10564                 cls: 'glyphicon form-control-feedback'
10565             };
10566
10567             inputblock = {
10568                 cls : 'has-feedback',
10569                 cn :  [
10570                     input,
10571                     feedback
10572                 ] 
10573             };  
10574         }
10575         
10576         
10577         if (this.before || this.after) {
10578             
10579             inputblock = {
10580                 cls : 'input-group',
10581                 cn :  [] 
10582             };
10583             if (this.before) {
10584                 inputblock.cn.push({
10585                     tag :'span',
10586                     cls : 'input-group-addon',
10587                     html : this.before
10588                 });
10589             }
10590             
10591             inputblock.cn.push(input);
10592             
10593             if(this.hasFeedback && !this.allowBlank){
10594                 inputblock.cls += ' has-feedback';
10595                 inputblock.cn.push(feedback);
10596             }
10597             
10598             if (this.after) {
10599                 inputblock.cn.push({
10600                     tag :'span',
10601                     cls : 'input-group-addon',
10602                     html : this.after
10603                 });
10604             }
10605             
10606         }
10607         
10608         if (align ==='left' && this.fieldLabel.length) {
10609             cfg.cn = [
10610                 {
10611                     tag: 'label',
10612                     'for' :  id,
10613                     cls : 'control-label',
10614                     html : this.fieldLabel
10615                 },
10616                 {
10617                     cls : "",
10618                     cn: [
10619                         inputblock
10620                     ]
10621                 }
10622
10623             ];
10624             
10625             if(this.labelWidth > 12){
10626                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10627             }
10628
10629             if(this.labelWidth < 13 && this.labelmd == 0){
10630                 this.labelmd = this.labelWidth;
10631             }
10632
10633             if(this.labellg > 0){
10634                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10635                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10636             }
10637
10638             if(this.labelmd > 0){
10639                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10640                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10641             }
10642
10643             if(this.labelsm > 0){
10644                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10645                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10646             }
10647
10648             if(this.labelxs > 0){
10649                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10650                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10651             }
10652             
10653         } else if ( this.fieldLabel.length) {
10654             cfg.cn = [
10655
10656                {
10657                    tag: 'label',
10658                    //cls : 'input-group-addon',
10659                    html : this.fieldLabel
10660
10661                },
10662
10663                inputblock
10664
10665            ];
10666
10667         } else {
10668
10669             cfg.cn = [
10670
10671                 inputblock
10672
10673             ];
10674                 
10675         }
10676         
10677         if (this.disabled) {
10678             input.disabled=true;
10679         }
10680         
10681         return cfg;
10682         
10683     },
10684     /**
10685      * return the real textarea element.
10686      */
10687     inputEl: function ()
10688     {
10689         return this.el.select('textarea.form-control',true).first();
10690     },
10691     
10692     /**
10693      * Clear any invalid styles/messages for this field
10694      */
10695     clearInvalid : function()
10696     {
10697         
10698         if(!this.el || this.preventMark){ // not rendered
10699             return;
10700         }
10701         
10702         var label = this.el.select('label', true).first();
10703         var icon = this.el.select('i.fa-star', true).first();
10704         
10705         if(label && icon){
10706             icon.remove();
10707         }
10708         this.el.removeClass( this.validClass);
10709         this.inputEl().removeClass('is-invalid');
10710          
10711         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10712             
10713             var feedback = this.el.select('.form-control-feedback', true).first();
10714             
10715             if(feedback){
10716                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10717             }
10718             
10719         }
10720         
10721         this.fireEvent('valid', this);
10722     },
10723     
10724      /**
10725      * Mark this field as valid
10726      */
10727     markValid : function()
10728     {
10729         if(!this.el  || this.preventMark){ // not rendered
10730             return;
10731         }
10732         
10733         this.el.removeClass([this.invalidClass, this.validClass]);
10734         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10735         
10736         var feedback = this.el.select('.form-control-feedback', true).first();
10737             
10738         if(feedback){
10739             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10740         }
10741
10742         if(this.disabled || this.allowBlank){
10743             return;
10744         }
10745         
10746         var label = this.el.select('label', true).first();
10747         var icon = this.el.select('i.fa-star', true).first();
10748         
10749         if(label && icon){
10750             icon.remove();
10751         }
10752         if (Roo.bootstrap.version == 3) {
10753             this.el.addClass(this.validClass);
10754         } else {
10755             this.inputEl().addClass('is-valid');
10756         }
10757         
10758         
10759         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10760             
10761             var feedback = this.el.select('.form-control-feedback', true).first();
10762             
10763             if(feedback){
10764                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10765                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10766             }
10767             
10768         }
10769         
10770         this.fireEvent('valid', this);
10771     },
10772     
10773      /**
10774      * Mark this field as invalid
10775      * @param {String} msg The validation message
10776      */
10777     markInvalid : function(msg)
10778     {
10779         if(!this.el  || this.preventMark){ // not rendered
10780             return;
10781         }
10782         
10783         this.el.removeClass([this.invalidClass, this.validClass]);
10784         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10785         
10786         var feedback = this.el.select('.form-control-feedback', true).first();
10787             
10788         if(feedback){
10789             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10790         }
10791
10792         if(this.disabled || this.allowBlank){
10793             return;
10794         }
10795         
10796         var label = this.el.select('label', true).first();
10797         var icon = this.el.select('i.fa-star', true).first();
10798         
10799         if(!this.getValue().length && label && !icon){
10800             this.el.createChild({
10801                 tag : 'i',
10802                 cls : 'text-danger fa fa-lg fa-star',
10803                 tooltip : 'This field is required',
10804                 style : 'margin-right:5px;'
10805             }, label, true);
10806         }
10807         
10808         if (Roo.bootstrap.version == 3) {
10809             this.el.addClass(this.invalidClass);
10810         } else {
10811             this.inputEl().addClass('is-invalid');
10812         }
10813         
10814         // fixme ... this may be depricated need to test..
10815         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10816             
10817             var feedback = this.el.select('.form-control-feedback', true).first();
10818             
10819             if(feedback){
10820                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10821                 
10822                 if(this.getValue().length || this.forceFeedback){
10823                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10824                 }
10825                 
10826             }
10827             
10828         }
10829         
10830         this.fireEvent('invalid', this, msg);
10831     }
10832 });
10833
10834  
10835 /*
10836  * - LGPL
10837  *
10838  * trigger field - base class for combo..
10839  * 
10840  */
10841  
10842 /**
10843  * @class Roo.bootstrap.TriggerField
10844  * @extends Roo.bootstrap.Input
10845  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10846  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10847  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10848  * for which you can provide a custom implementation.  For example:
10849  * <pre><code>
10850 var trigger = new Roo.bootstrap.TriggerField();
10851 trigger.onTriggerClick = myTriggerFn;
10852 trigger.applyTo('my-field');
10853 </code></pre>
10854  *
10855  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10856  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10857  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10858  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10859  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
10860
10861  * @constructor
10862  * Create a new TriggerField.
10863  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10864  * to the base TextField)
10865  */
10866 Roo.bootstrap.TriggerField = function(config){
10867     this.mimicing = false;
10868     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10869 };
10870
10871 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10872     /**
10873      * @cfg {String} triggerClass A CSS class to apply to the trigger
10874      */
10875      /**
10876      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10877      */
10878     hideTrigger:false,
10879
10880     /**
10881      * @cfg {Boolean} removable (true|false) special filter default false
10882      */
10883     removable : false,
10884     
10885     /** @cfg {Boolean} grow @hide */
10886     /** @cfg {Number} growMin @hide */
10887     /** @cfg {Number} growMax @hide */
10888
10889     /**
10890      * @hide 
10891      * @method
10892      */
10893     autoSize: Roo.emptyFn,
10894     // private
10895     monitorTab : true,
10896     // private
10897     deferHeight : true,
10898
10899     
10900     actionMode : 'wrap',
10901     
10902     caret : false,
10903     
10904     
10905     getAutoCreate : function(){
10906        
10907         var align = this.labelAlign || this.parentLabelAlign();
10908         
10909         var id = Roo.id();
10910         
10911         var cfg = {
10912             cls: 'form-group' //input-group
10913         };
10914         
10915         
10916         var input =  {
10917             tag: 'input',
10918             id : id,
10919             type : this.inputType,
10920             cls : 'form-control',
10921             autocomplete: 'new-password',
10922             placeholder : this.placeholder || '' 
10923             
10924         };
10925         if (this.name) {
10926             input.name = this.name;
10927         }
10928         if (this.size) {
10929             input.cls += ' input-' + this.size;
10930         }
10931         
10932         if (this.disabled) {
10933             input.disabled=true;
10934         }
10935         
10936         var inputblock = input;
10937         
10938         if(this.hasFeedback && !this.allowBlank){
10939             
10940             var feedback = {
10941                 tag: 'span',
10942                 cls: 'glyphicon form-control-feedback'
10943             };
10944             
10945             if(this.removable && !this.editable && !this.tickable){
10946                 inputblock = {
10947                     cls : 'has-feedback',
10948                     cn :  [
10949                         inputblock,
10950                         {
10951                             tag: 'button',
10952                             html : 'x',
10953                             cls : 'roo-combo-removable-btn close'
10954                         },
10955                         feedback
10956                     ] 
10957                 };
10958             } else {
10959                 inputblock = {
10960                     cls : 'has-feedback',
10961                     cn :  [
10962                         inputblock,
10963                         feedback
10964                     ] 
10965                 };
10966             }
10967
10968         } else {
10969             if(this.removable && !this.editable && !this.tickable){
10970                 inputblock = {
10971                     cls : 'roo-removable',
10972                     cn :  [
10973                         inputblock,
10974                         {
10975                             tag: 'button',
10976                             html : 'x',
10977                             cls : 'roo-combo-removable-btn close'
10978                         }
10979                     ] 
10980                 };
10981             }
10982         }
10983         
10984         if (this.before || this.after) {
10985             
10986             inputblock = {
10987                 cls : 'input-group',
10988                 cn :  [] 
10989             };
10990             if (this.before) {
10991                 inputblock.cn.push({
10992                     tag :'span',
10993                     cls : 'input-group-addon input-group-prepend input-group-text',
10994                     html : this.before
10995                 });
10996             }
10997             
10998             inputblock.cn.push(input);
10999             
11000             if(this.hasFeedback && !this.allowBlank){
11001                 inputblock.cls += ' has-feedback';
11002                 inputblock.cn.push(feedback);
11003             }
11004             
11005             if (this.after) {
11006                 inputblock.cn.push({
11007                     tag :'span',
11008                     cls : 'input-group-addon input-group-append input-group-text',
11009                     html : this.after
11010                 });
11011             }
11012             
11013         };
11014         
11015       
11016         
11017         var ibwrap = inputblock;
11018         
11019         if(this.multiple){
11020             ibwrap = {
11021                 tag: 'ul',
11022                 cls: 'roo-select2-choices',
11023                 cn:[
11024                     {
11025                         tag: 'li',
11026                         cls: 'roo-select2-search-field',
11027                         cn: [
11028
11029                             inputblock
11030                         ]
11031                     }
11032                 ]
11033             };
11034                 
11035         }
11036         
11037         var combobox = {
11038             cls: 'roo-select2-container input-group',
11039             cn: [
11040                  {
11041                     tag: 'input',
11042                     type : 'hidden',
11043                     cls: 'form-hidden-field'
11044                 },
11045                 ibwrap
11046             ]
11047         };
11048         
11049         if(!this.multiple && this.showToggleBtn){
11050             
11051             var caret = {
11052                         tag: 'span',
11053                         cls: 'caret'
11054              };
11055             if (this.caret != false) {
11056                 caret = {
11057                      tag: 'i',
11058                      cls: 'fa fa-' + this.caret
11059                 };
11060                 
11061             }
11062             
11063             combobox.cn.push({
11064                 tag :'span',
11065                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11066                 cn : [
11067                     Roo.bootstrap.version == 3 ? caret : '',
11068                     {
11069                         tag: 'span',
11070                         cls: 'combobox-clear',
11071                         cn  : [
11072                             {
11073                                 tag : 'i',
11074                                 cls: 'icon-remove'
11075                             }
11076                         ]
11077                     }
11078                 ]
11079
11080             })
11081         }
11082         
11083         if(this.multiple){
11084             combobox.cls += ' roo-select2-container-multi';
11085         }
11086          var indicator = {
11087             tag : 'i',
11088             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11089             tooltip : 'This field is required'
11090         };
11091         if (Roo.bootstrap.version == 4) {
11092             indicator = {
11093                 tag : 'i',
11094                 style : 'display:none'
11095             };
11096         }
11097         
11098         
11099         if (align ==='left' && this.fieldLabel.length) {
11100             
11101             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11102
11103             cfg.cn = [
11104                 indicator,
11105                 {
11106                     tag: 'label',
11107                     'for' :  id,
11108                     cls : 'control-label',
11109                     html : this.fieldLabel
11110
11111                 },
11112                 {
11113                     cls : "", 
11114                     cn: [
11115                         combobox
11116                     ]
11117                 }
11118
11119             ];
11120             
11121             var labelCfg = cfg.cn[1];
11122             var contentCfg = cfg.cn[2];
11123             
11124             if(this.indicatorpos == 'right'){
11125                 cfg.cn = [
11126                     {
11127                         tag: 'label',
11128                         'for' :  id,
11129                         cls : 'control-label',
11130                         cn : [
11131                             {
11132                                 tag : 'span',
11133                                 html : this.fieldLabel
11134                             },
11135                             indicator
11136                         ]
11137                     },
11138                     {
11139                         cls : "", 
11140                         cn: [
11141                             combobox
11142                         ]
11143                     }
11144
11145                 ];
11146                 
11147                 labelCfg = cfg.cn[0];
11148                 contentCfg = cfg.cn[1];
11149             }
11150             
11151             if(this.labelWidth > 12){
11152                 labelCfg.style = "width: " + this.labelWidth + 'px';
11153             }
11154             
11155             if(this.labelWidth < 13 && this.labelmd == 0){
11156                 this.labelmd = this.labelWidth;
11157             }
11158             
11159             if(this.labellg > 0){
11160                 labelCfg.cls += ' col-lg-' + this.labellg;
11161                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11162             }
11163             
11164             if(this.labelmd > 0){
11165                 labelCfg.cls += ' col-md-' + this.labelmd;
11166                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11167             }
11168             
11169             if(this.labelsm > 0){
11170                 labelCfg.cls += ' col-sm-' + this.labelsm;
11171                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11172             }
11173             
11174             if(this.labelxs > 0){
11175                 labelCfg.cls += ' col-xs-' + this.labelxs;
11176                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11177             }
11178             
11179         } else if ( this.fieldLabel.length) {
11180 //                Roo.log(" label");
11181             cfg.cn = [
11182                 indicator,
11183                {
11184                    tag: 'label',
11185                    //cls : 'input-group-addon',
11186                    html : this.fieldLabel
11187
11188                },
11189
11190                combobox
11191
11192             ];
11193             
11194             if(this.indicatorpos == 'right'){
11195                 
11196                 cfg.cn = [
11197                     {
11198                        tag: 'label',
11199                        cn : [
11200                            {
11201                                tag : 'span',
11202                                html : this.fieldLabel
11203                            },
11204                            indicator
11205                        ]
11206
11207                     },
11208                     combobox
11209
11210                 ];
11211
11212             }
11213
11214         } else {
11215             
11216 //                Roo.log(" no label && no align");
11217                 cfg = combobox
11218                      
11219                 
11220         }
11221         
11222         var settings=this;
11223         ['xs','sm','md','lg'].map(function(size){
11224             if (settings[size]) {
11225                 cfg.cls += ' col-' + size + '-' + settings[size];
11226             }
11227         });
11228         
11229         return cfg;
11230         
11231     },
11232     
11233     
11234     
11235     // private
11236     onResize : function(w, h){
11237 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
11238 //        if(typeof w == 'number'){
11239 //            var x = w - this.trigger.getWidth();
11240 //            this.inputEl().setWidth(this.adjustWidth('input', x));
11241 //            this.trigger.setStyle('left', x+'px');
11242 //        }
11243     },
11244
11245     // private
11246     adjustSize : Roo.BoxComponent.prototype.adjustSize,
11247
11248     // private
11249     getResizeEl : function(){
11250         return this.inputEl();
11251     },
11252
11253     // private
11254     getPositionEl : function(){
11255         return this.inputEl();
11256     },
11257
11258     // private
11259     alignErrorIcon : function(){
11260         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
11261     },
11262
11263     // private
11264     initEvents : function(){
11265         
11266         this.createList();
11267         
11268         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
11269         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
11270         if(!this.multiple && this.showToggleBtn){
11271             this.trigger = this.el.select('span.dropdown-toggle',true).first();
11272             if(this.hideTrigger){
11273                 this.trigger.setDisplayed(false);
11274             }
11275             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
11276         }
11277         
11278         if(this.multiple){
11279             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
11280         }
11281         
11282         if(this.removable && !this.editable && !this.tickable){
11283             var close = this.closeTriggerEl();
11284             
11285             if(close){
11286                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
11287                 close.on('click', this.removeBtnClick, this, close);
11288             }
11289         }
11290         
11291         //this.trigger.addClassOnOver('x-form-trigger-over');
11292         //this.trigger.addClassOnClick('x-form-trigger-click');
11293         
11294         //if(!this.width){
11295         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
11296         //}
11297     },
11298     
11299     closeTriggerEl : function()
11300     {
11301         var close = this.el.select('.roo-combo-removable-btn', true).first();
11302         return close ? close : false;
11303     },
11304     
11305     removeBtnClick : function(e, h, el)
11306     {
11307         e.preventDefault();
11308         
11309         if(this.fireEvent("remove", this) !== false){
11310             this.reset();
11311             this.fireEvent("afterremove", this)
11312         }
11313     },
11314     
11315     createList : function()
11316     {
11317         this.list = Roo.get(document.body).createChild({
11318             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
11319             cls: 'typeahead typeahead-long dropdown-menu',
11320             style: 'display:none'
11321         });
11322         
11323         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
11324         
11325     },
11326
11327     // private
11328     initTrigger : function(){
11329        
11330     },
11331
11332     // private
11333     onDestroy : function(){
11334         if(this.trigger){
11335             this.trigger.removeAllListeners();
11336           //  this.trigger.remove();
11337         }
11338         //if(this.wrap){
11339         //    this.wrap.remove();
11340         //}
11341         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
11342     },
11343
11344     // private
11345     onFocus : function(){
11346         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
11347         /*
11348         if(!this.mimicing){
11349             this.wrap.addClass('x-trigger-wrap-focus');
11350             this.mimicing = true;
11351             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
11352             if(this.monitorTab){
11353                 this.el.on("keydown", this.checkTab, this);
11354             }
11355         }
11356         */
11357     },
11358
11359     // private
11360     checkTab : function(e){
11361         if(e.getKey() == e.TAB){
11362             this.triggerBlur();
11363         }
11364     },
11365
11366     // private
11367     onBlur : function(){
11368         // do nothing
11369     },
11370
11371     // private
11372     mimicBlur : function(e, t){
11373         /*
11374         if(!this.wrap.contains(t) && this.validateBlur()){
11375             this.triggerBlur();
11376         }
11377         */
11378     },
11379
11380     // private
11381     triggerBlur : function(){
11382         this.mimicing = false;
11383         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
11384         if(this.monitorTab){
11385             this.el.un("keydown", this.checkTab, this);
11386         }
11387         //this.wrap.removeClass('x-trigger-wrap-focus');
11388         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
11389     },
11390
11391     // private
11392     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
11393     validateBlur : function(e, t){
11394         return true;
11395     },
11396
11397     // private
11398     onDisable : function(){
11399         this.inputEl().dom.disabled = true;
11400         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11401         //if(this.wrap){
11402         //    this.wrap.addClass('x-item-disabled');
11403         //}
11404     },
11405
11406     // private
11407     onEnable : function(){
11408         this.inputEl().dom.disabled = false;
11409         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11410         //if(this.wrap){
11411         //    this.el.removeClass('x-item-disabled');
11412         //}
11413     },
11414
11415     // private
11416     onShow : function(){
11417         var ae = this.getActionEl();
11418         
11419         if(ae){
11420             ae.dom.style.display = '';
11421             ae.dom.style.visibility = 'visible';
11422         }
11423     },
11424
11425     // private
11426     
11427     onHide : function(){
11428         var ae = this.getActionEl();
11429         ae.dom.style.display = 'none';
11430     },
11431
11432     /**
11433      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
11434      * by an implementing function.
11435      * @method
11436      * @param {EventObject} e
11437      */
11438     onTriggerClick : Roo.emptyFn
11439 });
11440  /*
11441  * Based on:
11442  * Ext JS Library 1.1.1
11443  * Copyright(c) 2006-2007, Ext JS, LLC.
11444  *
11445  * Originally Released Under LGPL - original licence link has changed is not relivant.
11446  *
11447  * Fork - LGPL
11448  * <script type="text/javascript">
11449  */
11450
11451
11452 /**
11453  * @class Roo.data.SortTypes
11454  * @singleton
11455  * Defines the default sorting (casting?) comparison functions used when sorting data.
11456  */
11457 Roo.data.SortTypes = {
11458     /**
11459      * Default sort that does nothing
11460      * @param {Mixed} s The value being converted
11461      * @return {Mixed} The comparison value
11462      */
11463     none : function(s){
11464         return s;
11465     },
11466     
11467     /**
11468      * The regular expression used to strip tags
11469      * @type {RegExp}
11470      * @property
11471      */
11472     stripTagsRE : /<\/?[^>]+>/gi,
11473     
11474     /**
11475      * Strips all HTML tags to sort on text only
11476      * @param {Mixed} s The value being converted
11477      * @return {String} The comparison value
11478      */
11479     asText : function(s){
11480         return String(s).replace(this.stripTagsRE, "");
11481     },
11482     
11483     /**
11484      * Strips all HTML tags to sort on text only - Case insensitive
11485      * @param {Mixed} s The value being converted
11486      * @return {String} The comparison value
11487      */
11488     asUCText : function(s){
11489         return String(s).toUpperCase().replace(this.stripTagsRE, "");
11490     },
11491     
11492     /**
11493      * Case insensitive string
11494      * @param {Mixed} s The value being converted
11495      * @return {String} The comparison value
11496      */
11497     asUCString : function(s) {
11498         return String(s).toUpperCase();
11499     },
11500     
11501     /**
11502      * Date sorting
11503      * @param {Mixed} s The value being converted
11504      * @return {Number} The comparison value
11505      */
11506     asDate : function(s) {
11507         if(!s){
11508             return 0;
11509         }
11510         if(s instanceof Date){
11511             return s.getTime();
11512         }
11513         return Date.parse(String(s));
11514     },
11515     
11516     /**
11517      * Float sorting
11518      * @param {Mixed} s The value being converted
11519      * @return {Float} The comparison value
11520      */
11521     asFloat : function(s) {
11522         var val = parseFloat(String(s).replace(/,/g, ""));
11523         if(isNaN(val)) {
11524             val = 0;
11525         }
11526         return val;
11527     },
11528     
11529     /**
11530      * Integer sorting
11531      * @param {Mixed} s The value being converted
11532      * @return {Number} The comparison value
11533      */
11534     asInt : function(s) {
11535         var val = parseInt(String(s).replace(/,/g, ""));
11536         if(isNaN(val)) {
11537             val = 0;
11538         }
11539         return val;
11540     }
11541 };/*
11542  * Based on:
11543  * Ext JS Library 1.1.1
11544  * Copyright(c) 2006-2007, Ext JS, LLC.
11545  *
11546  * Originally Released Under LGPL - original licence link has changed is not relivant.
11547  *
11548  * Fork - LGPL
11549  * <script type="text/javascript">
11550  */
11551
11552 /**
11553 * @class Roo.data.Record
11554  * Instances of this class encapsulate both record <em>definition</em> information, and record
11555  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11556  * to access Records cached in an {@link Roo.data.Store} object.<br>
11557  * <p>
11558  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11559  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11560  * objects.<br>
11561  * <p>
11562  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11563  * @constructor
11564  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11565  * {@link #create}. The parameters are the same.
11566  * @param {Array} data An associative Array of data values keyed by the field name.
11567  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11568  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11569  * not specified an integer id is generated.
11570  */
11571 Roo.data.Record = function(data, id){
11572     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11573     this.data = data;
11574 };
11575
11576 /**
11577  * Generate a constructor for a specific record layout.
11578  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11579  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11580  * Each field definition object may contain the following properties: <ul>
11581  * <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,
11582  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11583  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11584  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11585  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11586  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11587  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11588  * this may be omitted.</p></li>
11589  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11590  * <ul><li>auto (Default, implies no conversion)</li>
11591  * <li>string</li>
11592  * <li>int</li>
11593  * <li>float</li>
11594  * <li>boolean</li>
11595  * <li>date</li></ul></p></li>
11596  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11597  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11598  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11599  * by the Reader into an object that will be stored in the Record. It is passed the
11600  * following parameters:<ul>
11601  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11602  * </ul></p></li>
11603  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11604  * </ul>
11605  * <br>usage:<br><pre><code>
11606 var TopicRecord = Roo.data.Record.create(
11607     {name: 'title', mapping: 'topic_title'},
11608     {name: 'author', mapping: 'username'},
11609     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11610     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11611     {name: 'lastPoster', mapping: 'user2'},
11612     {name: 'excerpt', mapping: 'post_text'}
11613 );
11614
11615 var myNewRecord = new TopicRecord({
11616     title: 'Do my job please',
11617     author: 'noobie',
11618     totalPosts: 1,
11619     lastPost: new Date(),
11620     lastPoster: 'Animal',
11621     excerpt: 'No way dude!'
11622 });
11623 myStore.add(myNewRecord);
11624 </code></pre>
11625  * @method create
11626  * @static
11627  */
11628 Roo.data.Record.create = function(o){
11629     var f = function(){
11630         f.superclass.constructor.apply(this, arguments);
11631     };
11632     Roo.extend(f, Roo.data.Record);
11633     var p = f.prototype;
11634     p.fields = new Roo.util.MixedCollection(false, function(field){
11635         return field.name;
11636     });
11637     for(var i = 0, len = o.length; i < len; i++){
11638         p.fields.add(new Roo.data.Field(o[i]));
11639     }
11640     f.getField = function(name){
11641         return p.fields.get(name);  
11642     };
11643     return f;
11644 };
11645
11646 Roo.data.Record.AUTO_ID = 1000;
11647 Roo.data.Record.EDIT = 'edit';
11648 Roo.data.Record.REJECT = 'reject';
11649 Roo.data.Record.COMMIT = 'commit';
11650
11651 Roo.data.Record.prototype = {
11652     /**
11653      * Readonly flag - true if this record has been modified.
11654      * @type Boolean
11655      */
11656     dirty : false,
11657     editing : false,
11658     error: null,
11659     modified: null,
11660
11661     // private
11662     join : function(store){
11663         this.store = store;
11664     },
11665
11666     /**
11667      * Set the named field to the specified value.
11668      * @param {String} name The name of the field to set.
11669      * @param {Object} value The value to set the field to.
11670      */
11671     set : function(name, value){
11672         if(this.data[name] == value){
11673             return;
11674         }
11675         this.dirty = true;
11676         if(!this.modified){
11677             this.modified = {};
11678         }
11679         if(typeof this.modified[name] == 'undefined'){
11680             this.modified[name] = this.data[name];
11681         }
11682         this.data[name] = value;
11683         if(!this.editing && this.store){
11684             this.store.afterEdit(this);
11685         }       
11686     },
11687
11688     /**
11689      * Get the value of the named field.
11690      * @param {String} name The name of the field to get the value of.
11691      * @return {Object} The value of the field.
11692      */
11693     get : function(name){
11694         return this.data[name]; 
11695     },
11696
11697     // private
11698     beginEdit : function(){
11699         this.editing = true;
11700         this.modified = {}; 
11701     },
11702
11703     // private
11704     cancelEdit : function(){
11705         this.editing = false;
11706         delete this.modified;
11707     },
11708
11709     // private
11710     endEdit : function(){
11711         this.editing = false;
11712         if(this.dirty && this.store){
11713             this.store.afterEdit(this);
11714         }
11715     },
11716
11717     /**
11718      * Usually called by the {@link Roo.data.Store} which owns the Record.
11719      * Rejects all changes made to the Record since either creation, or the last commit operation.
11720      * Modified fields are reverted to their original values.
11721      * <p>
11722      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11723      * of reject operations.
11724      */
11725     reject : function(){
11726         var m = this.modified;
11727         for(var n in m){
11728             if(typeof m[n] != "function"){
11729                 this.data[n] = m[n];
11730             }
11731         }
11732         this.dirty = false;
11733         delete this.modified;
11734         this.editing = false;
11735         if(this.store){
11736             this.store.afterReject(this);
11737         }
11738     },
11739
11740     /**
11741      * Usually called by the {@link Roo.data.Store} which owns the Record.
11742      * Commits all changes made to the Record since either creation, or the last commit operation.
11743      * <p>
11744      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11745      * of commit operations.
11746      */
11747     commit : function(){
11748         this.dirty = false;
11749         delete this.modified;
11750         this.editing = false;
11751         if(this.store){
11752             this.store.afterCommit(this);
11753         }
11754     },
11755
11756     // private
11757     hasError : function(){
11758         return this.error != null;
11759     },
11760
11761     // private
11762     clearError : function(){
11763         this.error = null;
11764     },
11765
11766     /**
11767      * Creates a copy of this record.
11768      * @param {String} id (optional) A new record id if you don't want to use this record's id
11769      * @return {Record}
11770      */
11771     copy : function(newId) {
11772         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11773     }
11774 };/*
11775  * Based on:
11776  * Ext JS Library 1.1.1
11777  * Copyright(c) 2006-2007, Ext JS, LLC.
11778  *
11779  * Originally Released Under LGPL - original licence link has changed is not relivant.
11780  *
11781  * Fork - LGPL
11782  * <script type="text/javascript">
11783  */
11784
11785
11786
11787 /**
11788  * @class Roo.data.Store
11789  * @extends Roo.util.Observable
11790  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11791  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11792  * <p>
11793  * 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
11794  * has no knowledge of the format of the data returned by the Proxy.<br>
11795  * <p>
11796  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11797  * instances from the data object. These records are cached and made available through accessor functions.
11798  * @constructor
11799  * Creates a new Store.
11800  * @param {Object} config A config object containing the objects needed for the Store to access data,
11801  * and read the data into Records.
11802  */
11803 Roo.data.Store = function(config){
11804     this.data = new Roo.util.MixedCollection(false);
11805     this.data.getKey = function(o){
11806         return o.id;
11807     };
11808     this.baseParams = {};
11809     // private
11810     this.paramNames = {
11811         "start" : "start",
11812         "limit" : "limit",
11813         "sort" : "sort",
11814         "dir" : "dir",
11815         "multisort" : "_multisort"
11816     };
11817
11818     if(config && config.data){
11819         this.inlineData = config.data;
11820         delete config.data;
11821     }
11822
11823     Roo.apply(this, config);
11824     
11825     if(this.reader){ // reader passed
11826         this.reader = Roo.factory(this.reader, Roo.data);
11827         this.reader.xmodule = this.xmodule || false;
11828         if(!this.recordType){
11829             this.recordType = this.reader.recordType;
11830         }
11831         if(this.reader.onMetaChange){
11832             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11833         }
11834     }
11835
11836     if(this.recordType){
11837         this.fields = this.recordType.prototype.fields;
11838     }
11839     this.modified = [];
11840
11841     this.addEvents({
11842         /**
11843          * @event datachanged
11844          * Fires when the data cache has changed, and a widget which is using this Store
11845          * as a Record cache should refresh its view.
11846          * @param {Store} this
11847          */
11848         datachanged : true,
11849         /**
11850          * @event metachange
11851          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11852          * @param {Store} this
11853          * @param {Object} meta The JSON metadata
11854          */
11855         metachange : true,
11856         /**
11857          * @event add
11858          * Fires when Records have been added to the Store
11859          * @param {Store} this
11860          * @param {Roo.data.Record[]} records The array of Records added
11861          * @param {Number} index The index at which the record(s) were added
11862          */
11863         add : true,
11864         /**
11865          * @event remove
11866          * Fires when a Record has been removed from the Store
11867          * @param {Store} this
11868          * @param {Roo.data.Record} record The Record that was removed
11869          * @param {Number} index The index at which the record was removed
11870          */
11871         remove : true,
11872         /**
11873          * @event update
11874          * Fires when a Record has been updated
11875          * @param {Store} this
11876          * @param {Roo.data.Record} record The Record that was updated
11877          * @param {String} operation The update operation being performed.  Value may be one of:
11878          * <pre><code>
11879  Roo.data.Record.EDIT
11880  Roo.data.Record.REJECT
11881  Roo.data.Record.COMMIT
11882          * </code></pre>
11883          */
11884         update : true,
11885         /**
11886          * @event clear
11887          * Fires when the data cache has been cleared.
11888          * @param {Store} this
11889          */
11890         clear : true,
11891         /**
11892          * @event beforeload
11893          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11894          * the load action will be canceled.
11895          * @param {Store} this
11896          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11897          */
11898         beforeload : true,
11899         /**
11900          * @event beforeloadadd
11901          * Fires after a new set of Records has been loaded.
11902          * @param {Store} this
11903          * @param {Roo.data.Record[]} records The Records that were loaded
11904          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11905          */
11906         beforeloadadd : true,
11907         /**
11908          * @event load
11909          * Fires after a new set of Records has been loaded, before they are added to the store.
11910          * @param {Store} this
11911          * @param {Roo.data.Record[]} records The Records that were loaded
11912          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11913          * @params {Object} return from reader
11914          */
11915         load : true,
11916         /**
11917          * @event loadexception
11918          * Fires if an exception occurs in the Proxy during loading.
11919          * Called with the signature of the Proxy's "loadexception" event.
11920          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11921          * 
11922          * @param {Proxy} 
11923          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11924          * @param {Object} load options 
11925          * @param {Object} jsonData from your request (normally this contains the Exception)
11926          */
11927         loadexception : true
11928     });
11929     
11930     if(this.proxy){
11931         this.proxy = Roo.factory(this.proxy, Roo.data);
11932         this.proxy.xmodule = this.xmodule || false;
11933         this.relayEvents(this.proxy,  ["loadexception"]);
11934     }
11935     this.sortToggle = {};
11936     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11937
11938     Roo.data.Store.superclass.constructor.call(this);
11939
11940     if(this.inlineData){
11941         this.loadData(this.inlineData);
11942         delete this.inlineData;
11943     }
11944 };
11945
11946 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11947      /**
11948     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11949     * without a remote query - used by combo/forms at present.
11950     */
11951     
11952     /**
11953     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11954     */
11955     /**
11956     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11957     */
11958     /**
11959     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11960     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11961     */
11962     /**
11963     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11964     * on any HTTP request
11965     */
11966     /**
11967     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11968     */
11969     /**
11970     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11971     */
11972     multiSort: false,
11973     /**
11974     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11975     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11976     */
11977     remoteSort : false,
11978
11979     /**
11980     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11981      * loaded or when a record is removed. (defaults to false).
11982     */
11983     pruneModifiedRecords : false,
11984
11985     // private
11986     lastOptions : null,
11987
11988     /**
11989      * Add Records to the Store and fires the add event.
11990      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11991      */
11992     add : function(records){
11993         records = [].concat(records);
11994         for(var i = 0, len = records.length; i < len; i++){
11995             records[i].join(this);
11996         }
11997         var index = this.data.length;
11998         this.data.addAll(records);
11999         this.fireEvent("add", this, records, index);
12000     },
12001
12002     /**
12003      * Remove a Record from the Store and fires the remove event.
12004      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
12005      */
12006     remove : function(record){
12007         var index = this.data.indexOf(record);
12008         this.data.removeAt(index);
12009  
12010         if(this.pruneModifiedRecords){
12011             this.modified.remove(record);
12012         }
12013         this.fireEvent("remove", this, record, index);
12014     },
12015
12016     /**
12017      * Remove all Records from the Store and fires the clear event.
12018      */
12019     removeAll : function(){
12020         this.data.clear();
12021         if(this.pruneModifiedRecords){
12022             this.modified = [];
12023         }
12024         this.fireEvent("clear", this);
12025     },
12026
12027     /**
12028      * Inserts Records to the Store at the given index and fires the add event.
12029      * @param {Number} index The start index at which to insert the passed Records.
12030      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
12031      */
12032     insert : function(index, records){
12033         records = [].concat(records);
12034         for(var i = 0, len = records.length; i < len; i++){
12035             this.data.insert(index, records[i]);
12036             records[i].join(this);
12037         }
12038         this.fireEvent("add", this, records, index);
12039     },
12040
12041     /**
12042      * Get the index within the cache of the passed Record.
12043      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
12044      * @return {Number} The index of the passed Record. Returns -1 if not found.
12045      */
12046     indexOf : function(record){
12047         return this.data.indexOf(record);
12048     },
12049
12050     /**
12051      * Get the index within the cache of the Record with the passed id.
12052      * @param {String} id The id of the Record to find.
12053      * @return {Number} The index of the Record. Returns -1 if not found.
12054      */
12055     indexOfId : function(id){
12056         return this.data.indexOfKey(id);
12057     },
12058
12059     /**
12060      * Get the Record with the specified id.
12061      * @param {String} id The id of the Record to find.
12062      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
12063      */
12064     getById : function(id){
12065         return this.data.key(id);
12066     },
12067
12068     /**
12069      * Get the Record at the specified index.
12070      * @param {Number} index The index of the Record to find.
12071      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
12072      */
12073     getAt : function(index){
12074         return this.data.itemAt(index);
12075     },
12076
12077     /**
12078      * Returns a range of Records between specified indices.
12079      * @param {Number} startIndex (optional) The starting index (defaults to 0)
12080      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
12081      * @return {Roo.data.Record[]} An array of Records
12082      */
12083     getRange : function(start, end){
12084         return this.data.getRange(start, end);
12085     },
12086
12087     // private
12088     storeOptions : function(o){
12089         o = Roo.apply({}, o);
12090         delete o.callback;
12091         delete o.scope;
12092         this.lastOptions = o;
12093     },
12094
12095     /**
12096      * Loads the Record cache from the configured Proxy using the configured Reader.
12097      * <p>
12098      * If using remote paging, then the first load call must specify the <em>start</em>
12099      * and <em>limit</em> properties in the options.params property to establish the initial
12100      * position within the dataset, and the number of Records to cache on each read from the Proxy.
12101      * <p>
12102      * <strong>It is important to note that for remote data sources, loading is asynchronous,
12103      * and this call will return before the new data has been loaded. Perform any post-processing
12104      * in a callback function, or in a "load" event handler.</strong>
12105      * <p>
12106      * @param {Object} options An object containing properties which control loading options:<ul>
12107      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
12108      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
12109      * passed the following arguments:<ul>
12110      * <li>r : Roo.data.Record[]</li>
12111      * <li>options: Options object from the load call</li>
12112      * <li>success: Boolean success indicator</li></ul></li>
12113      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
12114      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
12115      * </ul>
12116      */
12117     load : function(options){
12118         options = options || {};
12119         if(this.fireEvent("beforeload", this, options) !== false){
12120             this.storeOptions(options);
12121             var p = Roo.apply(options.params || {}, this.baseParams);
12122             // if meta was not loaded from remote source.. try requesting it.
12123             if (!this.reader.metaFromRemote) {
12124                 p._requestMeta = 1;
12125             }
12126             if(this.sortInfo && this.remoteSort){
12127                 var pn = this.paramNames;
12128                 p[pn["sort"]] = this.sortInfo.field;
12129                 p[pn["dir"]] = this.sortInfo.direction;
12130             }
12131             if (this.multiSort) {
12132                 var pn = this.paramNames;
12133                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
12134             }
12135             
12136             this.proxy.load(p, this.reader, this.loadRecords, this, options);
12137         }
12138     },
12139
12140     /**
12141      * Reloads the Record cache from the configured Proxy using the configured Reader and
12142      * the options from the last load operation performed.
12143      * @param {Object} options (optional) An object containing properties which may override the options
12144      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
12145      * the most recently used options are reused).
12146      */
12147     reload : function(options){
12148         this.load(Roo.applyIf(options||{}, this.lastOptions));
12149     },
12150
12151     // private
12152     // Called as a callback by the Reader during a load operation.
12153     loadRecords : function(o, options, success){
12154         if(!o || success === false){
12155             if(success !== false){
12156                 this.fireEvent("load", this, [], options, o);
12157             }
12158             if(options.callback){
12159                 options.callback.call(options.scope || this, [], options, false);
12160             }
12161             return;
12162         }
12163         // if data returned failure - throw an exception.
12164         if (o.success === false) {
12165             // show a message if no listener is registered.
12166             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
12167                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
12168             }
12169             // loadmask wil be hooked into this..
12170             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
12171             return;
12172         }
12173         var r = o.records, t = o.totalRecords || r.length;
12174         
12175         this.fireEvent("beforeloadadd", this, r, options, o);
12176         
12177         if(!options || options.add !== true){
12178             if(this.pruneModifiedRecords){
12179                 this.modified = [];
12180             }
12181             for(var i = 0, len = r.length; i < len; i++){
12182                 r[i].join(this);
12183             }
12184             if(this.snapshot){
12185                 this.data = this.snapshot;
12186                 delete this.snapshot;
12187             }
12188             this.data.clear();
12189             this.data.addAll(r);
12190             this.totalLength = t;
12191             this.applySort();
12192             this.fireEvent("datachanged", this);
12193         }else{
12194             this.totalLength = Math.max(t, this.data.length+r.length);
12195             this.add(r);
12196         }
12197         
12198         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
12199                 
12200             var e = new Roo.data.Record({});
12201
12202             e.set(this.parent.displayField, this.parent.emptyTitle);
12203             e.set(this.parent.valueField, '');
12204
12205             this.insert(0, e);
12206         }
12207             
12208         this.fireEvent("load", this, r, options, o);
12209         if(options.callback){
12210             options.callback.call(options.scope || this, r, options, true);
12211         }
12212     },
12213
12214
12215     /**
12216      * Loads data from a passed data block. A Reader which understands the format of the data
12217      * must have been configured in the constructor.
12218      * @param {Object} data The data block from which to read the Records.  The format of the data expected
12219      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
12220      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
12221      */
12222     loadData : function(o, append){
12223         var r = this.reader.readRecords(o);
12224         this.loadRecords(r, {add: append}, true);
12225     },
12226     
12227      /**
12228      * using 'cn' the nested child reader read the child array into it's child stores.
12229      * @param {Object} rec The record with a 'children array
12230      */
12231     loadDataFromChildren : function(rec)
12232     {
12233         this.loadData(this.reader.toLoadData(rec));
12234     },
12235     
12236
12237     /**
12238      * Gets the number of cached records.
12239      * <p>
12240      * <em>If using paging, this may not be the total size of the dataset. If the data object
12241      * used by the Reader contains the dataset size, then the getTotalCount() function returns
12242      * the data set size</em>
12243      */
12244     getCount : function(){
12245         return this.data.length || 0;
12246     },
12247
12248     /**
12249      * Gets the total number of records in the dataset as returned by the server.
12250      * <p>
12251      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
12252      * the dataset size</em>
12253      */
12254     getTotalCount : function(){
12255         return this.totalLength || 0;
12256     },
12257
12258     /**
12259      * Returns the sort state of the Store as an object with two properties:
12260      * <pre><code>
12261  field {String} The name of the field by which the Records are sorted
12262  direction {String} The sort order, "ASC" or "DESC"
12263      * </code></pre>
12264      */
12265     getSortState : function(){
12266         return this.sortInfo;
12267     },
12268
12269     // private
12270     applySort : function(){
12271         if(this.sortInfo && !this.remoteSort){
12272             var s = this.sortInfo, f = s.field;
12273             var st = this.fields.get(f).sortType;
12274             var fn = function(r1, r2){
12275                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
12276                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12277             };
12278             this.data.sort(s.direction, fn);
12279             if(this.snapshot && this.snapshot != this.data){
12280                 this.snapshot.sort(s.direction, fn);
12281             }
12282         }
12283     },
12284
12285     /**
12286      * Sets the default sort column and order to be used by the next load operation.
12287      * @param {String} fieldName The name of the field to sort by.
12288      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12289      */
12290     setDefaultSort : function(field, dir){
12291         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
12292     },
12293
12294     /**
12295      * Sort the Records.
12296      * If remote sorting is used, the sort is performed on the server, and the cache is
12297      * reloaded. If local sorting is used, the cache is sorted internally.
12298      * @param {String} fieldName The name of the field to sort by.
12299      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12300      */
12301     sort : function(fieldName, dir){
12302         var f = this.fields.get(fieldName);
12303         if(!dir){
12304             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
12305             
12306             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
12307                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
12308             }else{
12309                 dir = f.sortDir;
12310             }
12311         }
12312         this.sortToggle[f.name] = dir;
12313         this.sortInfo = {field: f.name, direction: dir};
12314         if(!this.remoteSort){
12315             this.applySort();
12316             this.fireEvent("datachanged", this);
12317         }else{
12318             this.load(this.lastOptions);
12319         }
12320     },
12321
12322     /**
12323      * Calls the specified function for each of the Records in the cache.
12324      * @param {Function} fn The function to call. The Record is passed as the first parameter.
12325      * Returning <em>false</em> aborts and exits the iteration.
12326      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
12327      */
12328     each : function(fn, scope){
12329         this.data.each(fn, scope);
12330     },
12331
12332     /**
12333      * Gets all records modified since the last commit.  Modified records are persisted across load operations
12334      * (e.g., during paging).
12335      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
12336      */
12337     getModifiedRecords : function(){
12338         return this.modified;
12339     },
12340
12341     // private
12342     createFilterFn : function(property, value, anyMatch){
12343         if(!value.exec){ // not a regex
12344             value = String(value);
12345             if(value.length == 0){
12346                 return false;
12347             }
12348             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
12349         }
12350         return function(r){
12351             return value.test(r.data[property]);
12352         };
12353     },
12354
12355     /**
12356      * Sums the value of <i>property</i> for each record between start and end and returns the result.
12357      * @param {String} property A field on your records
12358      * @param {Number} start The record index to start at (defaults to 0)
12359      * @param {Number} end The last record index to include (defaults to length - 1)
12360      * @return {Number} The sum
12361      */
12362     sum : function(property, start, end){
12363         var rs = this.data.items, v = 0;
12364         start = start || 0;
12365         end = (end || end === 0) ? end : rs.length-1;
12366
12367         for(var i = start; i <= end; i++){
12368             v += (rs[i].data[property] || 0);
12369         }
12370         return v;
12371     },
12372
12373     /**
12374      * Filter the records by a specified property.
12375      * @param {String} field A field on your records
12376      * @param {String/RegExp} value Either a string that the field
12377      * should start with or a RegExp to test against the field
12378      * @param {Boolean} anyMatch True to match any part not just the beginning
12379      */
12380     filter : function(property, value, anyMatch){
12381         var fn = this.createFilterFn(property, value, anyMatch);
12382         return fn ? this.filterBy(fn) : this.clearFilter();
12383     },
12384
12385     /**
12386      * Filter by a function. The specified function will be called with each
12387      * record in this data source. If the function returns true the record is included,
12388      * otherwise it is filtered.
12389      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12390      * @param {Object} scope (optional) The scope of the function (defaults to this)
12391      */
12392     filterBy : function(fn, scope){
12393         this.snapshot = this.snapshot || this.data;
12394         this.data = this.queryBy(fn, scope||this);
12395         this.fireEvent("datachanged", this);
12396     },
12397
12398     /**
12399      * Query the records by a specified property.
12400      * @param {String} field A field on your records
12401      * @param {String/RegExp} value Either a string that the field
12402      * should start with or a RegExp to test against the field
12403      * @param {Boolean} anyMatch True to match any part not just the beginning
12404      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12405      */
12406     query : function(property, value, anyMatch){
12407         var fn = this.createFilterFn(property, value, anyMatch);
12408         return fn ? this.queryBy(fn) : this.data.clone();
12409     },
12410
12411     /**
12412      * Query by a function. The specified function will be called with each
12413      * record in this data source. If the function returns true the record is included
12414      * in the results.
12415      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12416      * @param {Object} scope (optional) The scope of the function (defaults to this)
12417       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12418      **/
12419     queryBy : function(fn, scope){
12420         var data = this.snapshot || this.data;
12421         return data.filterBy(fn, scope||this);
12422     },
12423
12424     /**
12425      * Collects unique values for a particular dataIndex from this store.
12426      * @param {String} dataIndex The property to collect
12427      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12428      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12429      * @return {Array} An array of the unique values
12430      **/
12431     collect : function(dataIndex, allowNull, bypassFilter){
12432         var d = (bypassFilter === true && this.snapshot) ?
12433                 this.snapshot.items : this.data.items;
12434         var v, sv, r = [], l = {};
12435         for(var i = 0, len = d.length; i < len; i++){
12436             v = d[i].data[dataIndex];
12437             sv = String(v);
12438             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12439                 l[sv] = true;
12440                 r[r.length] = v;
12441             }
12442         }
12443         return r;
12444     },
12445
12446     /**
12447      * Revert to a view of the Record cache with no filtering applied.
12448      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12449      */
12450     clearFilter : function(suppressEvent){
12451         if(this.snapshot && this.snapshot != this.data){
12452             this.data = this.snapshot;
12453             delete this.snapshot;
12454             if(suppressEvent !== true){
12455                 this.fireEvent("datachanged", this);
12456             }
12457         }
12458     },
12459
12460     // private
12461     afterEdit : function(record){
12462         if(this.modified.indexOf(record) == -1){
12463             this.modified.push(record);
12464         }
12465         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12466     },
12467     
12468     // private
12469     afterReject : function(record){
12470         this.modified.remove(record);
12471         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12472     },
12473
12474     // private
12475     afterCommit : function(record){
12476         this.modified.remove(record);
12477         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12478     },
12479
12480     /**
12481      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12482      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12483      */
12484     commitChanges : function(){
12485         var m = this.modified.slice(0);
12486         this.modified = [];
12487         for(var i = 0, len = m.length; i < len; i++){
12488             m[i].commit();
12489         }
12490     },
12491
12492     /**
12493      * Cancel outstanding changes on all changed records.
12494      */
12495     rejectChanges : function(){
12496         var m = this.modified.slice(0);
12497         this.modified = [];
12498         for(var i = 0, len = m.length; i < len; i++){
12499             m[i].reject();
12500         }
12501     },
12502
12503     onMetaChange : function(meta, rtype, o){
12504         this.recordType = rtype;
12505         this.fields = rtype.prototype.fields;
12506         delete this.snapshot;
12507         this.sortInfo = meta.sortInfo || this.sortInfo;
12508         this.modified = [];
12509         this.fireEvent('metachange', this, this.reader.meta);
12510     },
12511     
12512     moveIndex : function(data, type)
12513     {
12514         var index = this.indexOf(data);
12515         
12516         var newIndex = index + type;
12517         
12518         this.remove(data);
12519         
12520         this.insert(newIndex, data);
12521         
12522     }
12523 });/*
12524  * Based on:
12525  * Ext JS Library 1.1.1
12526  * Copyright(c) 2006-2007, Ext JS, LLC.
12527  *
12528  * Originally Released Under LGPL - original licence link has changed is not relivant.
12529  *
12530  * Fork - LGPL
12531  * <script type="text/javascript">
12532  */
12533
12534 /**
12535  * @class Roo.data.SimpleStore
12536  * @extends Roo.data.Store
12537  * Small helper class to make creating Stores from Array data easier.
12538  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12539  * @cfg {Array} fields An array of field definition objects, or field name strings.
12540  * @cfg {Object} an existing reader (eg. copied from another store)
12541  * @cfg {Array} data The multi-dimensional array of data
12542  * @constructor
12543  * @param {Object} config
12544  */
12545 Roo.data.SimpleStore = function(config)
12546 {
12547     Roo.data.SimpleStore.superclass.constructor.call(this, {
12548         isLocal : true,
12549         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
12550                 id: config.id
12551             },
12552             Roo.data.Record.create(config.fields)
12553         ),
12554         proxy : new Roo.data.MemoryProxy(config.data)
12555     });
12556     this.load();
12557 };
12558 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12559  * Based on:
12560  * Ext JS Library 1.1.1
12561  * Copyright(c) 2006-2007, Ext JS, LLC.
12562  *
12563  * Originally Released Under LGPL - original licence link has changed is not relivant.
12564  *
12565  * Fork - LGPL
12566  * <script type="text/javascript">
12567  */
12568
12569 /**
12570 /**
12571  * @extends Roo.data.Store
12572  * @class Roo.data.JsonStore
12573  * Small helper class to make creating Stores for JSON data easier. <br/>
12574 <pre><code>
12575 var store = new Roo.data.JsonStore({
12576     url: 'get-images.php',
12577     root: 'images',
12578     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12579 });
12580 </code></pre>
12581  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12582  * JsonReader and HttpProxy (unless inline data is provided).</b>
12583  * @cfg {Array} fields An array of field definition objects, or field name strings.
12584  * @constructor
12585  * @param {Object} config
12586  */
12587 Roo.data.JsonStore = function(c){
12588     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12589         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12590         reader: new Roo.data.JsonReader(c, c.fields)
12591     }));
12592 };
12593 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12594  * Based on:
12595  * Ext JS Library 1.1.1
12596  * Copyright(c) 2006-2007, Ext JS, LLC.
12597  *
12598  * Originally Released Under LGPL - original licence link has changed is not relivant.
12599  *
12600  * Fork - LGPL
12601  * <script type="text/javascript">
12602  */
12603
12604  
12605 Roo.data.Field = function(config){
12606     if(typeof config == "string"){
12607         config = {name: config};
12608     }
12609     Roo.apply(this, config);
12610     
12611     if(!this.type){
12612         this.type = "auto";
12613     }
12614     
12615     var st = Roo.data.SortTypes;
12616     // named sortTypes are supported, here we look them up
12617     if(typeof this.sortType == "string"){
12618         this.sortType = st[this.sortType];
12619     }
12620     
12621     // set default sortType for strings and dates
12622     if(!this.sortType){
12623         switch(this.type){
12624             case "string":
12625                 this.sortType = st.asUCString;
12626                 break;
12627             case "date":
12628                 this.sortType = st.asDate;
12629                 break;
12630             default:
12631                 this.sortType = st.none;
12632         }
12633     }
12634
12635     // define once
12636     var stripRe = /[\$,%]/g;
12637
12638     // prebuilt conversion function for this field, instead of
12639     // switching every time we're reading a value
12640     if(!this.convert){
12641         var cv, dateFormat = this.dateFormat;
12642         switch(this.type){
12643             case "":
12644             case "auto":
12645             case undefined:
12646                 cv = function(v){ return v; };
12647                 break;
12648             case "string":
12649                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12650                 break;
12651             case "int":
12652                 cv = function(v){
12653                     return v !== undefined && v !== null && v !== '' ?
12654                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12655                     };
12656                 break;
12657             case "float":
12658                 cv = function(v){
12659                     return v !== undefined && v !== null && v !== '' ?
12660                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12661                     };
12662                 break;
12663             case "bool":
12664             case "boolean":
12665                 cv = function(v){ return v === true || v === "true" || v == 1; };
12666                 break;
12667             case "date":
12668                 cv = function(v){
12669                     if(!v){
12670                         return '';
12671                     }
12672                     if(v instanceof Date){
12673                         return v;
12674                     }
12675                     if(dateFormat){
12676                         if(dateFormat == "timestamp"){
12677                             return new Date(v*1000);
12678                         }
12679                         return Date.parseDate(v, dateFormat);
12680                     }
12681                     var parsed = Date.parse(v);
12682                     return parsed ? new Date(parsed) : null;
12683                 };
12684              break;
12685             
12686         }
12687         this.convert = cv;
12688     }
12689 };
12690
12691 Roo.data.Field.prototype = {
12692     dateFormat: null,
12693     defaultValue: "",
12694     mapping: null,
12695     sortType : null,
12696     sortDir : "ASC"
12697 };/*
12698  * Based on:
12699  * Ext JS Library 1.1.1
12700  * Copyright(c) 2006-2007, Ext JS, LLC.
12701  *
12702  * Originally Released Under LGPL - original licence link has changed is not relivant.
12703  *
12704  * Fork - LGPL
12705  * <script type="text/javascript">
12706  */
12707  
12708 // Base class for reading structured data from a data source.  This class is intended to be
12709 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12710
12711 /**
12712  * @class Roo.data.DataReader
12713  * Base class for reading structured data from a data source.  This class is intended to be
12714  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12715  */
12716
12717 Roo.data.DataReader = function(meta, recordType){
12718     
12719     this.meta = meta;
12720     
12721     this.recordType = recordType instanceof Array ? 
12722         Roo.data.Record.create(recordType) : recordType;
12723 };
12724
12725 Roo.data.DataReader.prototype = {
12726     
12727     
12728     readerType : 'Data',
12729      /**
12730      * Create an empty record
12731      * @param {Object} data (optional) - overlay some values
12732      * @return {Roo.data.Record} record created.
12733      */
12734     newRow :  function(d) {
12735         var da =  {};
12736         this.recordType.prototype.fields.each(function(c) {
12737             switch( c.type) {
12738                 case 'int' : da[c.name] = 0; break;
12739                 case 'date' : da[c.name] = new Date(); break;
12740                 case 'float' : da[c.name] = 0.0; break;
12741                 case 'boolean' : da[c.name] = false; break;
12742                 default : da[c.name] = ""; break;
12743             }
12744             
12745         });
12746         return new this.recordType(Roo.apply(da, d));
12747     }
12748     
12749     
12750 };/*
12751  * Based on:
12752  * Ext JS Library 1.1.1
12753  * Copyright(c) 2006-2007, Ext JS, LLC.
12754  *
12755  * Originally Released Under LGPL - original licence link has changed is not relivant.
12756  *
12757  * Fork - LGPL
12758  * <script type="text/javascript">
12759  */
12760
12761 /**
12762  * @class Roo.data.DataProxy
12763  * @extends Roo.data.Observable
12764  * This class is an abstract base class for implementations which provide retrieval of
12765  * unformatted data objects.<br>
12766  * <p>
12767  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12768  * (of the appropriate type which knows how to parse the data object) to provide a block of
12769  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12770  * <p>
12771  * Custom implementations must implement the load method as described in
12772  * {@link Roo.data.HttpProxy#load}.
12773  */
12774 Roo.data.DataProxy = function(){
12775     this.addEvents({
12776         /**
12777          * @event beforeload
12778          * Fires before a network request is made to retrieve a data object.
12779          * @param {Object} This DataProxy object.
12780          * @param {Object} params The params parameter to the load function.
12781          */
12782         beforeload : true,
12783         /**
12784          * @event load
12785          * Fires before the load method's callback is called.
12786          * @param {Object} This DataProxy object.
12787          * @param {Object} o The data object.
12788          * @param {Object} arg The callback argument object passed to the load function.
12789          */
12790         load : true,
12791         /**
12792          * @event loadexception
12793          * Fires if an Exception occurs during data retrieval.
12794          * @param {Object} This DataProxy object.
12795          * @param {Object} o The data object.
12796          * @param {Object} arg The callback argument object passed to the load function.
12797          * @param {Object} e The Exception.
12798          */
12799         loadexception : true
12800     });
12801     Roo.data.DataProxy.superclass.constructor.call(this);
12802 };
12803
12804 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12805
12806     /**
12807      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12808      */
12809 /*
12810  * Based on:
12811  * Ext JS Library 1.1.1
12812  * Copyright(c) 2006-2007, Ext JS, LLC.
12813  *
12814  * Originally Released Under LGPL - original licence link has changed is not relivant.
12815  *
12816  * Fork - LGPL
12817  * <script type="text/javascript">
12818  */
12819 /**
12820  * @class Roo.data.MemoryProxy
12821  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12822  * to the Reader when its load method is called.
12823  * @constructor
12824  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12825  */
12826 Roo.data.MemoryProxy = function(data){
12827     if (data.data) {
12828         data = data.data;
12829     }
12830     Roo.data.MemoryProxy.superclass.constructor.call(this);
12831     this.data = data;
12832 };
12833
12834 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12835     
12836     /**
12837      * Load data from the requested source (in this case an in-memory
12838      * data object passed to the constructor), read the data object into
12839      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12840      * process that block using the passed callback.
12841      * @param {Object} params This parameter is not used by the MemoryProxy class.
12842      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12843      * object into a block of Roo.data.Records.
12844      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12845      * The function must be passed <ul>
12846      * <li>The Record block object</li>
12847      * <li>The "arg" argument from the load function</li>
12848      * <li>A boolean success indicator</li>
12849      * </ul>
12850      * @param {Object} scope The scope in which to call the callback
12851      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12852      */
12853     load : function(params, reader, callback, scope, arg){
12854         params = params || {};
12855         var result;
12856         try {
12857             result = reader.readRecords(params.data ? params.data :this.data);
12858         }catch(e){
12859             this.fireEvent("loadexception", this, arg, null, e);
12860             callback.call(scope, null, arg, false);
12861             return;
12862         }
12863         callback.call(scope, result, arg, true);
12864     },
12865     
12866     // private
12867     update : function(params, records){
12868         
12869     }
12870 });/*
12871  * Based on:
12872  * Ext JS Library 1.1.1
12873  * Copyright(c) 2006-2007, Ext JS, LLC.
12874  *
12875  * Originally Released Under LGPL - original licence link has changed is not relivant.
12876  *
12877  * Fork - LGPL
12878  * <script type="text/javascript">
12879  */
12880 /**
12881  * @class Roo.data.HttpProxy
12882  * @extends Roo.data.DataProxy
12883  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12884  * configured to reference a certain URL.<br><br>
12885  * <p>
12886  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12887  * from which the running page was served.<br><br>
12888  * <p>
12889  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12890  * <p>
12891  * Be aware that to enable the browser to parse an XML document, the server must set
12892  * the Content-Type header in the HTTP response to "text/xml".
12893  * @constructor
12894  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12895  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12896  * will be used to make the request.
12897  */
12898 Roo.data.HttpProxy = function(conn){
12899     Roo.data.HttpProxy.superclass.constructor.call(this);
12900     // is conn a conn config or a real conn?
12901     this.conn = conn;
12902     this.useAjax = !conn || !conn.events;
12903   
12904 };
12905
12906 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12907     // thse are take from connection...
12908     
12909     /**
12910      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12911      */
12912     /**
12913      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12914      * extra parameters to each request made by this object. (defaults to undefined)
12915      */
12916     /**
12917      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12918      *  to each request made by this object. (defaults to undefined)
12919      */
12920     /**
12921      * @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)
12922      */
12923     /**
12924      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12925      */
12926      /**
12927      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12928      * @type Boolean
12929      */
12930   
12931
12932     /**
12933      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12934      * @type Boolean
12935      */
12936     /**
12937      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12938      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12939      * a finer-grained basis than the DataProxy events.
12940      */
12941     getConnection : function(){
12942         return this.useAjax ? Roo.Ajax : this.conn;
12943     },
12944
12945     /**
12946      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12947      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12948      * process that block using the passed callback.
12949      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12950      * for the request to the remote server.
12951      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12952      * object into a block of Roo.data.Records.
12953      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12954      * The function must be passed <ul>
12955      * <li>The Record block object</li>
12956      * <li>The "arg" argument from the load function</li>
12957      * <li>A boolean success indicator</li>
12958      * </ul>
12959      * @param {Object} scope The scope in which to call the callback
12960      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12961      */
12962     load : function(params, reader, callback, scope, arg){
12963         if(this.fireEvent("beforeload", this, params) !== false){
12964             var  o = {
12965                 params : params || {},
12966                 request: {
12967                     callback : callback,
12968                     scope : scope,
12969                     arg : arg
12970                 },
12971                 reader: reader,
12972                 callback : this.loadResponse,
12973                 scope: this
12974             };
12975             if(this.useAjax){
12976                 Roo.applyIf(o, this.conn);
12977                 if(this.activeRequest){
12978                     Roo.Ajax.abort(this.activeRequest);
12979                 }
12980                 this.activeRequest = Roo.Ajax.request(o);
12981             }else{
12982                 this.conn.request(o);
12983             }
12984         }else{
12985             callback.call(scope||this, null, arg, false);
12986         }
12987     },
12988
12989     // private
12990     loadResponse : function(o, success, response){
12991         delete this.activeRequest;
12992         if(!success){
12993             this.fireEvent("loadexception", this, o, response);
12994             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12995             return;
12996         }
12997         var result;
12998         try {
12999             result = o.reader.read(response);
13000         }catch(e){
13001             this.fireEvent("loadexception", this, o, response, e);
13002             o.request.callback.call(o.request.scope, null, o.request.arg, false);
13003             return;
13004         }
13005         
13006         this.fireEvent("load", this, o, o.request.arg);
13007         o.request.callback.call(o.request.scope, result, o.request.arg, true);
13008     },
13009
13010     // private
13011     update : function(dataSet){
13012
13013     },
13014
13015     // private
13016     updateResponse : function(dataSet){
13017
13018     }
13019 });/*
13020  * Based on:
13021  * Ext JS Library 1.1.1
13022  * Copyright(c) 2006-2007, Ext JS, LLC.
13023  *
13024  * Originally Released Under LGPL - original licence link has changed is not relivant.
13025  *
13026  * Fork - LGPL
13027  * <script type="text/javascript">
13028  */
13029
13030 /**
13031  * @class Roo.data.ScriptTagProxy
13032  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
13033  * other than the originating domain of the running page.<br><br>
13034  * <p>
13035  * <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
13036  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
13037  * <p>
13038  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
13039  * source code that is used as the source inside a &lt;script> tag.<br><br>
13040  * <p>
13041  * In order for the browser to process the returned data, the server must wrap the data object
13042  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
13043  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
13044  * depending on whether the callback name was passed:
13045  * <p>
13046  * <pre><code>
13047 boolean scriptTag = false;
13048 String cb = request.getParameter("callback");
13049 if (cb != null) {
13050     scriptTag = true;
13051     response.setContentType("text/javascript");
13052 } else {
13053     response.setContentType("application/x-json");
13054 }
13055 Writer out = response.getWriter();
13056 if (scriptTag) {
13057     out.write(cb + "(");
13058 }
13059 out.print(dataBlock.toJsonString());
13060 if (scriptTag) {
13061     out.write(");");
13062 }
13063 </pre></code>
13064  *
13065  * @constructor
13066  * @param {Object} config A configuration object.
13067  */
13068 Roo.data.ScriptTagProxy = function(config){
13069     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
13070     Roo.apply(this, config);
13071     this.head = document.getElementsByTagName("head")[0];
13072 };
13073
13074 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
13075
13076 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
13077     /**
13078      * @cfg {String} url The URL from which to request the data object.
13079      */
13080     /**
13081      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
13082      */
13083     timeout : 30000,
13084     /**
13085      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
13086      * the server the name of the callback function set up by the load call to process the returned data object.
13087      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
13088      * javascript output which calls this named function passing the data object as its only parameter.
13089      */
13090     callbackParam : "callback",
13091     /**
13092      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
13093      * name to the request.
13094      */
13095     nocache : true,
13096
13097     /**
13098      * Load data from the configured URL, read the data object into
13099      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13100      * process that block using the passed callback.
13101      * @param {Object} params An object containing properties which are to be used as HTTP parameters
13102      * for the request to the remote server.
13103      * @param {Roo.data.DataReader} reader The Reader object which converts the data
13104      * object into a block of Roo.data.Records.
13105      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
13106      * The function must be passed <ul>
13107      * <li>The Record block object</li>
13108      * <li>The "arg" argument from the load function</li>
13109      * <li>A boolean success indicator</li>
13110      * </ul>
13111      * @param {Object} scope The scope in which to call the callback
13112      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13113      */
13114     load : function(params, reader, callback, scope, arg){
13115         if(this.fireEvent("beforeload", this, params) !== false){
13116
13117             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
13118
13119             var url = this.url;
13120             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
13121             if(this.nocache){
13122                 url += "&_dc=" + (new Date().getTime());
13123             }
13124             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
13125             var trans = {
13126                 id : transId,
13127                 cb : "stcCallback"+transId,
13128                 scriptId : "stcScript"+transId,
13129                 params : params,
13130                 arg : arg,
13131                 url : url,
13132                 callback : callback,
13133                 scope : scope,
13134                 reader : reader
13135             };
13136             var conn = this;
13137
13138             window[trans.cb] = function(o){
13139                 conn.handleResponse(o, trans);
13140             };
13141
13142             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
13143
13144             if(this.autoAbort !== false){
13145                 this.abort();
13146             }
13147
13148             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
13149
13150             var script = document.createElement("script");
13151             script.setAttribute("src", url);
13152             script.setAttribute("type", "text/javascript");
13153             script.setAttribute("id", trans.scriptId);
13154             this.head.appendChild(script);
13155
13156             this.trans = trans;
13157         }else{
13158             callback.call(scope||this, null, arg, false);
13159         }
13160     },
13161
13162     // private
13163     isLoading : function(){
13164         return this.trans ? true : false;
13165     },
13166
13167     /**
13168      * Abort the current server request.
13169      */
13170     abort : function(){
13171         if(this.isLoading()){
13172             this.destroyTrans(this.trans);
13173         }
13174     },
13175
13176     // private
13177     destroyTrans : function(trans, isLoaded){
13178         this.head.removeChild(document.getElementById(trans.scriptId));
13179         clearTimeout(trans.timeoutId);
13180         if(isLoaded){
13181             window[trans.cb] = undefined;
13182             try{
13183                 delete window[trans.cb];
13184             }catch(e){}
13185         }else{
13186             // if hasn't been loaded, wait for load to remove it to prevent script error
13187             window[trans.cb] = function(){
13188                 window[trans.cb] = undefined;
13189                 try{
13190                     delete window[trans.cb];
13191                 }catch(e){}
13192             };
13193         }
13194     },
13195
13196     // private
13197     handleResponse : function(o, trans){
13198         this.trans = false;
13199         this.destroyTrans(trans, true);
13200         var result;
13201         try {
13202             result = trans.reader.readRecords(o);
13203         }catch(e){
13204             this.fireEvent("loadexception", this, o, trans.arg, e);
13205             trans.callback.call(trans.scope||window, null, trans.arg, false);
13206             return;
13207         }
13208         this.fireEvent("load", this, o, trans.arg);
13209         trans.callback.call(trans.scope||window, result, trans.arg, true);
13210     },
13211
13212     // private
13213     handleFailure : function(trans){
13214         this.trans = false;
13215         this.destroyTrans(trans, false);
13216         this.fireEvent("loadexception", this, null, trans.arg);
13217         trans.callback.call(trans.scope||window, null, trans.arg, false);
13218     }
13219 });/*
13220  * Based on:
13221  * Ext JS Library 1.1.1
13222  * Copyright(c) 2006-2007, Ext JS, LLC.
13223  *
13224  * Originally Released Under LGPL - original licence link has changed is not relivant.
13225  *
13226  * Fork - LGPL
13227  * <script type="text/javascript">
13228  */
13229
13230 /**
13231  * @class Roo.data.JsonReader
13232  * @extends Roo.data.DataReader
13233  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
13234  * based on mappings in a provided Roo.data.Record constructor.
13235  * 
13236  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
13237  * in the reply previously. 
13238  * 
13239  * <p>
13240  * Example code:
13241  * <pre><code>
13242 var RecordDef = Roo.data.Record.create([
13243     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
13244     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
13245 ]);
13246 var myReader = new Roo.data.JsonReader({
13247     totalProperty: "results",    // The property which contains the total dataset size (optional)
13248     root: "rows",                // The property which contains an Array of row objects
13249     id: "id"                     // The property within each row object that provides an ID for the record (optional)
13250 }, RecordDef);
13251 </code></pre>
13252  * <p>
13253  * This would consume a JSON file like this:
13254  * <pre><code>
13255 { 'results': 2, 'rows': [
13256     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
13257     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
13258 }
13259 </code></pre>
13260  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
13261  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
13262  * paged from the remote server.
13263  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
13264  * @cfg {String} root name of the property which contains the Array of row objects.
13265  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13266  * @cfg {Array} fields Array of field definition objects
13267  * @constructor
13268  * Create a new JsonReader
13269  * @param {Object} meta Metadata configuration options
13270  * @param {Object} recordType Either an Array of field definition objects,
13271  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
13272  */
13273 Roo.data.JsonReader = function(meta, recordType){
13274     
13275     meta = meta || {};
13276     // set some defaults:
13277     Roo.applyIf(meta, {
13278         totalProperty: 'total',
13279         successProperty : 'success',
13280         root : 'data',
13281         id : 'id'
13282     });
13283     
13284     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13285 };
13286 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
13287     
13288     readerType : 'Json',
13289     
13290     /**
13291      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
13292      * Used by Store query builder to append _requestMeta to params.
13293      * 
13294      */
13295     metaFromRemote : false,
13296     /**
13297      * This method is only used by a DataProxy which has retrieved data from a remote server.
13298      * @param {Object} response The XHR object which contains the JSON data in its responseText.
13299      * @return {Object} data A data block which is used by an Roo.data.Store object as
13300      * a cache of Roo.data.Records.
13301      */
13302     read : function(response){
13303         var json = response.responseText;
13304        
13305         var o = /* eval:var:o */ eval("("+json+")");
13306         if(!o) {
13307             throw {message: "JsonReader.read: Json object not found"};
13308         }
13309         
13310         if(o.metaData){
13311             
13312             delete this.ef;
13313             this.metaFromRemote = true;
13314             this.meta = o.metaData;
13315             this.recordType = Roo.data.Record.create(o.metaData.fields);
13316             this.onMetaChange(this.meta, this.recordType, o);
13317         }
13318         return this.readRecords(o);
13319     },
13320
13321     // private function a store will implement
13322     onMetaChange : function(meta, recordType, o){
13323
13324     },
13325
13326     /**
13327          * @ignore
13328          */
13329     simpleAccess: function(obj, subsc) {
13330         return obj[subsc];
13331     },
13332
13333         /**
13334          * @ignore
13335          */
13336     getJsonAccessor: function(){
13337         var re = /[\[\.]/;
13338         return function(expr) {
13339             try {
13340                 return(re.test(expr))
13341                     ? new Function("obj", "return obj." + expr)
13342                     : function(obj){
13343                         return obj[expr];
13344                     };
13345             } catch(e){}
13346             return Roo.emptyFn;
13347         };
13348     }(),
13349
13350     /**
13351      * Create a data block containing Roo.data.Records from an XML document.
13352      * @param {Object} o An object which contains an Array of row objects in the property specified
13353      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
13354      * which contains the total size of the dataset.
13355      * @return {Object} data A data block which is used by an Roo.data.Store object as
13356      * a cache of Roo.data.Records.
13357      */
13358     readRecords : function(o){
13359         /**
13360          * After any data loads, the raw JSON data is available for further custom processing.
13361          * @type Object
13362          */
13363         this.o = o;
13364         var s = this.meta, Record = this.recordType,
13365             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
13366
13367 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
13368         if (!this.ef) {
13369             if(s.totalProperty) {
13370                     this.getTotal = this.getJsonAccessor(s.totalProperty);
13371                 }
13372                 if(s.successProperty) {
13373                     this.getSuccess = this.getJsonAccessor(s.successProperty);
13374                 }
13375                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
13376                 if (s.id) {
13377                         var g = this.getJsonAccessor(s.id);
13378                         this.getId = function(rec) {
13379                                 var r = g(rec);  
13380                                 return (r === undefined || r === "") ? null : r;
13381                         };
13382                 } else {
13383                         this.getId = function(){return null;};
13384                 }
13385             this.ef = [];
13386             for(var jj = 0; jj < fl; jj++){
13387                 f = fi[jj];
13388                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
13389                 this.ef[jj] = this.getJsonAccessor(map);
13390             }
13391         }
13392
13393         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
13394         if(s.totalProperty){
13395             var vt = parseInt(this.getTotal(o), 10);
13396             if(!isNaN(vt)){
13397                 totalRecords = vt;
13398             }
13399         }
13400         if(s.successProperty){
13401             var vs = this.getSuccess(o);
13402             if(vs === false || vs === 'false'){
13403                 success = false;
13404             }
13405         }
13406         var records = [];
13407         for(var i = 0; i < c; i++){
13408                 var n = root[i];
13409             var values = {};
13410             var id = this.getId(n);
13411             for(var j = 0; j < fl; j++){
13412                 f = fi[j];
13413             var v = this.ef[j](n);
13414             if (!f.convert) {
13415                 Roo.log('missing convert for ' + f.name);
13416                 Roo.log(f);
13417                 continue;
13418             }
13419             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13420             }
13421             var record = new Record(values, id);
13422             record.json = n;
13423             records[i] = record;
13424         }
13425         return {
13426             raw : o,
13427             success : success,
13428             records : records,
13429             totalRecords : totalRecords
13430         };
13431     },
13432     // used when loading children.. @see loadDataFromChildren
13433     toLoadData: function(rec)
13434     {
13435         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13436         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13437         return { data : data, total : data.length };
13438         
13439     }
13440 });/*
13441  * Based on:
13442  * Ext JS Library 1.1.1
13443  * Copyright(c) 2006-2007, Ext JS, LLC.
13444  *
13445  * Originally Released Under LGPL - original licence link has changed is not relivant.
13446  *
13447  * Fork - LGPL
13448  * <script type="text/javascript">
13449  */
13450
13451 /**
13452  * @class Roo.data.ArrayReader
13453  * @extends Roo.data.DataReader
13454  * Data reader class to create an Array of Roo.data.Record objects from an Array.
13455  * Each element of that Array represents a row of data fields. The
13456  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13457  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13458  * <p>
13459  * Example code:.
13460  * <pre><code>
13461 var RecordDef = Roo.data.Record.create([
13462     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
13463     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
13464 ]);
13465 var myReader = new Roo.data.ArrayReader({
13466     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
13467 }, RecordDef);
13468 </code></pre>
13469  * <p>
13470  * This would consume an Array like this:
13471  * <pre><code>
13472 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13473   </code></pre>
13474  
13475  * @constructor
13476  * Create a new JsonReader
13477  * @param {Object} meta Metadata configuration options.
13478  * @param {Object|Array} recordType Either an Array of field definition objects
13479  * 
13480  * @cfg {Array} fields Array of field definition objects
13481  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13482  * as specified to {@link Roo.data.Record#create},
13483  * or an {@link Roo.data.Record} object
13484  *
13485  * 
13486  * created using {@link Roo.data.Record#create}.
13487  */
13488 Roo.data.ArrayReader = function(meta, recordType)
13489 {    
13490     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13491 };
13492
13493 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13494     
13495       /**
13496      * Create a data block containing Roo.data.Records from an XML document.
13497      * @param {Object} o An Array of row objects which represents the dataset.
13498      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13499      * a cache of Roo.data.Records.
13500      */
13501     readRecords : function(o)
13502     {
13503         var sid = this.meta ? this.meta.id : null;
13504         var recordType = this.recordType, fields = recordType.prototype.fields;
13505         var records = [];
13506         var root = o;
13507         for(var i = 0; i < root.length; i++){
13508                 var n = root[i];
13509             var values = {};
13510             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13511             for(var j = 0, jlen = fields.length; j < jlen; j++){
13512                 var f = fields.items[j];
13513                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13514                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13515                 v = f.convert(v);
13516                 values[f.name] = v;
13517             }
13518             var record = new recordType(values, id);
13519             record.json = n;
13520             records[records.length] = record;
13521         }
13522         return {
13523             records : records,
13524             totalRecords : records.length
13525         };
13526     },
13527     // used when loading children.. @see loadDataFromChildren
13528     toLoadData: function(rec)
13529     {
13530         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13531         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13532         
13533     }
13534     
13535     
13536 });/*
13537  * - LGPL
13538  * * 
13539  */
13540
13541 /**
13542  * @class Roo.bootstrap.ComboBox
13543  * @extends Roo.bootstrap.TriggerField
13544  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13545  * @cfg {Boolean} append (true|false) default false
13546  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13547  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13548  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13549  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13550  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13551  * @cfg {Boolean} animate default true
13552  * @cfg {Boolean} emptyResultText only for touch device
13553  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13554  * @cfg {String} emptyTitle default ''
13555  * @constructor
13556  * Create a new ComboBox.
13557  * @param {Object} config Configuration options
13558  */
13559 Roo.bootstrap.ComboBox = function(config){
13560     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13561     this.addEvents({
13562         /**
13563          * @event expand
13564          * Fires when the dropdown list is expanded
13565         * @param {Roo.bootstrap.ComboBox} combo This combo box
13566         */
13567         'expand' : true,
13568         /**
13569          * @event collapse
13570          * Fires when the dropdown list is collapsed
13571         * @param {Roo.bootstrap.ComboBox} combo This combo box
13572         */
13573         'collapse' : true,
13574         /**
13575          * @event beforeselect
13576          * Fires before a list item is selected. Return false to cancel the selection.
13577         * @param {Roo.bootstrap.ComboBox} combo This combo box
13578         * @param {Roo.data.Record} record The data record returned from the underlying store
13579         * @param {Number} index The index of the selected item in the dropdown list
13580         */
13581         'beforeselect' : true,
13582         /**
13583          * @event select
13584          * Fires when a list item is selected
13585         * @param {Roo.bootstrap.ComboBox} combo This combo box
13586         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13587         * @param {Number} index The index of the selected item in the dropdown list
13588         */
13589         'select' : true,
13590         /**
13591          * @event beforequery
13592          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13593          * The event object passed has these properties:
13594         * @param {Roo.bootstrap.ComboBox} combo This combo box
13595         * @param {String} query The query
13596         * @param {Boolean} forceAll true to force "all" query
13597         * @param {Boolean} cancel true to cancel the query
13598         * @param {Object} e The query event object
13599         */
13600         'beforequery': true,
13601          /**
13602          * @event add
13603          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13604         * @param {Roo.bootstrap.ComboBox} combo This combo box
13605         */
13606         'add' : true,
13607         /**
13608          * @event edit
13609          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13610         * @param {Roo.bootstrap.ComboBox} combo This combo box
13611         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13612         */
13613         'edit' : true,
13614         /**
13615          * @event remove
13616          * Fires when the remove value from the combobox array
13617         * @param {Roo.bootstrap.ComboBox} combo This combo box
13618         */
13619         'remove' : true,
13620         /**
13621          * @event afterremove
13622          * Fires when the remove value from the combobox array
13623         * @param {Roo.bootstrap.ComboBox} combo This combo box
13624         */
13625         'afterremove' : true,
13626         /**
13627          * @event specialfilter
13628          * Fires when specialfilter
13629             * @param {Roo.bootstrap.ComboBox} combo This combo box
13630             */
13631         'specialfilter' : true,
13632         /**
13633          * @event tick
13634          * Fires when tick the element
13635             * @param {Roo.bootstrap.ComboBox} combo This combo box
13636             */
13637         'tick' : true,
13638         /**
13639          * @event touchviewdisplay
13640          * Fires when touch view require special display (default is using displayField)
13641             * @param {Roo.bootstrap.ComboBox} combo This combo box
13642             * @param {Object} cfg set html .
13643             */
13644         'touchviewdisplay' : true
13645         
13646     });
13647     
13648     this.item = [];
13649     this.tickItems = [];
13650     
13651     this.selectedIndex = -1;
13652     if(this.mode == 'local'){
13653         if(config.queryDelay === undefined){
13654             this.queryDelay = 10;
13655         }
13656         if(config.minChars === undefined){
13657             this.minChars = 0;
13658         }
13659     }
13660 };
13661
13662 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13663      
13664     /**
13665      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13666      * rendering into an Roo.Editor, defaults to false)
13667      */
13668     /**
13669      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13670      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13671      */
13672     /**
13673      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13674      */
13675     /**
13676      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13677      * the dropdown list (defaults to undefined, with no header element)
13678      */
13679
13680      /**
13681      * @cfg {String/Roo.Template} tpl The template to use to render the output
13682      */
13683      
13684      /**
13685      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13686      */
13687     listWidth: undefined,
13688     /**
13689      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13690      * mode = 'remote' or 'text' if mode = 'local')
13691      */
13692     displayField: undefined,
13693     
13694     /**
13695      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13696      * mode = 'remote' or 'value' if mode = 'local'). 
13697      * Note: use of a valueField requires the user make a selection
13698      * in order for a value to be mapped.
13699      */
13700     valueField: undefined,
13701     /**
13702      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13703      */
13704     modalTitle : '',
13705     
13706     /**
13707      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13708      * field's data value (defaults to the underlying DOM element's name)
13709      */
13710     hiddenName: undefined,
13711     /**
13712      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13713      */
13714     listClass: '',
13715     /**
13716      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13717      */
13718     selectedClass: 'active',
13719     
13720     /**
13721      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13722      */
13723     shadow:'sides',
13724     /**
13725      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13726      * anchor positions (defaults to 'tl-bl')
13727      */
13728     listAlign: 'tl-bl?',
13729     /**
13730      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13731      */
13732     maxHeight: 300,
13733     /**
13734      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13735      * query specified by the allQuery config option (defaults to 'query')
13736      */
13737     triggerAction: 'query',
13738     /**
13739      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13740      * (defaults to 4, does not apply if editable = false)
13741      */
13742     minChars : 4,
13743     /**
13744      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13745      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13746      */
13747     typeAhead: false,
13748     /**
13749      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13750      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13751      */
13752     queryDelay: 500,
13753     /**
13754      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13755      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13756      */
13757     pageSize: 0,
13758     /**
13759      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13760      * when editable = true (defaults to false)
13761      */
13762     selectOnFocus:false,
13763     /**
13764      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13765      */
13766     queryParam: 'query',
13767     /**
13768      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13769      * when mode = 'remote' (defaults to 'Loading...')
13770      */
13771     loadingText: 'Loading...',
13772     /**
13773      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13774      */
13775     resizable: false,
13776     /**
13777      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13778      */
13779     handleHeight : 8,
13780     /**
13781      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13782      * traditional select (defaults to true)
13783      */
13784     editable: true,
13785     /**
13786      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13787      */
13788     allQuery: '',
13789     /**
13790      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13791      */
13792     mode: 'remote',
13793     /**
13794      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13795      * listWidth has a higher value)
13796      */
13797     minListWidth : 70,
13798     /**
13799      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13800      * allow the user to set arbitrary text into the field (defaults to false)
13801      */
13802     forceSelection:false,
13803     /**
13804      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13805      * if typeAhead = true (defaults to 250)
13806      */
13807     typeAheadDelay : 250,
13808     /**
13809      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13810      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13811      */
13812     valueNotFoundText : undefined,
13813     /**
13814      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13815      */
13816     blockFocus : false,
13817     
13818     /**
13819      * @cfg {Boolean} disableClear Disable showing of clear button.
13820      */
13821     disableClear : false,
13822     /**
13823      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13824      */
13825     alwaysQuery : false,
13826     
13827     /**
13828      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13829      */
13830     multiple : false,
13831     
13832     /**
13833      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13834      */
13835     invalidClass : "has-warning",
13836     
13837     /**
13838      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13839      */
13840     validClass : "has-success",
13841     
13842     /**
13843      * @cfg {Boolean} specialFilter (true|false) special filter default false
13844      */
13845     specialFilter : false,
13846     
13847     /**
13848      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13849      */
13850     mobileTouchView : true,
13851     
13852     /**
13853      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13854      */
13855     useNativeIOS : false,
13856     
13857     /**
13858      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13859      */
13860     mobile_restrict_height : false,
13861     
13862     ios_options : false,
13863     
13864     //private
13865     addicon : false,
13866     editicon: false,
13867     
13868     page: 0,
13869     hasQuery: false,
13870     append: false,
13871     loadNext: false,
13872     autoFocus : true,
13873     tickable : false,
13874     btnPosition : 'right',
13875     triggerList : true,
13876     showToggleBtn : true,
13877     animate : true,
13878     emptyResultText: 'Empty',
13879     triggerText : 'Select',
13880     emptyTitle : '',
13881     
13882     // element that contains real text value.. (when hidden is used..)
13883     
13884     getAutoCreate : function()
13885     {   
13886         var cfg = false;
13887         //render
13888         /*
13889          * Render classic select for iso
13890          */
13891         
13892         if(Roo.isIOS && this.useNativeIOS){
13893             cfg = this.getAutoCreateNativeIOS();
13894             return cfg;
13895         }
13896         
13897         /*
13898          * Touch Devices
13899          */
13900         
13901         if(Roo.isTouch && this.mobileTouchView){
13902             cfg = this.getAutoCreateTouchView();
13903             return cfg;;
13904         }
13905         
13906         /*
13907          *  Normal ComboBox
13908          */
13909         if(!this.tickable){
13910             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13911             return cfg;
13912         }
13913         
13914         /*
13915          *  ComboBox with tickable selections
13916          */
13917              
13918         var align = this.labelAlign || this.parentLabelAlign();
13919         
13920         cfg = {
13921             cls : 'form-group roo-combobox-tickable' //input-group
13922         };
13923         
13924         var btn_text_select = '';
13925         var btn_text_done = '';
13926         var btn_text_cancel = '';
13927         
13928         if (this.btn_text_show) {
13929             btn_text_select = 'Select';
13930             btn_text_done = 'Done';
13931             btn_text_cancel = 'Cancel'; 
13932         }
13933         
13934         var buttons = {
13935             tag : 'div',
13936             cls : 'tickable-buttons',
13937             cn : [
13938                 {
13939                     tag : 'button',
13940                     type : 'button',
13941                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13942                     //html : this.triggerText
13943                     html: btn_text_select
13944                 },
13945                 {
13946                     tag : 'button',
13947                     type : 'button',
13948                     name : 'ok',
13949                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13950                     //html : 'Done'
13951                     html: btn_text_done
13952                 },
13953                 {
13954                     tag : 'button',
13955                     type : 'button',
13956                     name : 'cancel',
13957                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13958                     //html : 'Cancel'
13959                     html: btn_text_cancel
13960                 }
13961             ]
13962         };
13963         
13964         if(this.editable){
13965             buttons.cn.unshift({
13966                 tag: 'input',
13967                 cls: 'roo-select2-search-field-input'
13968             });
13969         }
13970         
13971         var _this = this;
13972         
13973         Roo.each(buttons.cn, function(c){
13974             if (_this.size) {
13975                 c.cls += ' btn-' + _this.size;
13976             }
13977
13978             if (_this.disabled) {
13979                 c.disabled = true;
13980             }
13981         });
13982         
13983         var box = {
13984             tag: 'div',
13985             style : 'display: contents',
13986             cn: [
13987                 {
13988                     tag: 'input',
13989                     type : 'hidden',
13990                     cls: 'form-hidden-field'
13991                 },
13992                 {
13993                     tag: 'ul',
13994                     cls: 'roo-select2-choices',
13995                     cn:[
13996                         {
13997                             tag: 'li',
13998                             cls: 'roo-select2-search-field',
13999                             cn: [
14000                                 buttons
14001                             ]
14002                         }
14003                     ]
14004                 }
14005             ]
14006         };
14007         
14008         var combobox = {
14009             cls: 'roo-select2-container input-group roo-select2-container-multi',
14010             cn: [
14011                 
14012                 box
14013 //                {
14014 //                    tag: 'ul',
14015 //                    cls: 'typeahead typeahead-long dropdown-menu',
14016 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
14017 //                }
14018             ]
14019         };
14020         
14021         if(this.hasFeedback && !this.allowBlank){
14022             
14023             var feedback = {
14024                 tag: 'span',
14025                 cls: 'glyphicon form-control-feedback'
14026             };
14027
14028             combobox.cn.push(feedback);
14029         }
14030         
14031         var indicator = {
14032             tag : 'i',
14033             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
14034             tooltip : 'This field is required'
14035         };
14036         if (Roo.bootstrap.version == 4) {
14037             indicator = {
14038                 tag : 'i',
14039                 style : 'display:none'
14040             };
14041         }
14042         if (align ==='left' && this.fieldLabel.length) {
14043             
14044             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
14045             
14046             cfg.cn = [
14047                 indicator,
14048                 {
14049                     tag: 'label',
14050                     'for' :  id,
14051                     cls : 'control-label col-form-label',
14052                     html : this.fieldLabel
14053
14054                 },
14055                 {
14056                     cls : "", 
14057                     cn: [
14058                         combobox
14059                     ]
14060                 }
14061
14062             ];
14063             
14064             var labelCfg = cfg.cn[1];
14065             var contentCfg = cfg.cn[2];
14066             
14067
14068             if(this.indicatorpos == 'right'){
14069                 
14070                 cfg.cn = [
14071                     {
14072                         tag: 'label',
14073                         'for' :  id,
14074                         cls : 'control-label col-form-label',
14075                         cn : [
14076                             {
14077                                 tag : 'span',
14078                                 html : this.fieldLabel
14079                             },
14080                             indicator
14081                         ]
14082                     },
14083                     {
14084                         cls : "",
14085                         cn: [
14086                             combobox
14087                         ]
14088                     }
14089
14090                 ];
14091                 
14092                 
14093                 
14094                 labelCfg = cfg.cn[0];
14095                 contentCfg = cfg.cn[1];
14096             
14097             }
14098             
14099             if(this.labelWidth > 12){
14100                 labelCfg.style = "width: " + this.labelWidth + 'px';
14101             }
14102             
14103             if(this.labelWidth < 13 && this.labelmd == 0){
14104                 this.labelmd = this.labelWidth;
14105             }
14106             
14107             if(this.labellg > 0){
14108                 labelCfg.cls += ' col-lg-' + this.labellg;
14109                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14110             }
14111             
14112             if(this.labelmd > 0){
14113                 labelCfg.cls += ' col-md-' + this.labelmd;
14114                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14115             }
14116             
14117             if(this.labelsm > 0){
14118                 labelCfg.cls += ' col-sm-' + this.labelsm;
14119                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14120             }
14121             
14122             if(this.labelxs > 0){
14123                 labelCfg.cls += ' col-xs-' + this.labelxs;
14124                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14125             }
14126                 
14127                 
14128         } else if ( this.fieldLabel.length) {
14129 //                Roo.log(" label");
14130                  cfg.cn = [
14131                    indicator,
14132                     {
14133                         tag: 'label',
14134                         //cls : 'input-group-addon',
14135                         html : this.fieldLabel
14136                     },
14137                     combobox
14138                 ];
14139                 
14140                 if(this.indicatorpos == 'right'){
14141                     cfg.cn = [
14142                         {
14143                             tag: 'label',
14144                             //cls : 'input-group-addon',
14145                             html : this.fieldLabel
14146                         },
14147                         indicator,
14148                         combobox
14149                     ];
14150                     
14151                 }
14152
14153         } else {
14154             
14155 //                Roo.log(" no label && no align");
14156                 cfg = combobox
14157                      
14158                 
14159         }
14160          
14161         var settings=this;
14162         ['xs','sm','md','lg'].map(function(size){
14163             if (settings[size]) {
14164                 cfg.cls += ' col-' + size + '-' + settings[size];
14165             }
14166         });
14167         
14168         return cfg;
14169         
14170     },
14171     
14172     _initEventsCalled : false,
14173     
14174     // private
14175     initEvents: function()
14176     {   
14177         if (this._initEventsCalled) { // as we call render... prevent looping...
14178             return;
14179         }
14180         this._initEventsCalled = true;
14181         
14182         if (!this.store) {
14183             throw "can not find store for combo";
14184         }
14185         
14186         this.indicator = this.indicatorEl();
14187         
14188         this.store = Roo.factory(this.store, Roo.data);
14189         this.store.parent = this;
14190         
14191         // if we are building from html. then this element is so complex, that we can not really
14192         // use the rendered HTML.
14193         // so we have to trash and replace the previous code.
14194         if (Roo.XComponent.build_from_html) {
14195             // remove this element....
14196             var e = this.el.dom, k=0;
14197             while (e ) { e = e.previousSibling;  ++k;}
14198
14199             this.el.remove();
14200             
14201             this.el=false;
14202             this.rendered = false;
14203             
14204             this.render(this.parent().getChildContainer(true), k);
14205         }
14206         
14207         if(Roo.isIOS && this.useNativeIOS){
14208             this.initIOSView();
14209             return;
14210         }
14211         
14212         /*
14213          * Touch Devices
14214          */
14215         
14216         if(Roo.isTouch && this.mobileTouchView){
14217             this.initTouchView();
14218             return;
14219         }
14220         
14221         if(this.tickable){
14222             this.initTickableEvents();
14223             return;
14224         }
14225         
14226         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
14227         
14228         if(this.hiddenName){
14229             
14230             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14231             
14232             this.hiddenField.dom.value =
14233                 this.hiddenValue !== undefined ? this.hiddenValue :
14234                 this.value !== undefined ? this.value : '';
14235
14236             // prevent input submission
14237             this.el.dom.removeAttribute('name');
14238             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14239              
14240              
14241         }
14242         //if(Roo.isGecko){
14243         //    this.el.dom.setAttribute('autocomplete', 'off');
14244         //}
14245         
14246         var cls = 'x-combo-list';
14247         
14248         //this.list = new Roo.Layer({
14249         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
14250         //});
14251         
14252         var _this = this;
14253         
14254         (function(){
14255             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14256             _this.list.setWidth(lw);
14257         }).defer(100);
14258         
14259         this.list.on('mouseover', this.onViewOver, this);
14260         this.list.on('mousemove', this.onViewMove, this);
14261         this.list.on('scroll', this.onViewScroll, this);
14262         
14263         /*
14264         this.list.swallowEvent('mousewheel');
14265         this.assetHeight = 0;
14266
14267         if(this.title){
14268             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
14269             this.assetHeight += this.header.getHeight();
14270         }
14271
14272         this.innerList = this.list.createChild({cls:cls+'-inner'});
14273         this.innerList.on('mouseover', this.onViewOver, this);
14274         this.innerList.on('mousemove', this.onViewMove, this);
14275         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14276         
14277         if(this.allowBlank && !this.pageSize && !this.disableClear){
14278             this.footer = this.list.createChild({cls:cls+'-ft'});
14279             this.pageTb = new Roo.Toolbar(this.footer);
14280            
14281         }
14282         if(this.pageSize){
14283             this.footer = this.list.createChild({cls:cls+'-ft'});
14284             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
14285                     {pageSize: this.pageSize});
14286             
14287         }
14288         
14289         if (this.pageTb && this.allowBlank && !this.disableClear) {
14290             var _this = this;
14291             this.pageTb.add(new Roo.Toolbar.Fill(), {
14292                 cls: 'x-btn-icon x-btn-clear',
14293                 text: '&#160;',
14294                 handler: function()
14295                 {
14296                     _this.collapse();
14297                     _this.clearValue();
14298                     _this.onSelect(false, -1);
14299                 }
14300             });
14301         }
14302         if (this.footer) {
14303             this.assetHeight += this.footer.getHeight();
14304         }
14305         */
14306             
14307         if(!this.tpl){
14308             this.tpl = Roo.bootstrap.version == 4 ?
14309                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
14310                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
14311         }
14312
14313         this.view = new Roo.View(this.list, this.tpl, {
14314             singleSelect:true, store: this.store, selectedClass: this.selectedClass
14315         });
14316         //this.view.wrapEl.setDisplayed(false);
14317         this.view.on('click', this.onViewClick, this);
14318         
14319         
14320         this.store.on('beforeload', this.onBeforeLoad, this);
14321         this.store.on('load', this.onLoad, this);
14322         this.store.on('loadexception', this.onLoadException, this);
14323         /*
14324         if(this.resizable){
14325             this.resizer = new Roo.Resizable(this.list,  {
14326                pinned:true, handles:'se'
14327             });
14328             this.resizer.on('resize', function(r, w, h){
14329                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
14330                 this.listWidth = w;
14331                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
14332                 this.restrictHeight();
14333             }, this);
14334             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
14335         }
14336         */
14337         if(!this.editable){
14338             this.editable = true;
14339             this.setEditable(false);
14340         }
14341         
14342         /*
14343         
14344         if (typeof(this.events.add.listeners) != 'undefined') {
14345             
14346             this.addicon = this.wrap.createChild(
14347                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
14348        
14349             this.addicon.on('click', function(e) {
14350                 this.fireEvent('add', this);
14351             }, this);
14352         }
14353         if (typeof(this.events.edit.listeners) != 'undefined') {
14354             
14355             this.editicon = this.wrap.createChild(
14356                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
14357             if (this.addicon) {
14358                 this.editicon.setStyle('margin-left', '40px');
14359             }
14360             this.editicon.on('click', function(e) {
14361                 
14362                 // we fire even  if inothing is selected..
14363                 this.fireEvent('edit', this, this.lastData );
14364                 
14365             }, this);
14366         }
14367         */
14368         
14369         this.keyNav = new Roo.KeyNav(this.inputEl(), {
14370             "up" : function(e){
14371                 this.inKeyMode = true;
14372                 this.selectPrev();
14373             },
14374
14375             "down" : function(e){
14376                 if(!this.isExpanded()){
14377                     this.onTriggerClick();
14378                 }else{
14379                     this.inKeyMode = true;
14380                     this.selectNext();
14381                 }
14382             },
14383
14384             "enter" : function(e){
14385 //                this.onViewClick();
14386                 //return true;
14387                 this.collapse();
14388                 
14389                 if(this.fireEvent("specialkey", this, e)){
14390                     this.onViewClick(false);
14391                 }
14392                 
14393                 return true;
14394             },
14395
14396             "esc" : function(e){
14397                 this.collapse();
14398             },
14399
14400             "tab" : function(e){
14401                 this.collapse();
14402                 
14403                 if(this.fireEvent("specialkey", this, e)){
14404                     this.onViewClick(false);
14405                 }
14406                 
14407                 return true;
14408             },
14409
14410             scope : this,
14411
14412             doRelay : function(foo, bar, hname){
14413                 if(hname == 'down' || this.scope.isExpanded()){
14414                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14415                 }
14416                 return true;
14417             },
14418
14419             forceKeyDown: true
14420         });
14421         
14422         
14423         this.queryDelay = Math.max(this.queryDelay || 10,
14424                 this.mode == 'local' ? 10 : 250);
14425         
14426         
14427         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14428         
14429         if(this.typeAhead){
14430             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14431         }
14432         if(this.editable !== false){
14433             this.inputEl().on("keyup", this.onKeyUp, this);
14434         }
14435         if(this.forceSelection){
14436             this.inputEl().on('blur', this.doForce, this);
14437         }
14438         
14439         if(this.multiple){
14440             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14441             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14442         }
14443     },
14444     
14445     initTickableEvents: function()
14446     {   
14447         this.createList();
14448         
14449         if(this.hiddenName){
14450             
14451             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14452             
14453             this.hiddenField.dom.value =
14454                 this.hiddenValue !== undefined ? this.hiddenValue :
14455                 this.value !== undefined ? this.value : '';
14456
14457             // prevent input submission
14458             this.el.dom.removeAttribute('name');
14459             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14460              
14461              
14462         }
14463         
14464 //        this.list = this.el.select('ul.dropdown-menu',true).first();
14465         
14466         this.choices = this.el.select('ul.roo-select2-choices', true).first();
14467         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14468         if(this.triggerList){
14469             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14470         }
14471          
14472         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14473         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14474         
14475         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14476         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14477         
14478         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14479         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14480         
14481         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14482         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14483         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14484         
14485         this.okBtn.hide();
14486         this.cancelBtn.hide();
14487         
14488         var _this = this;
14489         
14490         (function(){
14491             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14492             _this.list.setWidth(lw);
14493         }).defer(100);
14494         
14495         this.list.on('mouseover', this.onViewOver, this);
14496         this.list.on('mousemove', this.onViewMove, this);
14497         
14498         this.list.on('scroll', this.onViewScroll, this);
14499         
14500         if(!this.tpl){
14501             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
14502                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14503         }
14504
14505         this.view = new Roo.View(this.list, this.tpl, {
14506             singleSelect:true,
14507             tickable:true,
14508             parent:this,
14509             store: this.store,
14510             selectedClass: this.selectedClass
14511         });
14512         
14513         //this.view.wrapEl.setDisplayed(false);
14514         this.view.on('click', this.onViewClick, this);
14515         
14516         
14517         
14518         this.store.on('beforeload', this.onBeforeLoad, this);
14519         this.store.on('load', this.onLoad, this);
14520         this.store.on('loadexception', this.onLoadException, this);
14521         
14522         if(this.editable){
14523             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14524                 "up" : function(e){
14525                     this.inKeyMode = true;
14526                     this.selectPrev();
14527                 },
14528
14529                 "down" : function(e){
14530                     this.inKeyMode = true;
14531                     this.selectNext();
14532                 },
14533
14534                 "enter" : function(e){
14535                     if(this.fireEvent("specialkey", this, e)){
14536                         this.onViewClick(false);
14537                     }
14538                     
14539                     return true;
14540                 },
14541
14542                 "esc" : function(e){
14543                     this.onTickableFooterButtonClick(e, false, false);
14544                 },
14545
14546                 "tab" : function(e){
14547                     this.fireEvent("specialkey", this, e);
14548                     
14549                     this.onTickableFooterButtonClick(e, false, false);
14550                     
14551                     return true;
14552                 },
14553
14554                 scope : this,
14555
14556                 doRelay : function(e, fn, key){
14557                     if(this.scope.isExpanded()){
14558                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14559                     }
14560                     return true;
14561                 },
14562
14563                 forceKeyDown: true
14564             });
14565         }
14566         
14567         this.queryDelay = Math.max(this.queryDelay || 10,
14568                 this.mode == 'local' ? 10 : 250);
14569         
14570         
14571         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14572         
14573         if(this.typeAhead){
14574             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14575         }
14576         
14577         if(this.editable !== false){
14578             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14579         }
14580         
14581         this.indicator = this.indicatorEl();
14582         
14583         if(this.indicator){
14584             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14585             this.indicator.hide();
14586         }
14587         
14588     },
14589
14590     onDestroy : function(){
14591         if(this.view){
14592             this.view.setStore(null);
14593             this.view.el.removeAllListeners();
14594             this.view.el.remove();
14595             this.view.purgeListeners();
14596         }
14597         if(this.list){
14598             this.list.dom.innerHTML  = '';
14599         }
14600         
14601         if(this.store){
14602             this.store.un('beforeload', this.onBeforeLoad, this);
14603             this.store.un('load', this.onLoad, this);
14604             this.store.un('loadexception', this.onLoadException, this);
14605         }
14606         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14607     },
14608
14609     // private
14610     fireKey : function(e){
14611         if(e.isNavKeyPress() && !this.list.isVisible()){
14612             this.fireEvent("specialkey", this, e);
14613         }
14614     },
14615
14616     // private
14617     onResize: function(w, h){
14618 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14619 //        
14620 //        if(typeof w != 'number'){
14621 //            // we do not handle it!?!?
14622 //            return;
14623 //        }
14624 //        var tw = this.trigger.getWidth();
14625 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14626 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14627 //        var x = w - tw;
14628 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14629 //            
14630 //        //this.trigger.setStyle('left', x+'px');
14631 //        
14632 //        if(this.list && this.listWidth === undefined){
14633 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14634 //            this.list.setWidth(lw);
14635 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14636 //        }
14637         
14638     
14639         
14640     },
14641
14642     /**
14643      * Allow or prevent the user from directly editing the field text.  If false is passed,
14644      * the user will only be able to select from the items defined in the dropdown list.  This method
14645      * is the runtime equivalent of setting the 'editable' config option at config time.
14646      * @param {Boolean} value True to allow the user to directly edit the field text
14647      */
14648     setEditable : function(value){
14649         if(value == this.editable){
14650             return;
14651         }
14652         this.editable = value;
14653         if(!value){
14654             this.inputEl().dom.setAttribute('readOnly', true);
14655             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14656             this.inputEl().addClass('x-combo-noedit');
14657         }else{
14658             this.inputEl().dom.setAttribute('readOnly', false);
14659             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14660             this.inputEl().removeClass('x-combo-noedit');
14661         }
14662     },
14663
14664     // private
14665     
14666     onBeforeLoad : function(combo,opts){
14667         if(!this.hasFocus){
14668             return;
14669         }
14670          if (!opts.add) {
14671             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14672          }
14673         this.restrictHeight();
14674         this.selectedIndex = -1;
14675     },
14676
14677     // private
14678     onLoad : function(){
14679         
14680         this.hasQuery = false;
14681         
14682         if(!this.hasFocus){
14683             return;
14684         }
14685         
14686         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14687             this.loading.hide();
14688         }
14689         
14690         if(this.store.getCount() > 0){
14691             
14692             this.expand();
14693             this.restrictHeight();
14694             if(this.lastQuery == this.allQuery){
14695                 if(this.editable && !this.tickable){
14696                     this.inputEl().dom.select();
14697                 }
14698                 
14699                 if(
14700                     !this.selectByValue(this.value, true) &&
14701                     this.autoFocus && 
14702                     (
14703                         !this.store.lastOptions ||
14704                         typeof(this.store.lastOptions.add) == 'undefined' || 
14705                         this.store.lastOptions.add != true
14706                     )
14707                 ){
14708                     this.select(0, true);
14709                 }
14710             }else{
14711                 if(this.autoFocus){
14712                     this.selectNext();
14713                 }
14714                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14715                     this.taTask.delay(this.typeAheadDelay);
14716                 }
14717             }
14718         }else{
14719             this.onEmptyResults();
14720         }
14721         
14722         //this.el.focus();
14723     },
14724     // private
14725     onLoadException : function()
14726     {
14727         this.hasQuery = false;
14728         
14729         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14730             this.loading.hide();
14731         }
14732         
14733         if(this.tickable && this.editable){
14734             return;
14735         }
14736         
14737         this.collapse();
14738         // only causes errors at present
14739         //Roo.log(this.store.reader.jsonData);
14740         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14741             // fixme
14742             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14743         //}
14744         
14745         
14746     },
14747     // private
14748     onTypeAhead : function(){
14749         if(this.store.getCount() > 0){
14750             var r = this.store.getAt(0);
14751             var newValue = r.data[this.displayField];
14752             var len = newValue.length;
14753             var selStart = this.getRawValue().length;
14754             
14755             if(selStart != len){
14756                 this.setRawValue(newValue);
14757                 this.selectText(selStart, newValue.length);
14758             }
14759         }
14760     },
14761
14762     // private
14763     onSelect : function(record, index){
14764         
14765         if(this.fireEvent('beforeselect', this, record, index) !== false){
14766         
14767             this.setFromData(index > -1 ? record.data : false);
14768             
14769             this.collapse();
14770             this.fireEvent('select', this, record, index);
14771         }
14772     },
14773
14774     /**
14775      * Returns the currently selected field value or empty string if no value is set.
14776      * @return {String} value The selected value
14777      */
14778     getValue : function()
14779     {
14780         if(Roo.isIOS && this.useNativeIOS){
14781             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14782         }
14783         
14784         if(this.multiple){
14785             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14786         }
14787         
14788         if(this.valueField){
14789             return typeof this.value != 'undefined' ? this.value : '';
14790         }else{
14791             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14792         }
14793     },
14794     
14795     getRawValue : function()
14796     {
14797         if(Roo.isIOS && this.useNativeIOS){
14798             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14799         }
14800         
14801         var v = this.inputEl().getValue();
14802         
14803         return v;
14804     },
14805
14806     /**
14807      * Clears any text/value currently set in the field
14808      */
14809     clearValue : function(){
14810         
14811         if(this.hiddenField){
14812             this.hiddenField.dom.value = '';
14813         }
14814         this.value = '';
14815         this.setRawValue('');
14816         this.lastSelectionText = '';
14817         this.lastData = false;
14818         
14819         var close = this.closeTriggerEl();
14820         
14821         if(close){
14822             close.hide();
14823         }
14824         
14825         this.validate();
14826         
14827     },
14828
14829     /**
14830      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14831      * will be displayed in the field.  If the value does not match the data value of an existing item,
14832      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14833      * Otherwise the field will be blank (although the value will still be set).
14834      * @param {String} value The value to match
14835      */
14836     setValue : function(v)
14837     {
14838         if(Roo.isIOS && this.useNativeIOS){
14839             this.setIOSValue(v);
14840             return;
14841         }
14842         
14843         if(this.multiple){
14844             this.syncValue();
14845             return;
14846         }
14847         
14848         var text = v;
14849         if(this.valueField){
14850             var r = this.findRecord(this.valueField, v);
14851             if(r){
14852                 text = r.data[this.displayField];
14853             }else if(this.valueNotFoundText !== undefined){
14854                 text = this.valueNotFoundText;
14855             }
14856         }
14857         this.lastSelectionText = text;
14858         if(this.hiddenField){
14859             this.hiddenField.dom.value = v;
14860         }
14861         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14862         this.value = v;
14863         
14864         var close = this.closeTriggerEl();
14865         
14866         if(close){
14867             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14868         }
14869         
14870         this.validate();
14871     },
14872     /**
14873      * @property {Object} the last set data for the element
14874      */
14875     
14876     lastData : false,
14877     /**
14878      * Sets the value of the field based on a object which is related to the record format for the store.
14879      * @param {Object} value the value to set as. or false on reset?
14880      */
14881     setFromData : function(o){
14882         
14883         if(this.multiple){
14884             this.addItem(o);
14885             return;
14886         }
14887             
14888         var dv = ''; // display value
14889         var vv = ''; // value value..
14890         this.lastData = o;
14891         if (this.displayField) {
14892             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14893         } else {
14894             // this is an error condition!!!
14895             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14896         }
14897         
14898         if(this.valueField){
14899             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14900         }
14901         
14902         var close = this.closeTriggerEl();
14903         
14904         if(close){
14905             if(dv.length || vv * 1 > 0){
14906                 close.show() ;
14907                 this.blockFocus=true;
14908             } else {
14909                 close.hide();
14910             }             
14911         }
14912         
14913         if(this.hiddenField){
14914             this.hiddenField.dom.value = vv;
14915             
14916             this.lastSelectionText = dv;
14917             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14918             this.value = vv;
14919             return;
14920         }
14921         // no hidden field.. - we store the value in 'value', but still display
14922         // display field!!!!
14923         this.lastSelectionText = dv;
14924         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14925         this.value = vv;
14926         
14927         
14928         
14929     },
14930     // private
14931     reset : function(){
14932         // overridden so that last data is reset..
14933         
14934         if(this.multiple){
14935             this.clearItem();
14936             return;
14937         }
14938         
14939         this.setValue(this.originalValue);
14940         //this.clearInvalid();
14941         this.lastData = false;
14942         if (this.view) {
14943             this.view.clearSelections();
14944         }
14945         
14946         this.validate();
14947     },
14948     // private
14949     findRecord : function(prop, value){
14950         var record;
14951         if(this.store.getCount() > 0){
14952             this.store.each(function(r){
14953                 if(r.data[prop] == value){
14954                     record = r;
14955                     return false;
14956                 }
14957                 return true;
14958             });
14959         }
14960         return record;
14961     },
14962     
14963     getName: function()
14964     {
14965         // returns hidden if it's set..
14966         if (!this.rendered) {return ''};
14967         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14968         
14969     },
14970     // private
14971     onViewMove : function(e, t){
14972         this.inKeyMode = false;
14973     },
14974
14975     // private
14976     onViewOver : function(e, t){
14977         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14978             return;
14979         }
14980         var item = this.view.findItemFromChild(t);
14981         
14982         if(item){
14983             var index = this.view.indexOf(item);
14984             this.select(index, false);
14985         }
14986     },
14987
14988     // private
14989     onViewClick : function(view, doFocus, el, e)
14990     {
14991         var index = this.view.getSelectedIndexes()[0];
14992         
14993         var r = this.store.getAt(index);
14994         
14995         if(this.tickable){
14996             
14997             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14998                 return;
14999             }
15000             
15001             var rm = false;
15002             var _this = this;
15003             
15004             Roo.each(this.tickItems, function(v,k){
15005                 
15006                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
15007                     Roo.log(v);
15008                     _this.tickItems.splice(k, 1);
15009                     
15010                     if(typeof(e) == 'undefined' && view == false){
15011                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
15012                     }
15013                     
15014                     rm = true;
15015                     return;
15016                 }
15017             });
15018             
15019             if(rm){
15020                 return;
15021             }
15022             
15023             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
15024                 this.tickItems.push(r.data);
15025             }
15026             
15027             if(typeof(e) == 'undefined' && view == false){
15028                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
15029             }
15030                     
15031             return;
15032         }
15033         
15034         if(r){
15035             this.onSelect(r, index);
15036         }
15037         if(doFocus !== false && !this.blockFocus){
15038             this.inputEl().focus();
15039         }
15040     },
15041
15042     // private
15043     restrictHeight : function(){
15044         //this.innerList.dom.style.height = '';
15045         //var inner = this.innerList.dom;
15046         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
15047         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
15048         //this.list.beginUpdate();
15049         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
15050         this.list.alignTo(this.inputEl(), this.listAlign);
15051         this.list.alignTo(this.inputEl(), this.listAlign);
15052         //this.list.endUpdate();
15053     },
15054
15055     // private
15056     onEmptyResults : function(){
15057         
15058         if(this.tickable && this.editable){
15059             this.hasFocus = false;
15060             this.restrictHeight();
15061             return;
15062         }
15063         
15064         this.collapse();
15065     },
15066
15067     /**
15068      * Returns true if the dropdown list is expanded, else false.
15069      */
15070     isExpanded : function(){
15071         return this.list.isVisible();
15072     },
15073
15074     /**
15075      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
15076      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15077      * @param {String} value The data value of the item to select
15078      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15079      * selected item if it is not currently in view (defaults to true)
15080      * @return {Boolean} True if the value matched an item in the list, else false
15081      */
15082     selectByValue : function(v, scrollIntoView){
15083         if(v !== undefined && v !== null){
15084             var r = this.findRecord(this.valueField || this.displayField, v);
15085             if(r){
15086                 this.select(this.store.indexOf(r), scrollIntoView);
15087                 return true;
15088             }
15089         }
15090         return false;
15091     },
15092
15093     /**
15094      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
15095      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15096      * @param {Number} index The zero-based index of the list item to select
15097      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15098      * selected item if it is not currently in view (defaults to true)
15099      */
15100     select : function(index, scrollIntoView){
15101         this.selectedIndex = index;
15102         this.view.select(index);
15103         if(scrollIntoView !== false){
15104             var el = this.view.getNode(index);
15105             /*
15106              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
15107              */
15108             if(el){
15109                 this.list.scrollChildIntoView(el, false);
15110             }
15111         }
15112     },
15113
15114     // private
15115     selectNext : function(){
15116         var ct = this.store.getCount();
15117         if(ct > 0){
15118             if(this.selectedIndex == -1){
15119                 this.select(0);
15120             }else if(this.selectedIndex < ct-1){
15121                 this.select(this.selectedIndex+1);
15122             }
15123         }
15124     },
15125
15126     // private
15127     selectPrev : function(){
15128         var ct = this.store.getCount();
15129         if(ct > 0){
15130             if(this.selectedIndex == -1){
15131                 this.select(0);
15132             }else if(this.selectedIndex != 0){
15133                 this.select(this.selectedIndex-1);
15134             }
15135         }
15136     },
15137
15138     // private
15139     onKeyUp : function(e){
15140         if(this.editable !== false && !e.isSpecialKey()){
15141             this.lastKey = e.getKey();
15142             this.dqTask.delay(this.queryDelay);
15143         }
15144     },
15145
15146     // private
15147     validateBlur : function(){
15148         return !this.list || !this.list.isVisible();   
15149     },
15150
15151     // private
15152     initQuery : function(){
15153         
15154         var v = this.getRawValue();
15155         
15156         if(this.tickable && this.editable){
15157             v = this.tickableInputEl().getValue();
15158         }
15159         
15160         this.doQuery(v);
15161     },
15162
15163     // private
15164     doForce : function(){
15165         if(this.inputEl().dom.value.length > 0){
15166             this.inputEl().dom.value =
15167                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
15168              
15169         }
15170     },
15171
15172     /**
15173      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
15174      * query allowing the query action to be canceled if needed.
15175      * @param {String} query The SQL query to execute
15176      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
15177      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
15178      * saved in the current store (defaults to false)
15179      */
15180     doQuery : function(q, forceAll){
15181         
15182         if(q === undefined || q === null){
15183             q = '';
15184         }
15185         var qe = {
15186             query: q,
15187             forceAll: forceAll,
15188             combo: this,
15189             cancel:false
15190         };
15191         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
15192             return false;
15193         }
15194         q = qe.query;
15195         
15196         forceAll = qe.forceAll;
15197         if(forceAll === true || (q.length >= this.minChars)){
15198             
15199             this.hasQuery = true;
15200             
15201             if(this.lastQuery != q || this.alwaysQuery){
15202                 this.lastQuery = q;
15203                 if(this.mode == 'local'){
15204                     this.selectedIndex = -1;
15205                     if(forceAll){
15206                         this.store.clearFilter();
15207                     }else{
15208                         
15209                         if(this.specialFilter){
15210                             this.fireEvent('specialfilter', this);
15211                             this.onLoad();
15212                             return;
15213                         }
15214                         
15215                         this.store.filter(this.displayField, q);
15216                     }
15217                     
15218                     this.store.fireEvent("datachanged", this.store);
15219                     
15220                     this.onLoad();
15221                     
15222                     
15223                 }else{
15224                     
15225                     this.store.baseParams[this.queryParam] = q;
15226                     
15227                     var options = {params : this.getParams(q)};
15228                     
15229                     if(this.loadNext){
15230                         options.add = true;
15231                         options.params.start = this.page * this.pageSize;
15232                     }
15233                     
15234                     this.store.load(options);
15235                     
15236                     /*
15237                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
15238                      *  we should expand the list on onLoad
15239                      *  so command out it
15240                      */
15241 //                    this.expand();
15242                 }
15243             }else{
15244                 this.selectedIndex = -1;
15245                 this.onLoad();   
15246             }
15247         }
15248         
15249         this.loadNext = false;
15250     },
15251     
15252     // private
15253     getParams : function(q){
15254         var p = {};
15255         //p[this.queryParam] = q;
15256         
15257         if(this.pageSize){
15258             p.start = 0;
15259             p.limit = this.pageSize;
15260         }
15261         return p;
15262     },
15263
15264     /**
15265      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
15266      */
15267     collapse : function(){
15268         if(!this.isExpanded()){
15269             return;
15270         }
15271         
15272         this.list.hide();
15273         
15274         this.hasFocus = false;
15275         
15276         if(this.tickable){
15277             this.okBtn.hide();
15278             this.cancelBtn.hide();
15279             this.trigger.show();
15280             
15281             if(this.editable){
15282                 this.tickableInputEl().dom.value = '';
15283                 this.tickableInputEl().blur();
15284             }
15285             
15286         }
15287         
15288         Roo.get(document).un('mousedown', this.collapseIf, this);
15289         Roo.get(document).un('mousewheel', this.collapseIf, this);
15290         if (!this.editable) {
15291             Roo.get(document).un('keydown', this.listKeyPress, this);
15292         }
15293         this.fireEvent('collapse', this);
15294         
15295         this.validate();
15296     },
15297
15298     // private
15299     collapseIf : function(e){
15300         var in_combo  = e.within(this.el);
15301         var in_list =  e.within(this.list);
15302         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
15303         
15304         if (in_combo || in_list || is_list) {
15305             //e.stopPropagation();
15306             return;
15307         }
15308         
15309         if(this.tickable){
15310             this.onTickableFooterButtonClick(e, false, false);
15311         }
15312
15313         this.collapse();
15314         
15315     },
15316
15317     /**
15318      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
15319      */
15320     expand : function(){
15321        
15322         if(this.isExpanded() || !this.hasFocus){
15323             return;
15324         }
15325         
15326         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
15327         this.list.setWidth(lw);
15328         
15329         Roo.log('expand');
15330         
15331         this.list.show();
15332         
15333         this.restrictHeight();
15334         
15335         if(this.tickable){
15336             
15337             this.tickItems = Roo.apply([], this.item);
15338             
15339             this.okBtn.show();
15340             this.cancelBtn.show();
15341             this.trigger.hide();
15342             
15343             if(this.editable){
15344                 this.tickableInputEl().focus();
15345             }
15346             
15347         }
15348         
15349         Roo.get(document).on('mousedown', this.collapseIf, this);
15350         Roo.get(document).on('mousewheel', this.collapseIf, this);
15351         if (!this.editable) {
15352             Roo.get(document).on('keydown', this.listKeyPress, this);
15353         }
15354         
15355         this.fireEvent('expand', this);
15356     },
15357
15358     // private
15359     // Implements the default empty TriggerField.onTriggerClick function
15360     onTriggerClick : function(e)
15361     {
15362         Roo.log('trigger click');
15363         
15364         if(this.disabled || !this.triggerList){
15365             return;
15366         }
15367         
15368         this.page = 0;
15369         this.loadNext = false;
15370         
15371         if(this.isExpanded()){
15372             this.collapse();
15373             if (!this.blockFocus) {
15374                 this.inputEl().focus();
15375             }
15376             
15377         }else {
15378             this.hasFocus = true;
15379             if(this.triggerAction == 'all') {
15380                 this.doQuery(this.allQuery, true);
15381             } else {
15382                 this.doQuery(this.getRawValue());
15383             }
15384             if (!this.blockFocus) {
15385                 this.inputEl().focus();
15386             }
15387         }
15388     },
15389     
15390     onTickableTriggerClick : function(e)
15391     {
15392         if(this.disabled){
15393             return;
15394         }
15395         
15396         this.page = 0;
15397         this.loadNext = false;
15398         this.hasFocus = true;
15399         
15400         if(this.triggerAction == 'all') {
15401             this.doQuery(this.allQuery, true);
15402         } else {
15403             this.doQuery(this.getRawValue());
15404         }
15405     },
15406     
15407     onSearchFieldClick : function(e)
15408     {
15409         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
15410             this.onTickableFooterButtonClick(e, false, false);
15411             return;
15412         }
15413         
15414         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15415             return;
15416         }
15417         
15418         this.page = 0;
15419         this.loadNext = false;
15420         this.hasFocus = true;
15421         
15422         if(this.triggerAction == 'all') {
15423             this.doQuery(this.allQuery, true);
15424         } else {
15425             this.doQuery(this.getRawValue());
15426         }
15427     },
15428     
15429     listKeyPress : function(e)
15430     {
15431         //Roo.log('listkeypress');
15432         // scroll to first matching element based on key pres..
15433         if (e.isSpecialKey()) {
15434             return false;
15435         }
15436         var k = String.fromCharCode(e.getKey()).toUpperCase();
15437         //Roo.log(k);
15438         var match  = false;
15439         var csel = this.view.getSelectedNodes();
15440         var cselitem = false;
15441         if (csel.length) {
15442             var ix = this.view.indexOf(csel[0]);
15443             cselitem  = this.store.getAt(ix);
15444             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15445                 cselitem = false;
15446             }
15447             
15448         }
15449         
15450         this.store.each(function(v) { 
15451             if (cselitem) {
15452                 // start at existing selection.
15453                 if (cselitem.id == v.id) {
15454                     cselitem = false;
15455                 }
15456                 return true;
15457             }
15458                 
15459             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15460                 match = this.store.indexOf(v);
15461                 return false;
15462             }
15463             return true;
15464         }, this);
15465         
15466         if (match === false) {
15467             return true; // no more action?
15468         }
15469         // scroll to?
15470         this.view.select(match);
15471         var sn = Roo.get(this.view.getSelectedNodes()[0]);
15472         sn.scrollIntoView(sn.dom.parentNode, false);
15473     },
15474     
15475     onViewScroll : function(e, t){
15476         
15477         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){
15478             return;
15479         }
15480         
15481         this.hasQuery = true;
15482         
15483         this.loading = this.list.select('.loading', true).first();
15484         
15485         if(this.loading === null){
15486             this.list.createChild({
15487                 tag: 'div',
15488                 cls: 'loading roo-select2-more-results roo-select2-active',
15489                 html: 'Loading more results...'
15490             });
15491             
15492             this.loading = this.list.select('.loading', true).first();
15493             
15494             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15495             
15496             this.loading.hide();
15497         }
15498         
15499         this.loading.show();
15500         
15501         var _combo = this;
15502         
15503         this.page++;
15504         this.loadNext = true;
15505         
15506         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15507         
15508         return;
15509     },
15510     
15511     addItem : function(o)
15512     {   
15513         var dv = ''; // display value
15514         
15515         if (this.displayField) {
15516             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15517         } else {
15518             // this is an error condition!!!
15519             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15520         }
15521         
15522         if(!dv.length){
15523             return;
15524         }
15525         
15526         var choice = this.choices.createChild({
15527             tag: 'li',
15528             cls: 'roo-select2-search-choice',
15529             cn: [
15530                 {
15531                     tag: 'div',
15532                     html: dv
15533                 },
15534                 {
15535                     tag: 'a',
15536                     href: '#',
15537                     cls: 'roo-select2-search-choice-close fa fa-times',
15538                     tabindex: '-1'
15539                 }
15540             ]
15541             
15542         }, this.searchField);
15543         
15544         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15545         
15546         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15547         
15548         this.item.push(o);
15549         
15550         this.lastData = o;
15551         
15552         this.syncValue();
15553         
15554         this.inputEl().dom.value = '';
15555         
15556         this.validate();
15557     },
15558     
15559     onRemoveItem : function(e, _self, o)
15560     {
15561         e.preventDefault();
15562         
15563         this.lastItem = Roo.apply([], this.item);
15564         
15565         var index = this.item.indexOf(o.data) * 1;
15566         
15567         if( index < 0){
15568             Roo.log('not this item?!');
15569             return;
15570         }
15571         
15572         this.item.splice(index, 1);
15573         o.item.remove();
15574         
15575         this.syncValue();
15576         
15577         this.fireEvent('remove', this, e);
15578         
15579         this.validate();
15580         
15581     },
15582     
15583     syncValue : function()
15584     {
15585         if(!this.item.length){
15586             this.clearValue();
15587             return;
15588         }
15589             
15590         var value = [];
15591         var _this = this;
15592         Roo.each(this.item, function(i){
15593             if(_this.valueField){
15594                 value.push(i[_this.valueField]);
15595                 return;
15596             }
15597
15598             value.push(i);
15599         });
15600
15601         this.value = value.join(',');
15602
15603         if(this.hiddenField){
15604             this.hiddenField.dom.value = this.value;
15605         }
15606         
15607         this.store.fireEvent("datachanged", this.store);
15608         
15609         this.validate();
15610     },
15611     
15612     clearItem : function()
15613     {
15614         if(!this.multiple){
15615             return;
15616         }
15617         
15618         this.item = [];
15619         
15620         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15621            c.remove();
15622         });
15623         
15624         this.syncValue();
15625         
15626         this.validate();
15627         
15628         if(this.tickable && !Roo.isTouch){
15629             this.view.refresh();
15630         }
15631     },
15632     
15633     inputEl: function ()
15634     {
15635         if(Roo.isIOS && this.useNativeIOS){
15636             return this.el.select('select.roo-ios-select', true).first();
15637         }
15638         
15639         if(Roo.isTouch && this.mobileTouchView){
15640             return this.el.select('input.form-control',true).first();
15641         }
15642         
15643         if(this.tickable){
15644             return this.searchField;
15645         }
15646         
15647         return this.el.select('input.form-control',true).first();
15648     },
15649     
15650     onTickableFooterButtonClick : function(e, btn, el)
15651     {
15652         e.preventDefault();
15653         
15654         this.lastItem = Roo.apply([], this.item);
15655         
15656         if(btn && btn.name == 'cancel'){
15657             this.tickItems = Roo.apply([], this.item);
15658             this.collapse();
15659             return;
15660         }
15661         
15662         this.clearItem();
15663         
15664         var _this = this;
15665         
15666         Roo.each(this.tickItems, function(o){
15667             _this.addItem(o);
15668         });
15669         
15670         this.collapse();
15671         
15672     },
15673     
15674     validate : function()
15675     {
15676         if(this.getVisibilityEl().hasClass('hidden')){
15677             return true;
15678         }
15679         
15680         var v = this.getRawValue();
15681         
15682         if(this.multiple){
15683             v = this.getValue();
15684         }
15685         
15686         if(this.disabled || this.allowBlank || v.length){
15687             this.markValid();
15688             return true;
15689         }
15690         
15691         this.markInvalid();
15692         return false;
15693     },
15694     
15695     tickableInputEl : function()
15696     {
15697         if(!this.tickable || !this.editable){
15698             return this.inputEl();
15699         }
15700         
15701         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15702     },
15703     
15704     
15705     getAutoCreateTouchView : function()
15706     {
15707         var id = Roo.id();
15708         
15709         var cfg = {
15710             cls: 'form-group' //input-group
15711         };
15712         
15713         var input =  {
15714             tag: 'input',
15715             id : id,
15716             type : this.inputType,
15717             cls : 'form-control x-combo-noedit',
15718             autocomplete: 'new-password',
15719             placeholder : this.placeholder || '',
15720             readonly : true
15721         };
15722         
15723         if (this.name) {
15724             input.name = this.name;
15725         }
15726         
15727         if (this.size) {
15728             input.cls += ' input-' + this.size;
15729         }
15730         
15731         if (this.disabled) {
15732             input.disabled = true;
15733         }
15734         
15735         var inputblock = {
15736             cls : '',
15737             cn : [
15738                 input
15739             ]
15740         };
15741         
15742         if(this.before){
15743             inputblock.cls += ' input-group';
15744             
15745             inputblock.cn.unshift({
15746                 tag :'span',
15747                 cls : 'input-group-addon input-group-prepend input-group-text',
15748                 html : this.before
15749             });
15750         }
15751         
15752         if(this.removable && !this.multiple){
15753             inputblock.cls += ' roo-removable';
15754             
15755             inputblock.cn.push({
15756                 tag: 'button',
15757                 html : 'x',
15758                 cls : 'roo-combo-removable-btn close'
15759             });
15760         }
15761
15762         if(this.hasFeedback && !this.allowBlank){
15763             
15764             inputblock.cls += ' has-feedback';
15765             
15766             inputblock.cn.push({
15767                 tag: 'span',
15768                 cls: 'glyphicon form-control-feedback'
15769             });
15770             
15771         }
15772         
15773         if (this.after) {
15774             
15775             inputblock.cls += (this.before) ? '' : ' input-group';
15776             
15777             inputblock.cn.push({
15778                 tag :'span',
15779                 cls : 'input-group-addon input-group-append input-group-text',
15780                 html : this.after
15781             });
15782         }
15783
15784         
15785         var ibwrap = inputblock;
15786         
15787         if(this.multiple){
15788             ibwrap = {
15789                 tag: 'ul',
15790                 cls: 'roo-select2-choices',
15791                 cn:[
15792                     {
15793                         tag: 'li',
15794                         cls: 'roo-select2-search-field',
15795                         cn: [
15796
15797                             inputblock
15798                         ]
15799                     }
15800                 ]
15801             };
15802         
15803             
15804         }
15805         
15806         var combobox = {
15807             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15808             cn: [
15809                 {
15810                     tag: 'input',
15811                     type : 'hidden',
15812                     cls: 'form-hidden-field'
15813                 },
15814                 ibwrap
15815             ]
15816         };
15817         
15818         if(!this.multiple && this.showToggleBtn){
15819             
15820             var caret = {
15821                 cls: 'caret'
15822             };
15823             
15824             if (this.caret != false) {
15825                 caret = {
15826                      tag: 'i',
15827                      cls: 'fa fa-' + this.caret
15828                 };
15829                 
15830             }
15831             
15832             combobox.cn.push({
15833                 tag :'span',
15834                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15835                 cn : [
15836                     Roo.bootstrap.version == 3 ? caret : '',
15837                     {
15838                         tag: 'span',
15839                         cls: 'combobox-clear',
15840                         cn  : [
15841                             {
15842                                 tag : 'i',
15843                                 cls: 'icon-remove'
15844                             }
15845                         ]
15846                     }
15847                 ]
15848
15849             })
15850         }
15851         
15852         if(this.multiple){
15853             combobox.cls += ' roo-select2-container-multi';
15854         }
15855         
15856         var align = this.labelAlign || this.parentLabelAlign();
15857         
15858         if (align ==='left' && this.fieldLabel.length) {
15859
15860             cfg.cn = [
15861                 {
15862                    tag : 'i',
15863                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15864                    tooltip : 'This field is required'
15865                 },
15866                 {
15867                     tag: 'label',
15868                     cls : 'control-label col-form-label',
15869                     html : this.fieldLabel
15870
15871                 },
15872                 {
15873                     cls : '', 
15874                     cn: [
15875                         combobox
15876                     ]
15877                 }
15878             ];
15879             
15880             var labelCfg = cfg.cn[1];
15881             var contentCfg = cfg.cn[2];
15882             
15883
15884             if(this.indicatorpos == 'right'){
15885                 cfg.cn = [
15886                     {
15887                         tag: 'label',
15888                         'for' :  id,
15889                         cls : 'control-label col-form-label',
15890                         cn : [
15891                             {
15892                                 tag : 'span',
15893                                 html : this.fieldLabel
15894                             },
15895                             {
15896                                 tag : 'i',
15897                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15898                                 tooltip : 'This field is required'
15899                             }
15900                         ]
15901                     },
15902                     {
15903                         cls : "",
15904                         cn: [
15905                             combobox
15906                         ]
15907                     }
15908
15909                 ];
15910                 
15911                 labelCfg = cfg.cn[0];
15912                 contentCfg = cfg.cn[1];
15913             }
15914             
15915            
15916             
15917             if(this.labelWidth > 12){
15918                 labelCfg.style = "width: " + this.labelWidth + 'px';
15919             }
15920             
15921             if(this.labelWidth < 13 && this.labelmd == 0){
15922                 this.labelmd = this.labelWidth;
15923             }
15924             
15925             if(this.labellg > 0){
15926                 labelCfg.cls += ' col-lg-' + this.labellg;
15927                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15928             }
15929             
15930             if(this.labelmd > 0){
15931                 labelCfg.cls += ' col-md-' + this.labelmd;
15932                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15933             }
15934             
15935             if(this.labelsm > 0){
15936                 labelCfg.cls += ' col-sm-' + this.labelsm;
15937                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15938             }
15939             
15940             if(this.labelxs > 0){
15941                 labelCfg.cls += ' col-xs-' + this.labelxs;
15942                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15943             }
15944                 
15945                 
15946         } else if ( this.fieldLabel.length) {
15947             cfg.cn = [
15948                 {
15949                    tag : 'i',
15950                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15951                    tooltip : 'This field is required'
15952                 },
15953                 {
15954                     tag: 'label',
15955                     cls : 'control-label',
15956                     html : this.fieldLabel
15957
15958                 },
15959                 {
15960                     cls : '', 
15961                     cn: [
15962                         combobox
15963                     ]
15964                 }
15965             ];
15966             
15967             if(this.indicatorpos == 'right'){
15968                 cfg.cn = [
15969                     {
15970                         tag: 'label',
15971                         cls : 'control-label',
15972                         html : this.fieldLabel,
15973                         cn : [
15974                             {
15975                                tag : 'i',
15976                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15977                                tooltip : 'This field is required'
15978                             }
15979                         ]
15980                     },
15981                     {
15982                         cls : '', 
15983                         cn: [
15984                             combobox
15985                         ]
15986                     }
15987                 ];
15988             }
15989         } else {
15990             cfg.cn = combobox;    
15991         }
15992         
15993         
15994         var settings = this;
15995         
15996         ['xs','sm','md','lg'].map(function(size){
15997             if (settings[size]) {
15998                 cfg.cls += ' col-' + size + '-' + settings[size];
15999             }
16000         });
16001         
16002         return cfg;
16003     },
16004     
16005     initTouchView : function()
16006     {
16007         this.renderTouchView();
16008         
16009         this.touchViewEl.on('scroll', function(){
16010             this.el.dom.scrollTop = 0;
16011         }, this);
16012         
16013         this.originalValue = this.getValue();
16014         
16015         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
16016         
16017         this.inputEl().on("click", this.showTouchView, this);
16018         if (this.triggerEl) {
16019             this.triggerEl.on("click", this.showTouchView, this);
16020         }
16021         
16022         
16023         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
16024         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
16025         
16026         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
16027         
16028         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
16029         this.store.on('load', this.onTouchViewLoad, this);
16030         this.store.on('loadexception', this.onTouchViewLoadException, this);
16031         
16032         if(this.hiddenName){
16033             
16034             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16035             
16036             this.hiddenField.dom.value =
16037                 this.hiddenValue !== undefined ? this.hiddenValue :
16038                 this.value !== undefined ? this.value : '';
16039         
16040             this.el.dom.removeAttribute('name');
16041             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16042         }
16043         
16044         if(this.multiple){
16045             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16046             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16047         }
16048         
16049         if(this.removable && !this.multiple){
16050             var close = this.closeTriggerEl();
16051             if(close){
16052                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
16053                 close.on('click', this.removeBtnClick, this, close);
16054             }
16055         }
16056         /*
16057          * fix the bug in Safari iOS8
16058          */
16059         this.inputEl().on("focus", function(e){
16060             document.activeElement.blur();
16061         }, this);
16062         
16063         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
16064         
16065         return;
16066         
16067         
16068     },
16069     
16070     renderTouchView : function()
16071     {
16072         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
16073         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16074         
16075         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
16076         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16077         
16078         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
16079         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16080         this.touchViewBodyEl.setStyle('overflow', 'auto');
16081         
16082         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
16083         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16084         
16085         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
16086         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16087         
16088     },
16089     
16090     showTouchView : function()
16091     {
16092         if(this.disabled){
16093             return;
16094         }
16095         
16096         this.touchViewHeaderEl.hide();
16097
16098         if(this.modalTitle.length){
16099             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
16100             this.touchViewHeaderEl.show();
16101         }
16102
16103         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
16104         this.touchViewEl.show();
16105
16106         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
16107         
16108         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
16109         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
16110
16111         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16112
16113         if(this.modalTitle.length){
16114             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16115         }
16116         
16117         this.touchViewBodyEl.setHeight(bodyHeight);
16118
16119         if(this.animate){
16120             var _this = this;
16121             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
16122         }else{
16123             this.touchViewEl.addClass('in');
16124         }
16125         
16126         if(this._touchViewMask){
16127             Roo.get(document.body).addClass("x-body-masked");
16128             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
16129             this._touchViewMask.setStyle('z-index', 10000);
16130             this._touchViewMask.addClass('show');
16131         }
16132         
16133         this.doTouchViewQuery();
16134         
16135     },
16136     
16137     hideTouchView : function()
16138     {
16139         this.touchViewEl.removeClass('in');
16140
16141         if(this.animate){
16142             var _this = this;
16143             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
16144         }else{
16145             this.touchViewEl.setStyle('display', 'none');
16146         }
16147         
16148         if(this._touchViewMask){
16149             this._touchViewMask.removeClass('show');
16150             Roo.get(document.body).removeClass("x-body-masked");
16151         }
16152     },
16153     
16154     setTouchViewValue : function()
16155     {
16156         if(this.multiple){
16157             this.clearItem();
16158         
16159             var _this = this;
16160
16161             Roo.each(this.tickItems, function(o){
16162                 this.addItem(o);
16163             }, this);
16164         }
16165         
16166         this.hideTouchView();
16167     },
16168     
16169     doTouchViewQuery : function()
16170     {
16171         var qe = {
16172             query: '',
16173             forceAll: true,
16174             combo: this,
16175             cancel:false
16176         };
16177         
16178         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
16179             return false;
16180         }
16181         
16182         if(!this.alwaysQuery || this.mode == 'local'){
16183             this.onTouchViewLoad();
16184             return;
16185         }
16186         
16187         this.store.load();
16188     },
16189     
16190     onTouchViewBeforeLoad : function(combo,opts)
16191     {
16192         return;
16193     },
16194
16195     // private
16196     onTouchViewLoad : function()
16197     {
16198         if(this.store.getCount() < 1){
16199             this.onTouchViewEmptyResults();
16200             return;
16201         }
16202         
16203         this.clearTouchView();
16204         
16205         var rawValue = this.getRawValue();
16206         
16207         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
16208         
16209         this.tickItems = [];
16210         
16211         this.store.data.each(function(d, rowIndex){
16212             var row = this.touchViewListGroup.createChild(template);
16213             
16214             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
16215                 row.addClass(d.data.cls);
16216             }
16217             
16218             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16219                 var cfg = {
16220                     data : d.data,
16221                     html : d.data[this.displayField]
16222                 };
16223                 
16224                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
16225                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
16226                 }
16227             }
16228             row.removeClass('selected');
16229             if(!this.multiple && this.valueField &&
16230                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
16231             {
16232                 // radio buttons..
16233                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16234                 row.addClass('selected');
16235             }
16236             
16237             if(this.multiple && this.valueField &&
16238                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
16239             {
16240                 
16241                 // checkboxes...
16242                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16243                 this.tickItems.push(d.data);
16244             }
16245             
16246             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
16247             
16248         }, this);
16249         
16250         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
16251         
16252         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16253
16254         if(this.modalTitle.length){
16255             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16256         }
16257
16258         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
16259         
16260         if(this.mobile_restrict_height && listHeight < bodyHeight){
16261             this.touchViewBodyEl.setHeight(listHeight);
16262         }
16263         
16264         var _this = this;
16265         
16266         if(firstChecked && listHeight > bodyHeight){
16267             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
16268         }
16269         
16270     },
16271     
16272     onTouchViewLoadException : function()
16273     {
16274         this.hideTouchView();
16275     },
16276     
16277     onTouchViewEmptyResults : function()
16278     {
16279         this.clearTouchView();
16280         
16281         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
16282         
16283         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
16284         
16285     },
16286     
16287     clearTouchView : function()
16288     {
16289         this.touchViewListGroup.dom.innerHTML = '';
16290     },
16291     
16292     onTouchViewClick : function(e, el, o)
16293     {
16294         e.preventDefault();
16295         
16296         var row = o.row;
16297         var rowIndex = o.rowIndex;
16298         
16299         var r = this.store.getAt(rowIndex);
16300         
16301         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
16302             
16303             if(!this.multiple){
16304                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
16305                     c.dom.removeAttribute('checked');
16306                 }, this);
16307
16308                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16309
16310                 this.setFromData(r.data);
16311
16312                 var close = this.closeTriggerEl();
16313
16314                 if(close){
16315                     close.show();
16316                 }
16317
16318                 this.hideTouchView();
16319
16320                 this.fireEvent('select', this, r, rowIndex);
16321
16322                 return;
16323             }
16324
16325             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
16326                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
16327                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
16328                 return;
16329             }
16330
16331             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16332             this.addItem(r.data);
16333             this.tickItems.push(r.data);
16334         }
16335     },
16336     
16337     getAutoCreateNativeIOS : function()
16338     {
16339         var cfg = {
16340             cls: 'form-group' //input-group,
16341         };
16342         
16343         var combobox =  {
16344             tag: 'select',
16345             cls : 'roo-ios-select'
16346         };
16347         
16348         if (this.name) {
16349             combobox.name = this.name;
16350         }
16351         
16352         if (this.disabled) {
16353             combobox.disabled = true;
16354         }
16355         
16356         var settings = this;
16357         
16358         ['xs','sm','md','lg'].map(function(size){
16359             if (settings[size]) {
16360                 cfg.cls += ' col-' + size + '-' + settings[size];
16361             }
16362         });
16363         
16364         cfg.cn = combobox;
16365         
16366         return cfg;
16367         
16368     },
16369     
16370     initIOSView : function()
16371     {
16372         this.store.on('load', this.onIOSViewLoad, this);
16373         
16374         return;
16375     },
16376     
16377     onIOSViewLoad : function()
16378     {
16379         if(this.store.getCount() < 1){
16380             return;
16381         }
16382         
16383         this.clearIOSView();
16384         
16385         if(this.allowBlank) {
16386             
16387             var default_text = '-- SELECT --';
16388             
16389             if(this.placeholder.length){
16390                 default_text = this.placeholder;
16391             }
16392             
16393             if(this.emptyTitle.length){
16394                 default_text += ' - ' + this.emptyTitle + ' -';
16395             }
16396             
16397             var opt = this.inputEl().createChild({
16398                 tag: 'option',
16399                 value : 0,
16400                 html : default_text
16401             });
16402             
16403             var o = {};
16404             o[this.valueField] = 0;
16405             o[this.displayField] = default_text;
16406             
16407             this.ios_options.push({
16408                 data : o,
16409                 el : opt
16410             });
16411             
16412         }
16413         
16414         this.store.data.each(function(d, rowIndex){
16415             
16416             var html = '';
16417             
16418             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16419                 html = d.data[this.displayField];
16420             }
16421             
16422             var value = '';
16423             
16424             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16425                 value = d.data[this.valueField];
16426             }
16427             
16428             var option = {
16429                 tag: 'option',
16430                 value : value,
16431                 html : html
16432             };
16433             
16434             if(this.value == d.data[this.valueField]){
16435                 option['selected'] = true;
16436             }
16437             
16438             var opt = this.inputEl().createChild(option);
16439             
16440             this.ios_options.push({
16441                 data : d.data,
16442                 el : opt
16443             });
16444             
16445         }, this);
16446         
16447         this.inputEl().on('change', function(){
16448            this.fireEvent('select', this);
16449         }, this);
16450         
16451     },
16452     
16453     clearIOSView: function()
16454     {
16455         this.inputEl().dom.innerHTML = '';
16456         
16457         this.ios_options = [];
16458     },
16459     
16460     setIOSValue: function(v)
16461     {
16462         this.value = v;
16463         
16464         if(!this.ios_options){
16465             return;
16466         }
16467         
16468         Roo.each(this.ios_options, function(opts){
16469            
16470            opts.el.dom.removeAttribute('selected');
16471            
16472            if(opts.data[this.valueField] != v){
16473                return;
16474            }
16475            
16476            opts.el.dom.setAttribute('selected', true);
16477            
16478         }, this);
16479     }
16480
16481     /** 
16482     * @cfg {Boolean} grow 
16483     * @hide 
16484     */
16485     /** 
16486     * @cfg {Number} growMin 
16487     * @hide 
16488     */
16489     /** 
16490     * @cfg {Number} growMax 
16491     * @hide 
16492     */
16493     /**
16494      * @hide
16495      * @method autoSize
16496      */
16497 });
16498
16499 Roo.apply(Roo.bootstrap.ComboBox,  {
16500     
16501     header : {
16502         tag: 'div',
16503         cls: 'modal-header',
16504         cn: [
16505             {
16506                 tag: 'h4',
16507                 cls: 'modal-title'
16508             }
16509         ]
16510     },
16511     
16512     body : {
16513         tag: 'div',
16514         cls: 'modal-body',
16515         cn: [
16516             {
16517                 tag: 'ul',
16518                 cls: 'list-group'
16519             }
16520         ]
16521     },
16522     
16523     listItemRadio : {
16524         tag: 'li',
16525         cls: 'list-group-item',
16526         cn: [
16527             {
16528                 tag: 'span',
16529                 cls: 'roo-combobox-list-group-item-value'
16530             },
16531             {
16532                 tag: 'div',
16533                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16534                 cn: [
16535                     {
16536                         tag: 'input',
16537                         type: 'radio'
16538                     },
16539                     {
16540                         tag: 'label'
16541                     }
16542                 ]
16543             }
16544         ]
16545     },
16546     
16547     listItemCheckbox : {
16548         tag: 'li',
16549         cls: 'list-group-item',
16550         cn: [
16551             {
16552                 tag: 'span',
16553                 cls: 'roo-combobox-list-group-item-value'
16554             },
16555             {
16556                 tag: 'div',
16557                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16558                 cn: [
16559                     {
16560                         tag: 'input',
16561                         type: 'checkbox'
16562                     },
16563                     {
16564                         tag: 'label'
16565                     }
16566                 ]
16567             }
16568         ]
16569     },
16570     
16571     emptyResult : {
16572         tag: 'div',
16573         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16574     },
16575     
16576     footer : {
16577         tag: 'div',
16578         cls: 'modal-footer',
16579         cn: [
16580             {
16581                 tag: 'div',
16582                 cls: 'row',
16583                 cn: [
16584                     {
16585                         tag: 'div',
16586                         cls: 'col-xs-6 text-left',
16587                         cn: {
16588                             tag: 'button',
16589                             cls: 'btn btn-danger roo-touch-view-cancel',
16590                             html: 'Cancel'
16591                         }
16592                     },
16593                     {
16594                         tag: 'div',
16595                         cls: 'col-xs-6 text-right',
16596                         cn: {
16597                             tag: 'button',
16598                             cls: 'btn btn-success roo-touch-view-ok',
16599                             html: 'OK'
16600                         }
16601                     }
16602                 ]
16603             }
16604         ]
16605         
16606     }
16607 });
16608
16609 Roo.apply(Roo.bootstrap.ComboBox,  {
16610     
16611     touchViewTemplate : {
16612         tag: 'div',
16613         cls: 'modal fade roo-combobox-touch-view',
16614         cn: [
16615             {
16616                 tag: 'div',
16617                 cls: 'modal-dialog',
16618                 style : 'position:fixed', // we have to fix position....
16619                 cn: [
16620                     {
16621                         tag: 'div',
16622                         cls: 'modal-content',
16623                         cn: [
16624                             Roo.bootstrap.ComboBox.header,
16625                             Roo.bootstrap.ComboBox.body,
16626                             Roo.bootstrap.ComboBox.footer
16627                         ]
16628                     }
16629                 ]
16630             }
16631         ]
16632     }
16633 });/*
16634  * Based on:
16635  * Ext JS Library 1.1.1
16636  * Copyright(c) 2006-2007, Ext JS, LLC.
16637  *
16638  * Originally Released Under LGPL - original licence link has changed is not relivant.
16639  *
16640  * Fork - LGPL
16641  * <script type="text/javascript">
16642  */
16643
16644 /**
16645  * @class Roo.View
16646  * @extends Roo.util.Observable
16647  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16648  * This class also supports single and multi selection modes. <br>
16649  * Create a data model bound view:
16650  <pre><code>
16651  var store = new Roo.data.Store(...);
16652
16653  var view = new Roo.View({
16654     el : "my-element",
16655     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16656  
16657     singleSelect: true,
16658     selectedClass: "ydataview-selected",
16659     store: store
16660  });
16661
16662  // listen for node click?
16663  view.on("click", function(vw, index, node, e){
16664  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16665  });
16666
16667  // load XML data
16668  dataModel.load("foobar.xml");
16669  </code></pre>
16670  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16671  * <br><br>
16672  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16673  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16674  * 
16675  * Note: old style constructor is still suported (container, template, config)
16676  * 
16677  * @constructor
16678  * Create a new View
16679  * @param {Object} config The config object
16680  * 
16681  */
16682 Roo.View = function(config, depreciated_tpl, depreciated_config){
16683     
16684     this.parent = false;
16685     
16686     if (typeof(depreciated_tpl) == 'undefined') {
16687         // new way.. - universal constructor.
16688         Roo.apply(this, config);
16689         this.el  = Roo.get(this.el);
16690     } else {
16691         // old format..
16692         this.el  = Roo.get(config);
16693         this.tpl = depreciated_tpl;
16694         Roo.apply(this, depreciated_config);
16695     }
16696     this.wrapEl  = this.el.wrap().wrap();
16697     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16698     
16699     
16700     if(typeof(this.tpl) == "string"){
16701         this.tpl = new Roo.Template(this.tpl);
16702     } else {
16703         // support xtype ctors..
16704         this.tpl = new Roo.factory(this.tpl, Roo);
16705     }
16706     
16707     
16708     this.tpl.compile();
16709     
16710     /** @private */
16711     this.addEvents({
16712         /**
16713          * @event beforeclick
16714          * Fires before a click is processed. Returns false to cancel the default action.
16715          * @param {Roo.View} this
16716          * @param {Number} index The index of the target node
16717          * @param {HTMLElement} node The target node
16718          * @param {Roo.EventObject} e The raw event object
16719          */
16720             "beforeclick" : true,
16721         /**
16722          * @event click
16723          * Fires when a template node is clicked.
16724          * @param {Roo.View} this
16725          * @param {Number} index The index of the target node
16726          * @param {HTMLElement} node The target node
16727          * @param {Roo.EventObject} e The raw event object
16728          */
16729             "click" : true,
16730         /**
16731          * @event dblclick
16732          * Fires when a template node is double clicked.
16733          * @param {Roo.View} this
16734          * @param {Number} index The index of the target node
16735          * @param {HTMLElement} node The target node
16736          * @param {Roo.EventObject} e The raw event object
16737          */
16738             "dblclick" : true,
16739         /**
16740          * @event contextmenu
16741          * Fires when a template node is right clicked.
16742          * @param {Roo.View} this
16743          * @param {Number} index The index of the target node
16744          * @param {HTMLElement} node The target node
16745          * @param {Roo.EventObject} e The raw event object
16746          */
16747             "contextmenu" : true,
16748         /**
16749          * @event selectionchange
16750          * Fires when the selected nodes change.
16751          * @param {Roo.View} this
16752          * @param {Array} selections Array of the selected nodes
16753          */
16754             "selectionchange" : true,
16755     
16756         /**
16757          * @event beforeselect
16758          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16759          * @param {Roo.View} this
16760          * @param {HTMLElement} node The node to be selected
16761          * @param {Array} selections Array of currently selected nodes
16762          */
16763             "beforeselect" : true,
16764         /**
16765          * @event preparedata
16766          * Fires on every row to render, to allow you to change the data.
16767          * @param {Roo.View} this
16768          * @param {Object} data to be rendered (change this)
16769          */
16770           "preparedata" : true
16771           
16772           
16773         });
16774
16775
16776
16777     this.el.on({
16778         "click": this.onClick,
16779         "dblclick": this.onDblClick,
16780         "contextmenu": this.onContextMenu,
16781         scope:this
16782     });
16783
16784     this.selections = [];
16785     this.nodes = [];
16786     this.cmp = new Roo.CompositeElementLite([]);
16787     if(this.store){
16788         this.store = Roo.factory(this.store, Roo.data);
16789         this.setStore(this.store, true);
16790     }
16791     
16792     if ( this.footer && this.footer.xtype) {
16793            
16794          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16795         
16796         this.footer.dataSource = this.store;
16797         this.footer.container = fctr;
16798         this.footer = Roo.factory(this.footer, Roo);
16799         fctr.insertFirst(this.el);
16800         
16801         // this is a bit insane - as the paging toolbar seems to detach the el..
16802 //        dom.parentNode.parentNode.parentNode
16803          // they get detached?
16804     }
16805     
16806     
16807     Roo.View.superclass.constructor.call(this);
16808     
16809     
16810 };
16811
16812 Roo.extend(Roo.View, Roo.util.Observable, {
16813     
16814      /**
16815      * @cfg {Roo.data.Store} store Data store to load data from.
16816      */
16817     store : false,
16818     
16819     /**
16820      * @cfg {String|Roo.Element} el The container element.
16821      */
16822     el : '',
16823     
16824     /**
16825      * @cfg {String|Roo.Template} tpl The template used by this View 
16826      */
16827     tpl : false,
16828     /**
16829      * @cfg {String} dataName the named area of the template to use as the data area
16830      *                          Works with domtemplates roo-name="name"
16831      */
16832     dataName: false,
16833     /**
16834      * @cfg {String} selectedClass The css class to add to selected nodes
16835      */
16836     selectedClass : "x-view-selected",
16837      /**
16838      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16839      */
16840     emptyText : "",
16841     
16842     /**
16843      * @cfg {String} text to display on mask (default Loading)
16844      */
16845     mask : false,
16846     /**
16847      * @cfg {Boolean} multiSelect Allow multiple selection
16848      */
16849     multiSelect : false,
16850     /**
16851      * @cfg {Boolean} singleSelect Allow single selection
16852      */
16853     singleSelect:  false,
16854     
16855     /**
16856      * @cfg {Boolean} toggleSelect - selecting 
16857      */
16858     toggleSelect : false,
16859     
16860     /**
16861      * @cfg {Boolean} tickable - selecting 
16862      */
16863     tickable : false,
16864     
16865     /**
16866      * Returns the element this view is bound to.
16867      * @return {Roo.Element}
16868      */
16869     getEl : function(){
16870         return this.wrapEl;
16871     },
16872     
16873     
16874
16875     /**
16876      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16877      */
16878     refresh : function(){
16879         //Roo.log('refresh');
16880         var t = this.tpl;
16881         
16882         // if we are using something like 'domtemplate', then
16883         // the what gets used is:
16884         // t.applySubtemplate(NAME, data, wrapping data..)
16885         // the outer template then get' applied with
16886         //     the store 'extra data'
16887         // and the body get's added to the
16888         //      roo-name="data" node?
16889         //      <span class='roo-tpl-{name}'></span> ?????
16890         
16891         
16892         
16893         this.clearSelections();
16894         this.el.update("");
16895         var html = [];
16896         var records = this.store.getRange();
16897         if(records.length < 1) {
16898             
16899             // is this valid??  = should it render a template??
16900             
16901             this.el.update(this.emptyText);
16902             return;
16903         }
16904         var el = this.el;
16905         if (this.dataName) {
16906             this.el.update(t.apply(this.store.meta)); //????
16907             el = this.el.child('.roo-tpl-' + this.dataName);
16908         }
16909         
16910         for(var i = 0, len = records.length; i < len; i++){
16911             var data = this.prepareData(records[i].data, i, records[i]);
16912             this.fireEvent("preparedata", this, data, i, records[i]);
16913             
16914             var d = Roo.apply({}, data);
16915             
16916             if(this.tickable){
16917                 Roo.apply(d, {'roo-id' : Roo.id()});
16918                 
16919                 var _this = this;
16920             
16921                 Roo.each(this.parent.item, function(item){
16922                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16923                         return;
16924                     }
16925                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16926                 });
16927             }
16928             
16929             html[html.length] = Roo.util.Format.trim(
16930                 this.dataName ?
16931                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16932                     t.apply(d)
16933             );
16934         }
16935         
16936         
16937         
16938         el.update(html.join(""));
16939         this.nodes = el.dom.childNodes;
16940         this.updateIndexes(0);
16941     },
16942     
16943
16944     /**
16945      * Function to override to reformat the data that is sent to
16946      * the template for each node.
16947      * DEPRICATED - use the preparedata event handler.
16948      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16949      * a JSON object for an UpdateManager bound view).
16950      */
16951     prepareData : function(data, index, record)
16952     {
16953         this.fireEvent("preparedata", this, data, index, record);
16954         return data;
16955     },
16956
16957     onUpdate : function(ds, record){
16958         // Roo.log('on update');   
16959         this.clearSelections();
16960         var index = this.store.indexOf(record);
16961         var n = this.nodes[index];
16962         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16963         n.parentNode.removeChild(n);
16964         this.updateIndexes(index, index);
16965     },
16966
16967     
16968     
16969 // --------- FIXME     
16970     onAdd : function(ds, records, index)
16971     {
16972         //Roo.log(['on Add', ds, records, index] );        
16973         this.clearSelections();
16974         if(this.nodes.length == 0){
16975             this.refresh();
16976             return;
16977         }
16978         var n = this.nodes[index];
16979         for(var i = 0, len = records.length; i < len; i++){
16980             var d = this.prepareData(records[i].data, i, records[i]);
16981             if(n){
16982                 this.tpl.insertBefore(n, d);
16983             }else{
16984                 
16985                 this.tpl.append(this.el, d);
16986             }
16987         }
16988         this.updateIndexes(index);
16989     },
16990
16991     onRemove : function(ds, record, index){
16992        // Roo.log('onRemove');
16993         this.clearSelections();
16994         var el = this.dataName  ?
16995             this.el.child('.roo-tpl-' + this.dataName) :
16996             this.el; 
16997         
16998         el.dom.removeChild(this.nodes[index]);
16999         this.updateIndexes(index);
17000     },
17001
17002     /**
17003      * Refresh an individual node.
17004      * @param {Number} index
17005      */
17006     refreshNode : function(index){
17007         this.onUpdate(this.store, this.store.getAt(index));
17008     },
17009
17010     updateIndexes : function(startIndex, endIndex){
17011         var ns = this.nodes;
17012         startIndex = startIndex || 0;
17013         endIndex = endIndex || ns.length - 1;
17014         for(var i = startIndex; i <= endIndex; i++){
17015             ns[i].nodeIndex = i;
17016         }
17017     },
17018
17019     /**
17020      * Changes the data store this view uses and refresh the view.
17021      * @param {Store} store
17022      */
17023     setStore : function(store, initial){
17024         if(!initial && this.store){
17025             this.store.un("datachanged", this.refresh);
17026             this.store.un("add", this.onAdd);
17027             this.store.un("remove", this.onRemove);
17028             this.store.un("update", this.onUpdate);
17029             this.store.un("clear", this.refresh);
17030             this.store.un("beforeload", this.onBeforeLoad);
17031             this.store.un("load", this.onLoad);
17032             this.store.un("loadexception", this.onLoad);
17033         }
17034         if(store){
17035           
17036             store.on("datachanged", this.refresh, this);
17037             store.on("add", this.onAdd, this);
17038             store.on("remove", this.onRemove, this);
17039             store.on("update", this.onUpdate, this);
17040             store.on("clear", this.refresh, this);
17041             store.on("beforeload", this.onBeforeLoad, this);
17042             store.on("load", this.onLoad, this);
17043             store.on("loadexception", this.onLoad, this);
17044         }
17045         
17046         if(store){
17047             this.refresh();
17048         }
17049     },
17050     /**
17051      * onbeforeLoad - masks the loading area.
17052      *
17053      */
17054     onBeforeLoad : function(store,opts)
17055     {
17056          //Roo.log('onBeforeLoad');   
17057         if (!opts.add) {
17058             this.el.update("");
17059         }
17060         this.el.mask(this.mask ? this.mask : "Loading" ); 
17061     },
17062     onLoad : function ()
17063     {
17064         this.el.unmask();
17065     },
17066     
17067
17068     /**
17069      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
17070      * @param {HTMLElement} node
17071      * @return {HTMLElement} The template node
17072      */
17073     findItemFromChild : function(node){
17074         var el = this.dataName  ?
17075             this.el.child('.roo-tpl-' + this.dataName,true) :
17076             this.el.dom; 
17077         
17078         if(!node || node.parentNode == el){
17079                     return node;
17080             }
17081             var p = node.parentNode;
17082             while(p && p != el){
17083             if(p.parentNode == el){
17084                 return p;
17085             }
17086             p = p.parentNode;
17087         }
17088             return null;
17089     },
17090
17091     /** @ignore */
17092     onClick : function(e){
17093         var item = this.findItemFromChild(e.getTarget());
17094         if(item){
17095             var index = this.indexOf(item);
17096             if(this.onItemClick(item, index, e) !== false){
17097                 this.fireEvent("click", this, index, item, e);
17098             }
17099         }else{
17100             this.clearSelections();
17101         }
17102     },
17103
17104     /** @ignore */
17105     onContextMenu : function(e){
17106         var item = this.findItemFromChild(e.getTarget());
17107         if(item){
17108             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
17109         }
17110     },
17111
17112     /** @ignore */
17113     onDblClick : function(e){
17114         var item = this.findItemFromChild(e.getTarget());
17115         if(item){
17116             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
17117         }
17118     },
17119
17120     onItemClick : function(item, index, e)
17121     {
17122         if(this.fireEvent("beforeclick", this, index, item, e) === false){
17123             return false;
17124         }
17125         if (this.toggleSelect) {
17126             var m = this.isSelected(item) ? 'unselect' : 'select';
17127             //Roo.log(m);
17128             var _t = this;
17129             _t[m](item, true, false);
17130             return true;
17131         }
17132         if(this.multiSelect || this.singleSelect){
17133             if(this.multiSelect && e.shiftKey && this.lastSelection){
17134                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
17135             }else{
17136                 this.select(item, this.multiSelect && e.ctrlKey);
17137                 this.lastSelection = item;
17138             }
17139             
17140             if(!this.tickable){
17141                 e.preventDefault();
17142             }
17143             
17144         }
17145         return true;
17146     },
17147
17148     /**
17149      * Get the number of selected nodes.
17150      * @return {Number}
17151      */
17152     getSelectionCount : function(){
17153         return this.selections.length;
17154     },
17155
17156     /**
17157      * Get the currently selected nodes.
17158      * @return {Array} An array of HTMLElements
17159      */
17160     getSelectedNodes : function(){
17161         return this.selections;
17162     },
17163
17164     /**
17165      * Get the indexes of the selected nodes.
17166      * @return {Array}
17167      */
17168     getSelectedIndexes : function(){
17169         var indexes = [], s = this.selections;
17170         for(var i = 0, len = s.length; i < len; i++){
17171             indexes.push(s[i].nodeIndex);
17172         }
17173         return indexes;
17174     },
17175
17176     /**
17177      * Clear all selections
17178      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
17179      */
17180     clearSelections : function(suppressEvent){
17181         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
17182             this.cmp.elements = this.selections;
17183             this.cmp.removeClass(this.selectedClass);
17184             this.selections = [];
17185             if(!suppressEvent){
17186                 this.fireEvent("selectionchange", this, this.selections);
17187             }
17188         }
17189     },
17190
17191     /**
17192      * Returns true if the passed node is selected
17193      * @param {HTMLElement/Number} node The node or node index
17194      * @return {Boolean}
17195      */
17196     isSelected : function(node){
17197         var s = this.selections;
17198         if(s.length < 1){
17199             return false;
17200         }
17201         node = this.getNode(node);
17202         return s.indexOf(node) !== -1;
17203     },
17204
17205     /**
17206      * Selects nodes.
17207      * @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
17208      * @param {Boolean} keepExisting (optional) true to keep existing selections
17209      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17210      */
17211     select : function(nodeInfo, keepExisting, suppressEvent){
17212         if(nodeInfo instanceof Array){
17213             if(!keepExisting){
17214                 this.clearSelections(true);
17215             }
17216             for(var i = 0, len = nodeInfo.length; i < len; i++){
17217                 this.select(nodeInfo[i], true, true);
17218             }
17219             return;
17220         } 
17221         var node = this.getNode(nodeInfo);
17222         if(!node || this.isSelected(node)){
17223             return; // already selected.
17224         }
17225         if(!keepExisting){
17226             this.clearSelections(true);
17227         }
17228         
17229         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
17230             Roo.fly(node).addClass(this.selectedClass);
17231             this.selections.push(node);
17232             if(!suppressEvent){
17233                 this.fireEvent("selectionchange", this, this.selections);
17234             }
17235         }
17236         
17237         
17238     },
17239       /**
17240      * Unselects nodes.
17241      * @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
17242      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
17243      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17244      */
17245     unselect : function(nodeInfo, keepExisting, suppressEvent)
17246     {
17247         if(nodeInfo instanceof Array){
17248             Roo.each(this.selections, function(s) {
17249                 this.unselect(s, nodeInfo);
17250             }, this);
17251             return;
17252         }
17253         var node = this.getNode(nodeInfo);
17254         if(!node || !this.isSelected(node)){
17255             //Roo.log("not selected");
17256             return; // not selected.
17257         }
17258         // fireevent???
17259         var ns = [];
17260         Roo.each(this.selections, function(s) {
17261             if (s == node ) {
17262                 Roo.fly(node).removeClass(this.selectedClass);
17263
17264                 return;
17265             }
17266             ns.push(s);
17267         },this);
17268         
17269         this.selections= ns;
17270         this.fireEvent("selectionchange", this, this.selections);
17271     },
17272
17273     /**
17274      * Gets a template node.
17275      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17276      * @return {HTMLElement} The node or null if it wasn't found
17277      */
17278     getNode : function(nodeInfo){
17279         if(typeof nodeInfo == "string"){
17280             return document.getElementById(nodeInfo);
17281         }else if(typeof nodeInfo == "number"){
17282             return this.nodes[nodeInfo];
17283         }
17284         return nodeInfo;
17285     },
17286
17287     /**
17288      * Gets a range template nodes.
17289      * @param {Number} startIndex
17290      * @param {Number} endIndex
17291      * @return {Array} An array of nodes
17292      */
17293     getNodes : function(start, end){
17294         var ns = this.nodes;
17295         start = start || 0;
17296         end = typeof end == "undefined" ? ns.length - 1 : end;
17297         var nodes = [];
17298         if(start <= end){
17299             for(var i = start; i <= end; i++){
17300                 nodes.push(ns[i]);
17301             }
17302         } else{
17303             for(var i = start; i >= end; i--){
17304                 nodes.push(ns[i]);
17305             }
17306         }
17307         return nodes;
17308     },
17309
17310     /**
17311      * Finds the index of the passed node
17312      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17313      * @return {Number} The index of the node or -1
17314      */
17315     indexOf : function(node){
17316         node = this.getNode(node);
17317         if(typeof node.nodeIndex == "number"){
17318             return node.nodeIndex;
17319         }
17320         var ns = this.nodes;
17321         for(var i = 0, len = ns.length; i < len; i++){
17322             if(ns[i] == node){
17323                 return i;
17324             }
17325         }
17326         return -1;
17327     }
17328 });
17329 /*
17330  * - LGPL
17331  *
17332  * based on jquery fullcalendar
17333  * 
17334  */
17335
17336 Roo.bootstrap = Roo.bootstrap || {};
17337 /**
17338  * @class Roo.bootstrap.Calendar
17339  * @extends Roo.bootstrap.Component
17340  * Bootstrap Calendar class
17341  * @cfg {Boolean} loadMask (true|false) default false
17342  * @cfg {Object} header generate the user specific header of the calendar, default false
17343
17344  * @constructor
17345  * Create a new Container
17346  * @param {Object} config The config object
17347  */
17348
17349
17350
17351 Roo.bootstrap.Calendar = function(config){
17352     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
17353      this.addEvents({
17354         /**
17355              * @event select
17356              * Fires when a date is selected
17357              * @param {DatePicker} this
17358              * @param {Date} date The selected date
17359              */
17360         'select': true,
17361         /**
17362              * @event monthchange
17363              * Fires when the displayed month changes 
17364              * @param {DatePicker} this
17365              * @param {Date} date The selected month
17366              */
17367         'monthchange': true,
17368         /**
17369              * @event evententer
17370              * Fires when mouse over an event
17371              * @param {Calendar} this
17372              * @param {event} Event
17373              */
17374         'evententer': true,
17375         /**
17376              * @event eventleave
17377              * Fires when the mouse leaves an
17378              * @param {Calendar} this
17379              * @param {event}
17380              */
17381         'eventleave': true,
17382         /**
17383              * @event eventclick
17384              * Fires when the mouse click an
17385              * @param {Calendar} this
17386              * @param {event}
17387              */
17388         'eventclick': true
17389         
17390     });
17391
17392 };
17393
17394 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
17395     
17396      /**
17397      * @cfg {Number} startDay
17398      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
17399      */
17400     startDay : 0,
17401     
17402     loadMask : false,
17403     
17404     header : false,
17405       
17406     getAutoCreate : function(){
17407         
17408         
17409         var fc_button = function(name, corner, style, content ) {
17410             return Roo.apply({},{
17411                 tag : 'span',
17412                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
17413                          (corner.length ?
17414                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17415                             ''
17416                         ),
17417                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17418                 unselectable: 'on'
17419             });
17420         };
17421         
17422         var header = {};
17423         
17424         if(!this.header){
17425             header = {
17426                 tag : 'table',
17427                 cls : 'fc-header',
17428                 style : 'width:100%',
17429                 cn : [
17430                     {
17431                         tag: 'tr',
17432                         cn : [
17433                             {
17434                                 tag : 'td',
17435                                 cls : 'fc-header-left',
17436                                 cn : [
17437                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
17438                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
17439                                     { tag: 'span', cls: 'fc-header-space' },
17440                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
17441
17442
17443                                 ]
17444                             },
17445
17446                             {
17447                                 tag : 'td',
17448                                 cls : 'fc-header-center',
17449                                 cn : [
17450                                     {
17451                                         tag: 'span',
17452                                         cls: 'fc-header-title',
17453                                         cn : {
17454                                             tag: 'H2',
17455                                             html : 'month / year'
17456                                         }
17457                                     }
17458
17459                                 ]
17460                             },
17461                             {
17462                                 tag : 'td',
17463                                 cls : 'fc-header-right',
17464                                 cn : [
17465                               /*      fc_button('month', 'left', '', 'month' ),
17466                                     fc_button('week', '', '', 'week' ),
17467                                     fc_button('day', 'right', '', 'day' )
17468                                 */    
17469
17470                                 ]
17471                             }
17472
17473                         ]
17474                     }
17475                 ]
17476             };
17477         }
17478         
17479         header = this.header;
17480         
17481        
17482         var cal_heads = function() {
17483             var ret = [];
17484             // fixme - handle this.
17485             
17486             for (var i =0; i < Date.dayNames.length; i++) {
17487                 var d = Date.dayNames[i];
17488                 ret.push({
17489                     tag: 'th',
17490                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17491                     html : d.substring(0,3)
17492                 });
17493                 
17494             }
17495             ret[0].cls += ' fc-first';
17496             ret[6].cls += ' fc-last';
17497             return ret;
17498         };
17499         var cal_cell = function(n) {
17500             return  {
17501                 tag: 'td',
17502                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17503                 cn : [
17504                     {
17505                         cn : [
17506                             {
17507                                 cls: 'fc-day-number',
17508                                 html: 'D'
17509                             },
17510                             {
17511                                 cls: 'fc-day-content',
17512                              
17513                                 cn : [
17514                                      {
17515                                         style: 'position: relative;' // height: 17px;
17516                                     }
17517                                 ]
17518                             }
17519                             
17520                             
17521                         ]
17522                     }
17523                 ]
17524                 
17525             }
17526         };
17527         var cal_rows = function() {
17528             
17529             var ret = [];
17530             for (var r = 0; r < 6; r++) {
17531                 var row= {
17532                     tag : 'tr',
17533                     cls : 'fc-week',
17534                     cn : []
17535                 };
17536                 
17537                 for (var i =0; i < Date.dayNames.length; i++) {
17538                     var d = Date.dayNames[i];
17539                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17540
17541                 }
17542                 row.cn[0].cls+=' fc-first';
17543                 row.cn[0].cn[0].style = 'min-height:90px';
17544                 row.cn[6].cls+=' fc-last';
17545                 ret.push(row);
17546                 
17547             }
17548             ret[0].cls += ' fc-first';
17549             ret[4].cls += ' fc-prev-last';
17550             ret[5].cls += ' fc-last';
17551             return ret;
17552             
17553         };
17554         
17555         var cal_table = {
17556             tag: 'table',
17557             cls: 'fc-border-separate',
17558             style : 'width:100%',
17559             cellspacing  : 0,
17560             cn : [
17561                 { 
17562                     tag: 'thead',
17563                     cn : [
17564                         { 
17565                             tag: 'tr',
17566                             cls : 'fc-first fc-last',
17567                             cn : cal_heads()
17568                         }
17569                     ]
17570                 },
17571                 { 
17572                     tag: 'tbody',
17573                     cn : cal_rows()
17574                 }
17575                   
17576             ]
17577         };
17578          
17579          var cfg = {
17580             cls : 'fc fc-ltr',
17581             cn : [
17582                 header,
17583                 {
17584                     cls : 'fc-content',
17585                     style : "position: relative;",
17586                     cn : [
17587                         {
17588                             cls : 'fc-view fc-view-month fc-grid',
17589                             style : 'position: relative',
17590                             unselectable : 'on',
17591                             cn : [
17592                                 {
17593                                     cls : 'fc-event-container',
17594                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17595                                 },
17596                                 cal_table
17597                             ]
17598                         }
17599                     ]
17600     
17601                 }
17602            ] 
17603             
17604         };
17605         
17606          
17607         
17608         return cfg;
17609     },
17610     
17611     
17612     initEvents : function()
17613     {
17614         if(!this.store){
17615             throw "can not find store for calendar";
17616         }
17617         
17618         var mark = {
17619             tag: "div",
17620             cls:"x-dlg-mask",
17621             style: "text-align:center",
17622             cn: [
17623                 {
17624                     tag: "div",
17625                     style: "background-color:white;width:50%;margin:250 auto",
17626                     cn: [
17627                         {
17628                             tag: "img",
17629                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17630                         },
17631                         {
17632                             tag: "span",
17633                             html: "Loading"
17634                         }
17635                         
17636                     ]
17637                 }
17638             ]
17639         };
17640         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17641         
17642         var size = this.el.select('.fc-content', true).first().getSize();
17643         this.maskEl.setSize(size.width, size.height);
17644         this.maskEl.enableDisplayMode("block");
17645         if(!this.loadMask){
17646             this.maskEl.hide();
17647         }
17648         
17649         this.store = Roo.factory(this.store, Roo.data);
17650         this.store.on('load', this.onLoad, this);
17651         this.store.on('beforeload', this.onBeforeLoad, this);
17652         
17653         this.resize();
17654         
17655         this.cells = this.el.select('.fc-day',true);
17656         //Roo.log(this.cells);
17657         this.textNodes = this.el.query('.fc-day-number');
17658         this.cells.addClassOnOver('fc-state-hover');
17659         
17660         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17661         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17662         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17663         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17664         
17665         this.on('monthchange', this.onMonthChange, this);
17666         
17667         this.update(new Date().clearTime());
17668     },
17669     
17670     resize : function() {
17671         var sz  = this.el.getSize();
17672         
17673         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17674         this.el.select('.fc-day-content div',true).setHeight(34);
17675     },
17676     
17677     
17678     // private
17679     showPrevMonth : function(e){
17680         this.update(this.activeDate.add("mo", -1));
17681     },
17682     showToday : function(e){
17683         this.update(new Date().clearTime());
17684     },
17685     // private
17686     showNextMonth : function(e){
17687         this.update(this.activeDate.add("mo", 1));
17688     },
17689
17690     // private
17691     showPrevYear : function(){
17692         this.update(this.activeDate.add("y", -1));
17693     },
17694
17695     // private
17696     showNextYear : function(){
17697         this.update(this.activeDate.add("y", 1));
17698     },
17699
17700     
17701    // private
17702     update : function(date)
17703     {
17704         var vd = this.activeDate;
17705         this.activeDate = date;
17706 //        if(vd && this.el){
17707 //            var t = date.getTime();
17708 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17709 //                Roo.log('using add remove');
17710 //                
17711 //                this.fireEvent('monthchange', this, date);
17712 //                
17713 //                this.cells.removeClass("fc-state-highlight");
17714 //                this.cells.each(function(c){
17715 //                   if(c.dateValue == t){
17716 //                       c.addClass("fc-state-highlight");
17717 //                       setTimeout(function(){
17718 //                            try{c.dom.firstChild.focus();}catch(e){}
17719 //                       }, 50);
17720 //                       return false;
17721 //                   }
17722 //                   return true;
17723 //                });
17724 //                return;
17725 //            }
17726 //        }
17727         
17728         var days = date.getDaysInMonth();
17729         
17730         var firstOfMonth = date.getFirstDateOfMonth();
17731         var startingPos = firstOfMonth.getDay()-this.startDay;
17732         
17733         if(startingPos < this.startDay){
17734             startingPos += 7;
17735         }
17736         
17737         var pm = date.add(Date.MONTH, -1);
17738         var prevStart = pm.getDaysInMonth()-startingPos;
17739 //        
17740         this.cells = this.el.select('.fc-day',true);
17741         this.textNodes = this.el.query('.fc-day-number');
17742         this.cells.addClassOnOver('fc-state-hover');
17743         
17744         var cells = this.cells.elements;
17745         var textEls = this.textNodes;
17746         
17747         Roo.each(cells, function(cell){
17748             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17749         });
17750         
17751         days += startingPos;
17752
17753         // convert everything to numbers so it's fast
17754         var day = 86400000;
17755         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17756         //Roo.log(d);
17757         //Roo.log(pm);
17758         //Roo.log(prevStart);
17759         
17760         var today = new Date().clearTime().getTime();
17761         var sel = date.clearTime().getTime();
17762         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17763         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17764         var ddMatch = this.disabledDatesRE;
17765         var ddText = this.disabledDatesText;
17766         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17767         var ddaysText = this.disabledDaysText;
17768         var format = this.format;
17769         
17770         var setCellClass = function(cal, cell){
17771             cell.row = 0;
17772             cell.events = [];
17773             cell.more = [];
17774             //Roo.log('set Cell Class');
17775             cell.title = "";
17776             var t = d.getTime();
17777             
17778             //Roo.log(d);
17779             
17780             cell.dateValue = t;
17781             if(t == today){
17782                 cell.className += " fc-today";
17783                 cell.className += " fc-state-highlight";
17784                 cell.title = cal.todayText;
17785             }
17786             if(t == sel){
17787                 // disable highlight in other month..
17788                 //cell.className += " fc-state-highlight";
17789                 
17790             }
17791             // disabling
17792             if(t < min) {
17793                 cell.className = " fc-state-disabled";
17794                 cell.title = cal.minText;
17795                 return;
17796             }
17797             if(t > max) {
17798                 cell.className = " fc-state-disabled";
17799                 cell.title = cal.maxText;
17800                 return;
17801             }
17802             if(ddays){
17803                 if(ddays.indexOf(d.getDay()) != -1){
17804                     cell.title = ddaysText;
17805                     cell.className = " fc-state-disabled";
17806                 }
17807             }
17808             if(ddMatch && format){
17809                 var fvalue = d.dateFormat(format);
17810                 if(ddMatch.test(fvalue)){
17811                     cell.title = ddText.replace("%0", fvalue);
17812                     cell.className = " fc-state-disabled";
17813                 }
17814             }
17815             
17816             if (!cell.initialClassName) {
17817                 cell.initialClassName = cell.dom.className;
17818             }
17819             
17820             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17821         };
17822
17823         var i = 0;
17824         
17825         for(; i < startingPos; i++) {
17826             textEls[i].innerHTML = (++prevStart);
17827             d.setDate(d.getDate()+1);
17828             
17829             cells[i].className = "fc-past fc-other-month";
17830             setCellClass(this, cells[i]);
17831         }
17832         
17833         var intDay = 0;
17834         
17835         for(; i < days; i++){
17836             intDay = i - startingPos + 1;
17837             textEls[i].innerHTML = (intDay);
17838             d.setDate(d.getDate()+1);
17839             
17840             cells[i].className = ''; // "x-date-active";
17841             setCellClass(this, cells[i]);
17842         }
17843         var extraDays = 0;
17844         
17845         for(; i < 42; i++) {
17846             textEls[i].innerHTML = (++extraDays);
17847             d.setDate(d.getDate()+1);
17848             
17849             cells[i].className = "fc-future fc-other-month";
17850             setCellClass(this, cells[i]);
17851         }
17852         
17853         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17854         
17855         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17856         
17857         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17858         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17859         
17860         if(totalRows != 6){
17861             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17862             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17863         }
17864         
17865         this.fireEvent('monthchange', this, date);
17866         
17867         
17868         /*
17869         if(!this.internalRender){
17870             var main = this.el.dom.firstChild;
17871             var w = main.offsetWidth;
17872             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17873             Roo.fly(main).setWidth(w);
17874             this.internalRender = true;
17875             // opera does not respect the auto grow header center column
17876             // then, after it gets a width opera refuses to recalculate
17877             // without a second pass
17878             if(Roo.isOpera && !this.secondPass){
17879                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17880                 this.secondPass = true;
17881                 this.update.defer(10, this, [date]);
17882             }
17883         }
17884         */
17885         
17886     },
17887     
17888     findCell : function(dt) {
17889         dt = dt.clearTime().getTime();
17890         var ret = false;
17891         this.cells.each(function(c){
17892             //Roo.log("check " +c.dateValue + '?=' + dt);
17893             if(c.dateValue == dt){
17894                 ret = c;
17895                 return false;
17896             }
17897             return true;
17898         });
17899         
17900         return ret;
17901     },
17902     
17903     findCells : function(ev) {
17904         var s = ev.start.clone().clearTime().getTime();
17905        // Roo.log(s);
17906         var e= ev.end.clone().clearTime().getTime();
17907        // Roo.log(e);
17908         var ret = [];
17909         this.cells.each(function(c){
17910              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17911             
17912             if(c.dateValue > e){
17913                 return ;
17914             }
17915             if(c.dateValue < s){
17916                 return ;
17917             }
17918             ret.push(c);
17919         });
17920         
17921         return ret;    
17922     },
17923     
17924 //    findBestRow: function(cells)
17925 //    {
17926 //        var ret = 0;
17927 //        
17928 //        for (var i =0 ; i < cells.length;i++) {
17929 //            ret  = Math.max(cells[i].rows || 0,ret);
17930 //        }
17931 //        return ret;
17932 //        
17933 //    },
17934     
17935     
17936     addItem : function(ev)
17937     {
17938         // look for vertical location slot in
17939         var cells = this.findCells(ev);
17940         
17941 //        ev.row = this.findBestRow(cells);
17942         
17943         // work out the location.
17944         
17945         var crow = false;
17946         var rows = [];
17947         for(var i =0; i < cells.length; i++) {
17948             
17949             cells[i].row = cells[0].row;
17950             
17951             if(i == 0){
17952                 cells[i].row = cells[i].row + 1;
17953             }
17954             
17955             if (!crow) {
17956                 crow = {
17957                     start : cells[i],
17958                     end :  cells[i]
17959                 };
17960                 continue;
17961             }
17962             if (crow.start.getY() == cells[i].getY()) {
17963                 // on same row.
17964                 crow.end = cells[i];
17965                 continue;
17966             }
17967             // different row.
17968             rows.push(crow);
17969             crow = {
17970                 start: cells[i],
17971                 end : cells[i]
17972             };
17973             
17974         }
17975         
17976         rows.push(crow);
17977         ev.els = [];
17978         ev.rows = rows;
17979         ev.cells = cells;
17980         
17981         cells[0].events.push(ev);
17982         
17983         this.calevents.push(ev);
17984     },
17985     
17986     clearEvents: function() {
17987         
17988         if(!this.calevents){
17989             return;
17990         }
17991         
17992         Roo.each(this.cells.elements, function(c){
17993             c.row = 0;
17994             c.events = [];
17995             c.more = [];
17996         });
17997         
17998         Roo.each(this.calevents, function(e) {
17999             Roo.each(e.els, function(el) {
18000                 el.un('mouseenter' ,this.onEventEnter, this);
18001                 el.un('mouseleave' ,this.onEventLeave, this);
18002                 el.remove();
18003             },this);
18004         },this);
18005         
18006         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
18007             e.remove();
18008         });
18009         
18010     },
18011     
18012     renderEvents: function()
18013     {   
18014         var _this = this;
18015         
18016         this.cells.each(function(c) {
18017             
18018             if(c.row < 5){
18019                 return;
18020             }
18021             
18022             var ev = c.events;
18023             
18024             var r = 4;
18025             if(c.row != c.events.length){
18026                 r = 4 - (4 - (c.row - c.events.length));
18027             }
18028             
18029             c.events = ev.slice(0, r);
18030             c.more = ev.slice(r);
18031             
18032             if(c.more.length && c.more.length == 1){
18033                 c.events.push(c.more.pop());
18034             }
18035             
18036             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
18037             
18038         });
18039             
18040         this.cells.each(function(c) {
18041             
18042             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
18043             
18044             
18045             for (var e = 0; e < c.events.length; e++){
18046                 var ev = c.events[e];
18047                 var rows = ev.rows;
18048                 
18049                 for(var i = 0; i < rows.length; i++) {
18050                 
18051                     // how many rows should it span..
18052
18053                     var  cfg = {
18054                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
18055                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
18056
18057                         unselectable : "on",
18058                         cn : [
18059                             {
18060                                 cls: 'fc-event-inner',
18061                                 cn : [
18062     //                                {
18063     //                                  tag:'span',
18064     //                                  cls: 'fc-event-time',
18065     //                                  html : cells.length > 1 ? '' : ev.time
18066     //                                },
18067                                     {
18068                                       tag:'span',
18069                                       cls: 'fc-event-title',
18070                                       html : String.format('{0}', ev.title)
18071                                     }
18072
18073
18074                                 ]
18075                             },
18076                             {
18077                                 cls: 'ui-resizable-handle ui-resizable-e',
18078                                 html : '&nbsp;&nbsp;&nbsp'
18079                             }
18080
18081                         ]
18082                     };
18083
18084                     if (i == 0) {
18085                         cfg.cls += ' fc-event-start';
18086                     }
18087                     if ((i+1) == rows.length) {
18088                         cfg.cls += ' fc-event-end';
18089                     }
18090
18091                     var ctr = _this.el.select('.fc-event-container',true).first();
18092                     var cg = ctr.createChild(cfg);
18093
18094                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
18095                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
18096
18097                     var r = (c.more.length) ? 1 : 0;
18098                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
18099                     cg.setWidth(ebox.right - sbox.x -2);
18100
18101                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
18102                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
18103                     cg.on('click', _this.onEventClick, _this, ev);
18104
18105                     ev.els.push(cg);
18106                     
18107                 }
18108                 
18109             }
18110             
18111             
18112             if(c.more.length){
18113                 var  cfg = {
18114                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
18115                     style : 'position: absolute',
18116                     unselectable : "on",
18117                     cn : [
18118                         {
18119                             cls: 'fc-event-inner',
18120                             cn : [
18121                                 {
18122                                   tag:'span',
18123                                   cls: 'fc-event-title',
18124                                   html : 'More'
18125                                 }
18126
18127
18128                             ]
18129                         },
18130                         {
18131                             cls: 'ui-resizable-handle ui-resizable-e',
18132                             html : '&nbsp;&nbsp;&nbsp'
18133                         }
18134
18135                     ]
18136                 };
18137
18138                 var ctr = _this.el.select('.fc-event-container',true).first();
18139                 var cg = ctr.createChild(cfg);
18140
18141                 var sbox = c.select('.fc-day-content',true).first().getBox();
18142                 var ebox = c.select('.fc-day-content',true).first().getBox();
18143                 //Roo.log(cg);
18144                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
18145                 cg.setWidth(ebox.right - sbox.x -2);
18146
18147                 cg.on('click', _this.onMoreEventClick, _this, c.more);
18148                 
18149             }
18150             
18151         });
18152         
18153         
18154         
18155     },
18156     
18157     onEventEnter: function (e, el,event,d) {
18158         this.fireEvent('evententer', this, el, event);
18159     },
18160     
18161     onEventLeave: function (e, el,event,d) {
18162         this.fireEvent('eventleave', this, el, event);
18163     },
18164     
18165     onEventClick: function (e, el,event,d) {
18166         this.fireEvent('eventclick', this, el, event);
18167     },
18168     
18169     onMonthChange: function () {
18170         this.store.load();
18171     },
18172     
18173     onMoreEventClick: function(e, el, more)
18174     {
18175         var _this = this;
18176         
18177         this.calpopover.placement = 'right';
18178         this.calpopover.setTitle('More');
18179         
18180         this.calpopover.setContent('');
18181         
18182         var ctr = this.calpopover.el.select('.popover-content', true).first();
18183         
18184         Roo.each(more, function(m){
18185             var cfg = {
18186                 cls : 'fc-event-hori fc-event-draggable',
18187                 html : m.title
18188             };
18189             var cg = ctr.createChild(cfg);
18190             
18191             cg.on('click', _this.onEventClick, _this, m);
18192         });
18193         
18194         this.calpopover.show(el);
18195         
18196         
18197     },
18198     
18199     onLoad: function () 
18200     {   
18201         this.calevents = [];
18202         var cal = this;
18203         
18204         if(this.store.getCount() > 0){
18205             this.store.data.each(function(d){
18206                cal.addItem({
18207                     id : d.data.id,
18208                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
18209                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
18210                     time : d.data.start_time,
18211                     title : d.data.title,
18212                     description : d.data.description,
18213                     venue : d.data.venue
18214                 });
18215             });
18216         }
18217         
18218         this.renderEvents();
18219         
18220         if(this.calevents.length && this.loadMask){
18221             this.maskEl.hide();
18222         }
18223     },
18224     
18225     onBeforeLoad: function()
18226     {
18227         this.clearEvents();
18228         if(this.loadMask){
18229             this.maskEl.show();
18230         }
18231     }
18232 });
18233
18234  
18235  /*
18236  * - LGPL
18237  *
18238  * element
18239  * 
18240  */
18241
18242 /**
18243  * @class Roo.bootstrap.Popover
18244  * @extends Roo.bootstrap.Component
18245  * Bootstrap Popover class
18246  * @cfg {String} html contents of the popover   (or false to use children..)
18247  * @cfg {String} title of popover (or false to hide)
18248  * @cfg {String} placement how it is placed
18249  * @cfg {String} trigger click || hover (or false to trigger manually)
18250  * @cfg {String} over what (parent or false to trigger manually.)
18251  * @cfg {Number} delay - delay before showing
18252  
18253  * @constructor
18254  * Create a new Popover
18255  * @param {Object} config The config object
18256  */
18257
18258 Roo.bootstrap.Popover = function(config){
18259     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
18260     
18261     this.addEvents({
18262         // raw events
18263          /**
18264          * @event show
18265          * After the popover show
18266          * 
18267          * @param {Roo.bootstrap.Popover} this
18268          */
18269         "show" : true,
18270         /**
18271          * @event hide
18272          * After the popover hide
18273          * 
18274          * @param {Roo.bootstrap.Popover} this
18275          */
18276         "hide" : true
18277     });
18278 };
18279
18280 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
18281     
18282     title: 'Fill in a title',
18283     html: false,
18284     
18285     placement : 'right',
18286     trigger : 'hover', // hover
18287     
18288     delay : 0,
18289     
18290     over: 'parent',
18291     
18292     can_build_overlaid : false,
18293     
18294     getChildContainer : function()
18295     {
18296         return this.el.select('.popover-content',true).first();
18297     },
18298     
18299     getAutoCreate : function(){
18300          
18301         var cfg = {
18302            cls : 'popover roo-dynamic',
18303            style: 'display:block',
18304            cn : [
18305                 {
18306                     cls : 'arrow'
18307                 },
18308                 {
18309                     cls : 'popover-inner',
18310                     cn : [
18311                         {
18312                             tag: 'h3',
18313                             cls: 'popover-title popover-header',
18314                             html : this.title
18315                         },
18316                         {
18317                             cls : 'popover-content popover-body',
18318                             html : this.html
18319                         }
18320                     ]
18321                     
18322                 }
18323            ]
18324         };
18325         
18326         return cfg;
18327     },
18328     setTitle: function(str)
18329     {
18330         this.title = str;
18331         this.el.select('.popover-title',true).first().dom.innerHTML = str;
18332     },
18333     setContent: function(str)
18334     {
18335         this.html = str;
18336         this.el.select('.popover-content',true).first().dom.innerHTML = str;
18337     },
18338     // as it get's added to the bottom of the page.
18339     onRender : function(ct, position)
18340     {
18341         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
18342         if(!this.el){
18343             var cfg = Roo.apply({},  this.getAutoCreate());
18344             cfg.id = Roo.id();
18345             
18346             if (this.cls) {
18347                 cfg.cls += ' ' + this.cls;
18348             }
18349             if (this.style) {
18350                 cfg.style = this.style;
18351             }
18352             //Roo.log("adding to ");
18353             this.el = Roo.get(document.body).createChild(cfg, position);
18354 //            Roo.log(this.el);
18355         }
18356         this.initEvents();
18357     },
18358     
18359     initEvents : function()
18360     {
18361         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
18362         this.el.enableDisplayMode('block');
18363         this.el.hide();
18364         if (this.over === false) {
18365             return; 
18366         }
18367         if (this.triggers === false) {
18368             return;
18369         }
18370         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18371         var triggers = this.trigger ? this.trigger.split(' ') : [];
18372         Roo.each(triggers, function(trigger) {
18373         
18374             if (trigger == 'click') {
18375                 on_el.on('click', this.toggle, this);
18376             } else if (trigger != 'manual') {
18377                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
18378                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
18379       
18380                 on_el.on(eventIn  ,this.enter, this);
18381                 on_el.on(eventOut, this.leave, this);
18382             }
18383         }, this);
18384         
18385     },
18386     
18387     
18388     // private
18389     timeout : null,
18390     hoverState : null,
18391     
18392     toggle : function () {
18393         this.hoverState == 'in' ? this.leave() : this.enter();
18394     },
18395     
18396     enter : function () {
18397         
18398         clearTimeout(this.timeout);
18399     
18400         this.hoverState = 'in';
18401     
18402         if (!this.delay || !this.delay.show) {
18403             this.show();
18404             return;
18405         }
18406         var _t = this;
18407         this.timeout = setTimeout(function () {
18408             if (_t.hoverState == 'in') {
18409                 _t.show();
18410             }
18411         }, this.delay.show)
18412     },
18413     
18414     leave : function() {
18415         clearTimeout(this.timeout);
18416     
18417         this.hoverState = 'out';
18418     
18419         if (!this.delay || !this.delay.hide) {
18420             this.hide();
18421             return;
18422         }
18423         var _t = this;
18424         this.timeout = setTimeout(function () {
18425             if (_t.hoverState == 'out') {
18426                 _t.hide();
18427             }
18428         }, this.delay.hide)
18429     },
18430     
18431     show : function (on_el)
18432     {
18433         if (!on_el) {
18434             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18435         }
18436         
18437         // set content.
18438         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18439         if (this.html !== false) {
18440             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18441         }
18442         this.el.removeClass([
18443             'fade','top','bottom', 'left', 'right','in',
18444             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18445         ]);
18446         if (!this.title.length) {
18447             this.el.select('.popover-title',true).hide();
18448         }
18449         
18450         var placement = typeof this.placement == 'function' ?
18451             this.placement.call(this, this.el, on_el) :
18452             this.placement;
18453             
18454         var autoToken = /\s?auto?\s?/i;
18455         var autoPlace = autoToken.test(placement);
18456         if (autoPlace) {
18457             placement = placement.replace(autoToken, '') || 'top';
18458         }
18459         
18460         //this.el.detach()
18461         //this.el.setXY([0,0]);
18462         this.el.show();
18463         this.el.dom.style.display='block';
18464         this.el.addClass(placement);
18465         
18466         //this.el.appendTo(on_el);
18467         
18468         var p = this.getPosition();
18469         var box = this.el.getBox();
18470         
18471         if (autoPlace) {
18472             // fixme..
18473         }
18474         var align = Roo.bootstrap.Popover.alignment[placement];
18475         
18476 //        Roo.log(align);
18477         this.el.alignTo(on_el, align[0],align[1]);
18478         //var arrow = this.el.select('.arrow',true).first();
18479         //arrow.set(align[2], 
18480         
18481         this.el.addClass('in');
18482         
18483         
18484         if (this.el.hasClass('fade')) {
18485             // fade it?
18486         }
18487         
18488         this.hoverState = 'in';
18489         
18490         this.fireEvent('show', this);
18491         
18492     },
18493     hide : function()
18494     {
18495         this.el.setXY([0,0]);
18496         this.el.removeClass('in');
18497         this.el.hide();
18498         this.hoverState = null;
18499         
18500         this.fireEvent('hide', this);
18501     }
18502     
18503 });
18504
18505 Roo.bootstrap.Popover.alignment = {
18506     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18507     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18508     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18509     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18510 };
18511
18512  /*
18513  * - LGPL
18514  *
18515  * Progress
18516  * 
18517  */
18518
18519 /**
18520  * @class Roo.bootstrap.Progress
18521  * @extends Roo.bootstrap.Component
18522  * Bootstrap Progress class
18523  * @cfg {Boolean} striped striped of the progress bar
18524  * @cfg {Boolean} active animated of the progress bar
18525  * 
18526  * 
18527  * @constructor
18528  * Create a new Progress
18529  * @param {Object} config The config object
18530  */
18531
18532 Roo.bootstrap.Progress = function(config){
18533     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18534 };
18535
18536 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18537     
18538     striped : false,
18539     active: false,
18540     
18541     getAutoCreate : function(){
18542         var cfg = {
18543             tag: 'div',
18544             cls: 'progress'
18545         };
18546         
18547         
18548         if(this.striped){
18549             cfg.cls += ' progress-striped';
18550         }
18551       
18552         if(this.active){
18553             cfg.cls += ' active';
18554         }
18555         
18556         
18557         return cfg;
18558     }
18559    
18560 });
18561
18562  
18563
18564  /*
18565  * - LGPL
18566  *
18567  * ProgressBar
18568  * 
18569  */
18570
18571 /**
18572  * @class Roo.bootstrap.ProgressBar
18573  * @extends Roo.bootstrap.Component
18574  * Bootstrap ProgressBar class
18575  * @cfg {Number} aria_valuenow aria-value now
18576  * @cfg {Number} aria_valuemin aria-value min
18577  * @cfg {Number} aria_valuemax aria-value max
18578  * @cfg {String} label label for the progress bar
18579  * @cfg {String} panel (success | info | warning | danger )
18580  * @cfg {String} role role of the progress bar
18581  * @cfg {String} sr_only text
18582  * 
18583  * 
18584  * @constructor
18585  * Create a new ProgressBar
18586  * @param {Object} config The config object
18587  */
18588
18589 Roo.bootstrap.ProgressBar = function(config){
18590     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18591 };
18592
18593 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18594     
18595     aria_valuenow : 0,
18596     aria_valuemin : 0,
18597     aria_valuemax : 100,
18598     label : false,
18599     panel : false,
18600     role : false,
18601     sr_only: false,
18602     
18603     getAutoCreate : function()
18604     {
18605         
18606         var cfg = {
18607             tag: 'div',
18608             cls: 'progress-bar',
18609             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18610         };
18611         
18612         if(this.sr_only){
18613             cfg.cn = {
18614                 tag: 'span',
18615                 cls: 'sr-only',
18616                 html: this.sr_only
18617             }
18618         }
18619         
18620         if(this.role){
18621             cfg.role = this.role;
18622         }
18623         
18624         if(this.aria_valuenow){
18625             cfg['aria-valuenow'] = this.aria_valuenow;
18626         }
18627         
18628         if(this.aria_valuemin){
18629             cfg['aria-valuemin'] = this.aria_valuemin;
18630         }
18631         
18632         if(this.aria_valuemax){
18633             cfg['aria-valuemax'] = this.aria_valuemax;
18634         }
18635         
18636         if(this.label && !this.sr_only){
18637             cfg.html = this.label;
18638         }
18639         
18640         if(this.panel){
18641             cfg.cls += ' progress-bar-' + this.panel;
18642         }
18643         
18644         return cfg;
18645     },
18646     
18647     update : function(aria_valuenow)
18648     {
18649         this.aria_valuenow = aria_valuenow;
18650         
18651         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18652     }
18653    
18654 });
18655
18656  
18657
18658  /*
18659  * - LGPL
18660  *
18661  * column
18662  * 
18663  */
18664
18665 /**
18666  * @class Roo.bootstrap.TabGroup
18667  * @extends Roo.bootstrap.Column
18668  * Bootstrap Column class
18669  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18670  * @cfg {Boolean} carousel true to make the group behave like a carousel
18671  * @cfg {Boolean} bullets show bullets for the panels
18672  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18673  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18674  * @cfg {Boolean} showarrow (true|false) show arrow default true
18675  * 
18676  * @constructor
18677  * Create a new TabGroup
18678  * @param {Object} config The config object
18679  */
18680
18681 Roo.bootstrap.TabGroup = function(config){
18682     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18683     if (!this.navId) {
18684         this.navId = Roo.id();
18685     }
18686     this.tabs = [];
18687     Roo.bootstrap.TabGroup.register(this);
18688     
18689 };
18690
18691 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18692     
18693     carousel : false,
18694     transition : false,
18695     bullets : 0,
18696     timer : 0,
18697     autoslide : false,
18698     slideFn : false,
18699     slideOnTouch : false,
18700     showarrow : true,
18701     
18702     getAutoCreate : function()
18703     {
18704         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18705         
18706         cfg.cls += ' tab-content';
18707         
18708         if (this.carousel) {
18709             cfg.cls += ' carousel slide';
18710             
18711             cfg.cn = [{
18712                cls : 'carousel-inner',
18713                cn : []
18714             }];
18715         
18716             if(this.bullets  && !Roo.isTouch){
18717                 
18718                 var bullets = {
18719                     cls : 'carousel-bullets',
18720                     cn : []
18721                 };
18722                
18723                 if(this.bullets_cls){
18724                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18725                 }
18726                 
18727                 bullets.cn.push({
18728                     cls : 'clear'
18729                 });
18730                 
18731                 cfg.cn[0].cn.push(bullets);
18732             }
18733             
18734             if(this.showarrow){
18735                 cfg.cn[0].cn.push({
18736                     tag : 'div',
18737                     class : 'carousel-arrow',
18738                     cn : [
18739                         {
18740                             tag : 'div',
18741                             class : 'carousel-prev',
18742                             cn : [
18743                                 {
18744                                     tag : 'i',
18745                                     class : 'fa fa-chevron-left'
18746                                 }
18747                             ]
18748                         },
18749                         {
18750                             tag : 'div',
18751                             class : 'carousel-next',
18752                             cn : [
18753                                 {
18754                                     tag : 'i',
18755                                     class : 'fa fa-chevron-right'
18756                                 }
18757                             ]
18758                         }
18759                     ]
18760                 });
18761             }
18762             
18763         }
18764         
18765         return cfg;
18766     },
18767     
18768     initEvents:  function()
18769     {
18770 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18771 //            this.el.on("touchstart", this.onTouchStart, this);
18772 //        }
18773         
18774         if(this.autoslide){
18775             var _this = this;
18776             
18777             this.slideFn = window.setInterval(function() {
18778                 _this.showPanelNext();
18779             }, this.timer);
18780         }
18781         
18782         if(this.showarrow){
18783             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18784             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18785         }
18786         
18787         
18788     },
18789     
18790 //    onTouchStart : function(e, el, o)
18791 //    {
18792 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18793 //            return;
18794 //        }
18795 //        
18796 //        this.showPanelNext();
18797 //    },
18798     
18799     
18800     getChildContainer : function()
18801     {
18802         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18803     },
18804     
18805     /**
18806     * register a Navigation item
18807     * @param {Roo.bootstrap.NavItem} the navitem to add
18808     */
18809     register : function(item)
18810     {
18811         this.tabs.push( item);
18812         item.navId = this.navId; // not really needed..
18813         this.addBullet();
18814     
18815     },
18816     
18817     getActivePanel : function()
18818     {
18819         var r = false;
18820         Roo.each(this.tabs, function(t) {
18821             if (t.active) {
18822                 r = t;
18823                 return false;
18824             }
18825             return null;
18826         });
18827         return r;
18828         
18829     },
18830     getPanelByName : function(n)
18831     {
18832         var r = false;
18833         Roo.each(this.tabs, function(t) {
18834             if (t.tabId == n) {
18835                 r = t;
18836                 return false;
18837             }
18838             return null;
18839         });
18840         return r;
18841     },
18842     indexOfPanel : function(p)
18843     {
18844         var r = false;
18845         Roo.each(this.tabs, function(t,i) {
18846             if (t.tabId == p.tabId) {
18847                 r = i;
18848                 return false;
18849             }
18850             return null;
18851         });
18852         return r;
18853     },
18854     /**
18855      * show a specific panel
18856      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18857      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18858      */
18859     showPanel : function (pan)
18860     {
18861         if(this.transition || typeof(pan) == 'undefined'){
18862             Roo.log("waiting for the transitionend");
18863             return false;
18864         }
18865         
18866         if (typeof(pan) == 'number') {
18867             pan = this.tabs[pan];
18868         }
18869         
18870         if (typeof(pan) == 'string') {
18871             pan = this.getPanelByName(pan);
18872         }
18873         
18874         var cur = this.getActivePanel();
18875         
18876         if(!pan || !cur){
18877             Roo.log('pan or acitve pan is undefined');
18878             return false;
18879         }
18880         
18881         if (pan.tabId == this.getActivePanel().tabId) {
18882             return true;
18883         }
18884         
18885         if (false === cur.fireEvent('beforedeactivate')) {
18886             return false;
18887         }
18888         
18889         if(this.bullets > 0 && !Roo.isTouch){
18890             this.setActiveBullet(this.indexOfPanel(pan));
18891         }
18892         
18893         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18894             
18895             //class="carousel-item carousel-item-next carousel-item-left"
18896             
18897             this.transition = true;
18898             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18899             var lr = dir == 'next' ? 'left' : 'right';
18900             pan.el.addClass(dir); // or prev
18901             pan.el.addClass('carousel-item-' + dir); // or prev
18902             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18903             cur.el.addClass(lr); // or right
18904             pan.el.addClass(lr);
18905             cur.el.addClass('carousel-item-' +lr); // or right
18906             pan.el.addClass('carousel-item-' +lr);
18907             
18908             
18909             var _this = this;
18910             cur.el.on('transitionend', function() {
18911                 Roo.log("trans end?");
18912                 
18913                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18914                 pan.setActive(true);
18915                 
18916                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18917                 cur.setActive(false);
18918                 
18919                 _this.transition = false;
18920                 
18921             }, this, { single:  true } );
18922             
18923             return true;
18924         }
18925         
18926         cur.setActive(false);
18927         pan.setActive(true);
18928         
18929         return true;
18930         
18931     },
18932     showPanelNext : function()
18933     {
18934         var i = this.indexOfPanel(this.getActivePanel());
18935         
18936         if (i >= this.tabs.length - 1 && !this.autoslide) {
18937             return;
18938         }
18939         
18940         if (i >= this.tabs.length - 1 && this.autoslide) {
18941             i = -1;
18942         }
18943         
18944         this.showPanel(this.tabs[i+1]);
18945     },
18946     
18947     showPanelPrev : function()
18948     {
18949         var i = this.indexOfPanel(this.getActivePanel());
18950         
18951         if (i  < 1 && !this.autoslide) {
18952             return;
18953         }
18954         
18955         if (i < 1 && this.autoslide) {
18956             i = this.tabs.length;
18957         }
18958         
18959         this.showPanel(this.tabs[i-1]);
18960     },
18961     
18962     
18963     addBullet: function()
18964     {
18965         if(!this.bullets || Roo.isTouch){
18966             return;
18967         }
18968         var ctr = this.el.select('.carousel-bullets',true).first();
18969         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18970         var bullet = ctr.createChild({
18971             cls : 'bullet bullet-' + i
18972         },ctr.dom.lastChild);
18973         
18974         
18975         var _this = this;
18976         
18977         bullet.on('click', (function(e, el, o, ii, t){
18978
18979             e.preventDefault();
18980
18981             this.showPanel(ii);
18982
18983             if(this.autoslide && this.slideFn){
18984                 clearInterval(this.slideFn);
18985                 this.slideFn = window.setInterval(function() {
18986                     _this.showPanelNext();
18987                 }, this.timer);
18988             }
18989
18990         }).createDelegate(this, [i, bullet], true));
18991                 
18992         
18993     },
18994      
18995     setActiveBullet : function(i)
18996     {
18997         if(Roo.isTouch){
18998             return;
18999         }
19000         
19001         Roo.each(this.el.select('.bullet', true).elements, function(el){
19002             el.removeClass('selected');
19003         });
19004
19005         var bullet = this.el.select('.bullet-' + i, true).first();
19006         
19007         if(!bullet){
19008             return;
19009         }
19010         
19011         bullet.addClass('selected');
19012     }
19013     
19014     
19015   
19016 });
19017
19018  
19019
19020  
19021  
19022 Roo.apply(Roo.bootstrap.TabGroup, {
19023     
19024     groups: {},
19025      /**
19026     * register a Navigation Group
19027     * @param {Roo.bootstrap.NavGroup} the navgroup to add
19028     */
19029     register : function(navgrp)
19030     {
19031         this.groups[navgrp.navId] = navgrp;
19032         
19033     },
19034     /**
19035     * fetch a Navigation Group based on the navigation ID
19036     * if one does not exist , it will get created.
19037     * @param {string} the navgroup to add
19038     * @returns {Roo.bootstrap.NavGroup} the navgroup 
19039     */
19040     get: function(navId) {
19041         if (typeof(this.groups[navId]) == 'undefined') {
19042             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
19043         }
19044         return this.groups[navId] ;
19045     }
19046     
19047     
19048     
19049 });
19050
19051  /*
19052  * - LGPL
19053  *
19054  * TabPanel
19055  * 
19056  */
19057
19058 /**
19059  * @class Roo.bootstrap.TabPanel
19060  * @extends Roo.bootstrap.Component
19061  * Bootstrap TabPanel class
19062  * @cfg {Boolean} active panel active
19063  * @cfg {String} html panel content
19064  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
19065  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
19066  * @cfg {String} href click to link..
19067  * 
19068  * 
19069  * @constructor
19070  * Create a new TabPanel
19071  * @param {Object} config The config object
19072  */
19073
19074 Roo.bootstrap.TabPanel = function(config){
19075     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
19076     this.addEvents({
19077         /**
19078              * @event changed
19079              * Fires when the active status changes
19080              * @param {Roo.bootstrap.TabPanel} this
19081              * @param {Boolean} state the new state
19082             
19083          */
19084         'changed': true,
19085         /**
19086              * @event beforedeactivate
19087              * Fires before a tab is de-activated - can be used to do validation on a form.
19088              * @param {Roo.bootstrap.TabPanel} this
19089              * @return {Boolean} false if there is an error
19090             
19091          */
19092         'beforedeactivate': true
19093      });
19094     
19095     this.tabId = this.tabId || Roo.id();
19096   
19097 };
19098
19099 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
19100     
19101     active: false,
19102     html: false,
19103     tabId: false,
19104     navId : false,
19105     href : '',
19106     
19107     getAutoCreate : function(){
19108         
19109         
19110         var cfg = {
19111             tag: 'div',
19112             // item is needed for carousel - not sure if it has any effect otherwise
19113             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
19114             html: this.html || ''
19115         };
19116         
19117         if(this.active){
19118             cfg.cls += ' active';
19119         }
19120         
19121         if(this.tabId){
19122             cfg.tabId = this.tabId;
19123         }
19124         
19125         
19126         
19127         return cfg;
19128     },
19129     
19130     initEvents:  function()
19131     {
19132         var p = this.parent();
19133         
19134         this.navId = this.navId || p.navId;
19135         
19136         if (typeof(this.navId) != 'undefined') {
19137             // not really needed.. but just in case.. parent should be a NavGroup.
19138             var tg = Roo.bootstrap.TabGroup.get(this.navId);
19139             
19140             tg.register(this);
19141             
19142             var i = tg.tabs.length - 1;
19143             
19144             if(this.active && tg.bullets > 0 && i < tg.bullets){
19145                 tg.setActiveBullet(i);
19146             }
19147         }
19148         
19149         this.el.on('click', this.onClick, this);
19150         
19151         if(Roo.isTouch){
19152             this.el.on("touchstart", this.onTouchStart, this);
19153             this.el.on("touchmove", this.onTouchMove, this);
19154             this.el.on("touchend", this.onTouchEnd, this);
19155         }
19156         
19157     },
19158     
19159     onRender : function(ct, position)
19160     {
19161         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
19162     },
19163     
19164     setActive : function(state)
19165     {
19166         Roo.log("panel - set active " + this.tabId + "=" + state);
19167         
19168         this.active = state;
19169         if (!state) {
19170             this.el.removeClass('active');
19171             
19172         } else  if (!this.el.hasClass('active')) {
19173             this.el.addClass('active');
19174         }
19175         
19176         this.fireEvent('changed', this, state);
19177     },
19178     
19179     onClick : function(e)
19180     {
19181         e.preventDefault();
19182         
19183         if(!this.href.length){
19184             return;
19185         }
19186         
19187         window.location.href = this.href;
19188     },
19189     
19190     startX : 0,
19191     startY : 0,
19192     endX : 0,
19193     endY : 0,
19194     swiping : false,
19195     
19196     onTouchStart : function(e)
19197     {
19198         this.swiping = false;
19199         
19200         this.startX = e.browserEvent.touches[0].clientX;
19201         this.startY = e.browserEvent.touches[0].clientY;
19202     },
19203     
19204     onTouchMove : function(e)
19205     {
19206         this.swiping = true;
19207         
19208         this.endX = e.browserEvent.touches[0].clientX;
19209         this.endY = e.browserEvent.touches[0].clientY;
19210     },
19211     
19212     onTouchEnd : function(e)
19213     {
19214         if(!this.swiping){
19215             this.onClick(e);
19216             return;
19217         }
19218         
19219         var tabGroup = this.parent();
19220         
19221         if(this.endX > this.startX){ // swiping right
19222             tabGroup.showPanelPrev();
19223             return;
19224         }
19225         
19226         if(this.startX > this.endX){ // swiping left
19227             tabGroup.showPanelNext();
19228             return;
19229         }
19230     }
19231     
19232     
19233 });
19234  
19235
19236  
19237
19238  /*
19239  * - LGPL
19240  *
19241  * DateField
19242  * 
19243  */
19244
19245 /**
19246  * @class Roo.bootstrap.DateField
19247  * @extends Roo.bootstrap.Input
19248  * Bootstrap DateField class
19249  * @cfg {Number} weekStart default 0
19250  * @cfg {String} viewMode default empty, (months|years)
19251  * @cfg {String} minViewMode default empty, (months|years)
19252  * @cfg {Number} startDate default -Infinity
19253  * @cfg {Number} endDate default Infinity
19254  * @cfg {Boolean} todayHighlight default false
19255  * @cfg {Boolean} todayBtn default false
19256  * @cfg {Boolean} calendarWeeks default false
19257  * @cfg {Object} daysOfWeekDisabled default empty
19258  * @cfg {Boolean} singleMode default false (true | false)
19259  * 
19260  * @cfg {Boolean} keyboardNavigation default true
19261  * @cfg {String} language default en
19262  * 
19263  * @constructor
19264  * Create a new DateField
19265  * @param {Object} config The config object
19266  */
19267
19268 Roo.bootstrap.DateField = function(config){
19269     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
19270      this.addEvents({
19271             /**
19272              * @event show
19273              * Fires when this field show.
19274              * @param {Roo.bootstrap.DateField} this
19275              * @param {Mixed} date The date value
19276              */
19277             show : true,
19278             /**
19279              * @event show
19280              * Fires when this field hide.
19281              * @param {Roo.bootstrap.DateField} this
19282              * @param {Mixed} date The date value
19283              */
19284             hide : true,
19285             /**
19286              * @event select
19287              * Fires when select a date.
19288              * @param {Roo.bootstrap.DateField} this
19289              * @param {Mixed} date The date value
19290              */
19291             select : true,
19292             /**
19293              * @event beforeselect
19294              * Fires when before select a date.
19295              * @param {Roo.bootstrap.DateField} this
19296              * @param {Mixed} date The date value
19297              */
19298             beforeselect : true
19299         });
19300 };
19301
19302 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
19303     
19304     /**
19305      * @cfg {String} format
19306      * The default date format string which can be overriden for localization support.  The format must be
19307      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
19308      */
19309     format : "m/d/y",
19310     /**
19311      * @cfg {String} altFormats
19312      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
19313      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
19314      */
19315     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
19316     
19317     weekStart : 0,
19318     
19319     viewMode : '',
19320     
19321     minViewMode : '',
19322     
19323     todayHighlight : false,
19324     
19325     todayBtn: false,
19326     
19327     language: 'en',
19328     
19329     keyboardNavigation: true,
19330     
19331     calendarWeeks: false,
19332     
19333     startDate: -Infinity,
19334     
19335     endDate: Infinity,
19336     
19337     daysOfWeekDisabled: [],
19338     
19339     _events: [],
19340     
19341     singleMode : false,
19342     
19343     UTCDate: function()
19344     {
19345         return new Date(Date.UTC.apply(Date, arguments));
19346     },
19347     
19348     UTCToday: function()
19349     {
19350         var today = new Date();
19351         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
19352     },
19353     
19354     getDate: function() {
19355             var d = this.getUTCDate();
19356             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
19357     },
19358     
19359     getUTCDate: function() {
19360             return this.date;
19361     },
19362     
19363     setDate: function(d) {
19364             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
19365     },
19366     
19367     setUTCDate: function(d) {
19368             this.date = d;
19369             this.setValue(this.formatDate(this.date));
19370     },
19371         
19372     onRender: function(ct, position)
19373     {
19374         
19375         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
19376         
19377         this.language = this.language || 'en';
19378         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
19379         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
19380         
19381         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
19382         this.format = this.format || 'm/d/y';
19383         this.isInline = false;
19384         this.isInput = true;
19385         this.component = this.el.select('.add-on', true).first() || false;
19386         this.component = (this.component && this.component.length === 0) ? false : this.component;
19387         this.hasInput = this.component && this.inputEl().length;
19388         
19389         if (typeof(this.minViewMode === 'string')) {
19390             switch (this.minViewMode) {
19391                 case 'months':
19392                     this.minViewMode = 1;
19393                     break;
19394                 case 'years':
19395                     this.minViewMode = 2;
19396                     break;
19397                 default:
19398                     this.minViewMode = 0;
19399                     break;
19400             }
19401         }
19402         
19403         if (typeof(this.viewMode === 'string')) {
19404             switch (this.viewMode) {
19405                 case 'months':
19406                     this.viewMode = 1;
19407                     break;
19408                 case 'years':
19409                     this.viewMode = 2;
19410                     break;
19411                 default:
19412                     this.viewMode = 0;
19413                     break;
19414             }
19415         }
19416                 
19417         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19418         
19419 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19420         
19421         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19422         
19423         this.picker().on('mousedown', this.onMousedown, this);
19424         this.picker().on('click', this.onClick, this);
19425         
19426         this.picker().addClass('datepicker-dropdown');
19427         
19428         this.startViewMode = this.viewMode;
19429         
19430         if(this.singleMode){
19431             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19432                 v.setVisibilityMode(Roo.Element.DISPLAY);
19433                 v.hide();
19434             });
19435             
19436             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19437                 v.setStyle('width', '189px');
19438             });
19439         }
19440         
19441         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19442             if(!this.calendarWeeks){
19443                 v.remove();
19444                 return;
19445             }
19446             
19447             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19448             v.attr('colspan', function(i, val){
19449                 return parseInt(val) + 1;
19450             });
19451         });
19452                         
19453         
19454         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19455         
19456         this.setStartDate(this.startDate);
19457         this.setEndDate(this.endDate);
19458         
19459         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19460         
19461         this.fillDow();
19462         this.fillMonths();
19463         this.update();
19464         this.showMode();
19465         
19466         if(this.isInline) {
19467             this.showPopup();
19468         }
19469     },
19470     
19471     picker : function()
19472     {
19473         return this.pickerEl;
19474 //        return this.el.select('.datepicker', true).first();
19475     },
19476     
19477     fillDow: function()
19478     {
19479         var dowCnt = this.weekStart;
19480         
19481         var dow = {
19482             tag: 'tr',
19483             cn: [
19484                 
19485             ]
19486         };
19487         
19488         if(this.calendarWeeks){
19489             dow.cn.push({
19490                 tag: 'th',
19491                 cls: 'cw',
19492                 html: '&nbsp;'
19493             })
19494         }
19495         
19496         while (dowCnt < this.weekStart + 7) {
19497             dow.cn.push({
19498                 tag: 'th',
19499                 cls: 'dow',
19500                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19501             });
19502         }
19503         
19504         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19505     },
19506     
19507     fillMonths: function()
19508     {    
19509         var i = 0;
19510         var months = this.picker().select('>.datepicker-months td', true).first();
19511         
19512         months.dom.innerHTML = '';
19513         
19514         while (i < 12) {
19515             var month = {
19516                 tag: 'span',
19517                 cls: 'month',
19518                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19519             };
19520             
19521             months.createChild(month);
19522         }
19523         
19524     },
19525     
19526     update: function()
19527     {
19528         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;
19529         
19530         if (this.date < this.startDate) {
19531             this.viewDate = new Date(this.startDate);
19532         } else if (this.date > this.endDate) {
19533             this.viewDate = new Date(this.endDate);
19534         } else {
19535             this.viewDate = new Date(this.date);
19536         }
19537         
19538         this.fill();
19539     },
19540     
19541     fill: function() 
19542     {
19543         var d = new Date(this.viewDate),
19544                 year = d.getUTCFullYear(),
19545                 month = d.getUTCMonth(),
19546                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19547                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19548                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19549                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19550                 currentDate = this.date && this.date.valueOf(),
19551                 today = this.UTCToday();
19552         
19553         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19554         
19555 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19556         
19557 //        this.picker.select('>tfoot th.today').
19558 //                                              .text(dates[this.language].today)
19559 //                                              .toggle(this.todayBtn !== false);
19560     
19561         this.updateNavArrows();
19562         this.fillMonths();
19563                                                 
19564         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19565         
19566         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19567          
19568         prevMonth.setUTCDate(day);
19569         
19570         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19571         
19572         var nextMonth = new Date(prevMonth);
19573         
19574         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19575         
19576         nextMonth = nextMonth.valueOf();
19577         
19578         var fillMonths = false;
19579         
19580         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19581         
19582         while(prevMonth.valueOf() <= nextMonth) {
19583             var clsName = '';
19584             
19585             if (prevMonth.getUTCDay() === this.weekStart) {
19586                 if(fillMonths){
19587                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19588                 }
19589                     
19590                 fillMonths = {
19591                     tag: 'tr',
19592                     cn: []
19593                 };
19594                 
19595                 if(this.calendarWeeks){
19596                     // ISO 8601: First week contains first thursday.
19597                     // ISO also states week starts on Monday, but we can be more abstract here.
19598                     var
19599                     // Start of current week: based on weekstart/current date
19600                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19601                     // Thursday of this week
19602                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19603                     // First Thursday of year, year from thursday
19604                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19605                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19606                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19607                     
19608                     fillMonths.cn.push({
19609                         tag: 'td',
19610                         cls: 'cw',
19611                         html: calWeek
19612                     });
19613                 }
19614             }
19615             
19616             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19617                 clsName += ' old';
19618             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19619                 clsName += ' new';
19620             }
19621             if (this.todayHighlight &&
19622                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19623                 prevMonth.getUTCMonth() == today.getMonth() &&
19624                 prevMonth.getUTCDate() == today.getDate()) {
19625                 clsName += ' today';
19626             }
19627             
19628             if (currentDate && prevMonth.valueOf() === currentDate) {
19629                 clsName += ' active';
19630             }
19631             
19632             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19633                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19634                     clsName += ' disabled';
19635             }
19636             
19637             fillMonths.cn.push({
19638                 tag: 'td',
19639                 cls: 'day ' + clsName,
19640                 html: prevMonth.getDate()
19641             });
19642             
19643             prevMonth.setDate(prevMonth.getDate()+1);
19644         }
19645           
19646         var currentYear = this.date && this.date.getUTCFullYear();
19647         var currentMonth = this.date && this.date.getUTCMonth();
19648         
19649         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19650         
19651         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19652             v.removeClass('active');
19653             
19654             if(currentYear === year && k === currentMonth){
19655                 v.addClass('active');
19656             }
19657             
19658             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19659                 v.addClass('disabled');
19660             }
19661             
19662         });
19663         
19664         
19665         year = parseInt(year/10, 10) * 10;
19666         
19667         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19668         
19669         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19670         
19671         year -= 1;
19672         for (var i = -1; i < 11; i++) {
19673             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19674                 tag: 'span',
19675                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19676                 html: year
19677             });
19678             
19679             year += 1;
19680         }
19681     },
19682     
19683     showMode: function(dir) 
19684     {
19685         if (dir) {
19686             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19687         }
19688         
19689         Roo.each(this.picker().select('>div',true).elements, function(v){
19690             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19691             v.hide();
19692         });
19693         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19694     },
19695     
19696     place: function()
19697     {
19698         if(this.isInline) {
19699             return;
19700         }
19701         
19702         this.picker().removeClass(['bottom', 'top']);
19703         
19704         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19705             /*
19706              * place to the top of element!
19707              *
19708              */
19709             
19710             this.picker().addClass('top');
19711             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19712             
19713             return;
19714         }
19715         
19716         this.picker().addClass('bottom');
19717         
19718         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19719     },
19720     
19721     parseDate : function(value)
19722     {
19723         if(!value || value instanceof Date){
19724             return value;
19725         }
19726         var v = Date.parseDate(value, this.format);
19727         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19728             v = Date.parseDate(value, 'Y-m-d');
19729         }
19730         if(!v && this.altFormats){
19731             if(!this.altFormatsArray){
19732                 this.altFormatsArray = this.altFormats.split("|");
19733             }
19734             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19735                 v = Date.parseDate(value, this.altFormatsArray[i]);
19736             }
19737         }
19738         return v;
19739     },
19740     
19741     formatDate : function(date, fmt)
19742     {   
19743         return (!date || !(date instanceof Date)) ?
19744         date : date.dateFormat(fmt || this.format);
19745     },
19746     
19747     onFocus : function()
19748     {
19749         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19750         this.showPopup();
19751     },
19752     
19753     onBlur : function()
19754     {
19755         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19756         
19757         var d = this.inputEl().getValue();
19758         
19759         this.setValue(d);
19760                 
19761         this.hidePopup();
19762     },
19763     
19764     showPopup : function()
19765     {
19766         this.picker().show();
19767         this.update();
19768         this.place();
19769         
19770         this.fireEvent('showpopup', this, this.date);
19771     },
19772     
19773     hidePopup : function()
19774     {
19775         if(this.isInline) {
19776             return;
19777         }
19778         this.picker().hide();
19779         this.viewMode = this.startViewMode;
19780         this.showMode();
19781         
19782         this.fireEvent('hidepopup', this, this.date);
19783         
19784     },
19785     
19786     onMousedown: function(e)
19787     {
19788         e.stopPropagation();
19789         e.preventDefault();
19790     },
19791     
19792     keyup: function(e)
19793     {
19794         Roo.bootstrap.DateField.superclass.keyup.call(this);
19795         this.update();
19796     },
19797
19798     setValue: function(v)
19799     {
19800         if(this.fireEvent('beforeselect', this, v) !== false){
19801             var d = new Date(this.parseDate(v) ).clearTime();
19802         
19803             if(isNaN(d.getTime())){
19804                 this.date = this.viewDate = '';
19805                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19806                 return;
19807             }
19808
19809             v = this.formatDate(d);
19810
19811             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19812
19813             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19814
19815             this.update();
19816
19817             this.fireEvent('select', this, this.date);
19818         }
19819     },
19820     
19821     getValue: function()
19822     {
19823         return this.formatDate(this.date);
19824     },
19825     
19826     fireKey: function(e)
19827     {
19828         if (!this.picker().isVisible()){
19829             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19830                 this.showPopup();
19831             }
19832             return;
19833         }
19834         
19835         var dateChanged = false,
19836         dir, day, month,
19837         newDate, newViewDate;
19838         
19839         switch(e.keyCode){
19840             case 27: // escape
19841                 this.hidePopup();
19842                 e.preventDefault();
19843                 break;
19844             case 37: // left
19845             case 39: // right
19846                 if (!this.keyboardNavigation) {
19847                     break;
19848                 }
19849                 dir = e.keyCode == 37 ? -1 : 1;
19850                 
19851                 if (e.ctrlKey){
19852                     newDate = this.moveYear(this.date, dir);
19853                     newViewDate = this.moveYear(this.viewDate, dir);
19854                 } else if (e.shiftKey){
19855                     newDate = this.moveMonth(this.date, dir);
19856                     newViewDate = this.moveMonth(this.viewDate, dir);
19857                 } else {
19858                     newDate = new Date(this.date);
19859                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19860                     newViewDate = new Date(this.viewDate);
19861                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19862                 }
19863                 if (this.dateWithinRange(newDate)){
19864                     this.date = newDate;
19865                     this.viewDate = newViewDate;
19866                     this.setValue(this.formatDate(this.date));
19867 //                    this.update();
19868                     e.preventDefault();
19869                     dateChanged = true;
19870                 }
19871                 break;
19872             case 38: // up
19873             case 40: // down
19874                 if (!this.keyboardNavigation) {
19875                     break;
19876                 }
19877                 dir = e.keyCode == 38 ? -1 : 1;
19878                 if (e.ctrlKey){
19879                     newDate = this.moveYear(this.date, dir);
19880                     newViewDate = this.moveYear(this.viewDate, dir);
19881                 } else if (e.shiftKey){
19882                     newDate = this.moveMonth(this.date, dir);
19883                     newViewDate = this.moveMonth(this.viewDate, dir);
19884                 } else {
19885                     newDate = new Date(this.date);
19886                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19887                     newViewDate = new Date(this.viewDate);
19888                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19889                 }
19890                 if (this.dateWithinRange(newDate)){
19891                     this.date = newDate;
19892                     this.viewDate = newViewDate;
19893                     this.setValue(this.formatDate(this.date));
19894 //                    this.update();
19895                     e.preventDefault();
19896                     dateChanged = true;
19897                 }
19898                 break;
19899             case 13: // enter
19900                 this.setValue(this.formatDate(this.date));
19901                 this.hidePopup();
19902                 e.preventDefault();
19903                 break;
19904             case 9: // tab
19905                 this.setValue(this.formatDate(this.date));
19906                 this.hidePopup();
19907                 break;
19908             case 16: // shift
19909             case 17: // ctrl
19910             case 18: // alt
19911                 break;
19912             default :
19913                 this.hidePopup();
19914                 
19915         }
19916     },
19917     
19918     
19919     onClick: function(e) 
19920     {
19921         e.stopPropagation();
19922         e.preventDefault();
19923         
19924         var target = e.getTarget();
19925         
19926         if(target.nodeName.toLowerCase() === 'i'){
19927             target = Roo.get(target).dom.parentNode;
19928         }
19929         
19930         var nodeName = target.nodeName;
19931         var className = target.className;
19932         var html = target.innerHTML;
19933         //Roo.log(nodeName);
19934         
19935         switch(nodeName.toLowerCase()) {
19936             case 'th':
19937                 switch(className) {
19938                     case 'switch':
19939                         this.showMode(1);
19940                         break;
19941                     case 'prev':
19942                     case 'next':
19943                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19944                         switch(this.viewMode){
19945                                 case 0:
19946                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19947                                         break;
19948                                 case 1:
19949                                 case 2:
19950                                         this.viewDate = this.moveYear(this.viewDate, dir);
19951                                         break;
19952                         }
19953                         this.fill();
19954                         break;
19955                     case 'today':
19956                         var date = new Date();
19957                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19958 //                        this.fill()
19959                         this.setValue(this.formatDate(this.date));
19960                         
19961                         this.hidePopup();
19962                         break;
19963                 }
19964                 break;
19965             case 'span':
19966                 if (className.indexOf('disabled') < 0) {
19967                     this.viewDate.setUTCDate(1);
19968                     if (className.indexOf('month') > -1) {
19969                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19970                     } else {
19971                         var year = parseInt(html, 10) || 0;
19972                         this.viewDate.setUTCFullYear(year);
19973                         
19974                     }
19975                     
19976                     if(this.singleMode){
19977                         this.setValue(this.formatDate(this.viewDate));
19978                         this.hidePopup();
19979                         return;
19980                     }
19981                     
19982                     this.showMode(-1);
19983                     this.fill();
19984                 }
19985                 break;
19986                 
19987             case 'td':
19988                 //Roo.log(className);
19989                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19990                     var day = parseInt(html, 10) || 1;
19991                     var year = this.viewDate.getUTCFullYear(),
19992                         month = this.viewDate.getUTCMonth();
19993
19994                     if (className.indexOf('old') > -1) {
19995                         if(month === 0 ){
19996                             month = 11;
19997                             year -= 1;
19998                         }else{
19999                             month -= 1;
20000                         }
20001                     } else if (className.indexOf('new') > -1) {
20002                         if (month == 11) {
20003                             month = 0;
20004                             year += 1;
20005                         } else {
20006                             month += 1;
20007                         }
20008                     }
20009                     //Roo.log([year,month,day]);
20010                     this.date = this.UTCDate(year, month, day,0,0,0,0);
20011                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
20012 //                    this.fill();
20013                     //Roo.log(this.formatDate(this.date));
20014                     this.setValue(this.formatDate(this.date));
20015                     this.hidePopup();
20016                 }
20017                 break;
20018         }
20019     },
20020     
20021     setStartDate: function(startDate)
20022     {
20023         this.startDate = startDate || -Infinity;
20024         if (this.startDate !== -Infinity) {
20025             this.startDate = this.parseDate(this.startDate);
20026         }
20027         this.update();
20028         this.updateNavArrows();
20029     },
20030
20031     setEndDate: function(endDate)
20032     {
20033         this.endDate = endDate || Infinity;
20034         if (this.endDate !== Infinity) {
20035             this.endDate = this.parseDate(this.endDate);
20036         }
20037         this.update();
20038         this.updateNavArrows();
20039     },
20040     
20041     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
20042     {
20043         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
20044         if (typeof(this.daysOfWeekDisabled) !== 'object') {
20045             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
20046         }
20047         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
20048             return parseInt(d, 10);
20049         });
20050         this.update();
20051         this.updateNavArrows();
20052     },
20053     
20054     updateNavArrows: function() 
20055     {
20056         if(this.singleMode){
20057             return;
20058         }
20059         
20060         var d = new Date(this.viewDate),
20061         year = d.getUTCFullYear(),
20062         month = d.getUTCMonth();
20063         
20064         Roo.each(this.picker().select('.prev', true).elements, function(v){
20065             v.show();
20066             switch (this.viewMode) {
20067                 case 0:
20068
20069                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
20070                         v.hide();
20071                     }
20072                     break;
20073                 case 1:
20074                 case 2:
20075                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
20076                         v.hide();
20077                     }
20078                     break;
20079             }
20080         });
20081         
20082         Roo.each(this.picker().select('.next', true).elements, function(v){
20083             v.show();
20084             switch (this.viewMode) {
20085                 case 0:
20086
20087                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
20088                         v.hide();
20089                     }
20090                     break;
20091                 case 1:
20092                 case 2:
20093                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
20094                         v.hide();
20095                     }
20096                     break;
20097             }
20098         })
20099     },
20100     
20101     moveMonth: function(date, dir)
20102     {
20103         if (!dir) {
20104             return date;
20105         }
20106         var new_date = new Date(date.valueOf()),
20107         day = new_date.getUTCDate(),
20108         month = new_date.getUTCMonth(),
20109         mag = Math.abs(dir),
20110         new_month, test;
20111         dir = dir > 0 ? 1 : -1;
20112         if (mag == 1){
20113             test = dir == -1
20114             // If going back one month, make sure month is not current month
20115             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
20116             ? function(){
20117                 return new_date.getUTCMonth() == month;
20118             }
20119             // If going forward one month, make sure month is as expected
20120             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
20121             : function(){
20122                 return new_date.getUTCMonth() != new_month;
20123             };
20124             new_month = month + dir;
20125             new_date.setUTCMonth(new_month);
20126             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
20127             if (new_month < 0 || new_month > 11) {
20128                 new_month = (new_month + 12) % 12;
20129             }
20130         } else {
20131             // For magnitudes >1, move one month at a time...
20132             for (var i=0; i<mag; i++) {
20133                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
20134                 new_date = this.moveMonth(new_date, dir);
20135             }
20136             // ...then reset the day, keeping it in the new month
20137             new_month = new_date.getUTCMonth();
20138             new_date.setUTCDate(day);
20139             test = function(){
20140                 return new_month != new_date.getUTCMonth();
20141             };
20142         }
20143         // Common date-resetting loop -- if date is beyond end of month, make it
20144         // end of month
20145         while (test()){
20146             new_date.setUTCDate(--day);
20147             new_date.setUTCMonth(new_month);
20148         }
20149         return new_date;
20150     },
20151
20152     moveYear: function(date, dir)
20153     {
20154         return this.moveMonth(date, dir*12);
20155     },
20156
20157     dateWithinRange: function(date)
20158     {
20159         return date >= this.startDate && date <= this.endDate;
20160     },
20161
20162     
20163     remove: function() 
20164     {
20165         this.picker().remove();
20166     },
20167     
20168     validateValue : function(value)
20169     {
20170         if(this.getVisibilityEl().hasClass('hidden')){
20171             return true;
20172         }
20173         
20174         if(value.length < 1)  {
20175             if(this.allowBlank){
20176                 return true;
20177             }
20178             return false;
20179         }
20180         
20181         if(value.length < this.minLength){
20182             return false;
20183         }
20184         if(value.length > this.maxLength){
20185             return false;
20186         }
20187         if(this.vtype){
20188             var vt = Roo.form.VTypes;
20189             if(!vt[this.vtype](value, this)){
20190                 return false;
20191             }
20192         }
20193         if(typeof this.validator == "function"){
20194             var msg = this.validator(value);
20195             if(msg !== true){
20196                 return false;
20197             }
20198         }
20199         
20200         if(this.regex && !this.regex.test(value)){
20201             return false;
20202         }
20203         
20204         if(typeof(this.parseDate(value)) == 'undefined'){
20205             return false;
20206         }
20207         
20208         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
20209             return false;
20210         }      
20211         
20212         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
20213             return false;
20214         } 
20215         
20216         
20217         return true;
20218     },
20219     
20220     reset : function()
20221     {
20222         this.date = this.viewDate = '';
20223         
20224         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20225     }
20226    
20227 });
20228
20229 Roo.apply(Roo.bootstrap.DateField,  {
20230     
20231     head : {
20232         tag: 'thead',
20233         cn: [
20234         {
20235             tag: 'tr',
20236             cn: [
20237             {
20238                 tag: 'th',
20239                 cls: 'prev',
20240                 html: '<i class="fa fa-arrow-left"/>'
20241             },
20242             {
20243                 tag: 'th',
20244                 cls: 'switch',
20245                 colspan: '5'
20246             },
20247             {
20248                 tag: 'th',
20249                 cls: 'next',
20250                 html: '<i class="fa fa-arrow-right"/>'
20251             }
20252
20253             ]
20254         }
20255         ]
20256     },
20257     
20258     content : {
20259         tag: 'tbody',
20260         cn: [
20261         {
20262             tag: 'tr',
20263             cn: [
20264             {
20265                 tag: 'td',
20266                 colspan: '7'
20267             }
20268             ]
20269         }
20270         ]
20271     },
20272     
20273     footer : {
20274         tag: 'tfoot',
20275         cn: [
20276         {
20277             tag: 'tr',
20278             cn: [
20279             {
20280                 tag: 'th',
20281                 colspan: '7',
20282                 cls: 'today'
20283             }
20284                     
20285             ]
20286         }
20287         ]
20288     },
20289     
20290     dates:{
20291         en: {
20292             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
20293             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
20294             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
20295             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20296             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
20297             today: "Today"
20298         }
20299     },
20300     
20301     modes: [
20302     {
20303         clsName: 'days',
20304         navFnc: 'Month',
20305         navStep: 1
20306     },
20307     {
20308         clsName: 'months',
20309         navFnc: 'FullYear',
20310         navStep: 1
20311     },
20312     {
20313         clsName: 'years',
20314         navFnc: 'FullYear',
20315         navStep: 10
20316     }]
20317 });
20318
20319 Roo.apply(Roo.bootstrap.DateField,  {
20320   
20321     template : {
20322         tag: 'div',
20323         cls: 'datepicker dropdown-menu roo-dynamic',
20324         cn: [
20325         {
20326             tag: 'div',
20327             cls: 'datepicker-days',
20328             cn: [
20329             {
20330                 tag: 'table',
20331                 cls: 'table-condensed',
20332                 cn:[
20333                 Roo.bootstrap.DateField.head,
20334                 {
20335                     tag: 'tbody'
20336                 },
20337                 Roo.bootstrap.DateField.footer
20338                 ]
20339             }
20340             ]
20341         },
20342         {
20343             tag: 'div',
20344             cls: 'datepicker-months',
20345             cn: [
20346             {
20347                 tag: 'table',
20348                 cls: 'table-condensed',
20349                 cn:[
20350                 Roo.bootstrap.DateField.head,
20351                 Roo.bootstrap.DateField.content,
20352                 Roo.bootstrap.DateField.footer
20353                 ]
20354             }
20355             ]
20356         },
20357         {
20358             tag: 'div',
20359             cls: 'datepicker-years',
20360             cn: [
20361             {
20362                 tag: 'table',
20363                 cls: 'table-condensed',
20364                 cn:[
20365                 Roo.bootstrap.DateField.head,
20366                 Roo.bootstrap.DateField.content,
20367                 Roo.bootstrap.DateField.footer
20368                 ]
20369             }
20370             ]
20371         }
20372         ]
20373     }
20374 });
20375
20376  
20377
20378  /*
20379  * - LGPL
20380  *
20381  * TimeField
20382  * 
20383  */
20384
20385 /**
20386  * @class Roo.bootstrap.TimeField
20387  * @extends Roo.bootstrap.Input
20388  * Bootstrap DateField class
20389  * 
20390  * 
20391  * @constructor
20392  * Create a new TimeField
20393  * @param {Object} config The config object
20394  */
20395
20396 Roo.bootstrap.TimeField = function(config){
20397     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
20398     this.addEvents({
20399             /**
20400              * @event show
20401              * Fires when this field show.
20402              * @param {Roo.bootstrap.DateField} thisthis
20403              * @param {Mixed} date The date value
20404              */
20405             show : true,
20406             /**
20407              * @event show
20408              * Fires when this field hide.
20409              * @param {Roo.bootstrap.DateField} this
20410              * @param {Mixed} date The date value
20411              */
20412             hide : true,
20413             /**
20414              * @event select
20415              * Fires when select a date.
20416              * @param {Roo.bootstrap.DateField} this
20417              * @param {Mixed} date The date value
20418              */
20419             select : true
20420         });
20421 };
20422
20423 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
20424     
20425     /**
20426      * @cfg {String} format
20427      * The default time format string which can be overriden for localization support.  The format must be
20428      * valid according to {@link Date#parseDate} (defaults to 'H:i').
20429      */
20430     format : "H:i",
20431        
20432     onRender: function(ct, position)
20433     {
20434         
20435         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20436                 
20437         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20438         
20439         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20440         
20441         this.pop = this.picker().select('>.datepicker-time',true).first();
20442         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20443         
20444         this.picker().on('mousedown', this.onMousedown, this);
20445         this.picker().on('click', this.onClick, this);
20446         
20447         this.picker().addClass('datepicker-dropdown');
20448     
20449         this.fillTime();
20450         this.update();
20451             
20452         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20453         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20454         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20455         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20456         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20457         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20458
20459     },
20460     
20461     fireKey: function(e){
20462         if (!this.picker().isVisible()){
20463             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20464                 this.show();
20465             }
20466             return;
20467         }
20468
20469         e.preventDefault();
20470         
20471         switch(e.keyCode){
20472             case 27: // escape
20473                 this.hide();
20474                 break;
20475             case 37: // left
20476             case 39: // right
20477                 this.onTogglePeriod();
20478                 break;
20479             case 38: // up
20480                 this.onIncrementMinutes();
20481                 break;
20482             case 40: // down
20483                 this.onDecrementMinutes();
20484                 break;
20485             case 13: // enter
20486             case 9: // tab
20487                 this.setTime();
20488                 break;
20489         }
20490     },
20491     
20492     onClick: function(e) {
20493         e.stopPropagation();
20494         e.preventDefault();
20495     },
20496     
20497     picker : function()
20498     {
20499         return this.el.select('.datepicker', true).first();
20500     },
20501     
20502     fillTime: function()
20503     {    
20504         var time = this.pop.select('tbody', true).first();
20505         
20506         time.dom.innerHTML = '';
20507         
20508         time.createChild({
20509             tag: 'tr',
20510             cn: [
20511                 {
20512                     tag: 'td',
20513                     cn: [
20514                         {
20515                             tag: 'a',
20516                             href: '#',
20517                             cls: 'btn',
20518                             cn: [
20519                                 {
20520                                     tag: 'span',
20521                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20522                                 }
20523                             ]
20524                         } 
20525                     ]
20526                 },
20527                 {
20528                     tag: 'td',
20529                     cls: 'separator'
20530                 },
20531                 {
20532                     tag: 'td',
20533                     cn: [
20534                         {
20535                             tag: 'a',
20536                             href: '#',
20537                             cls: 'btn',
20538                             cn: [
20539                                 {
20540                                     tag: 'span',
20541                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20542                                 }
20543                             ]
20544                         }
20545                     ]
20546                 },
20547                 {
20548                     tag: 'td',
20549                     cls: 'separator'
20550                 }
20551             ]
20552         });
20553         
20554         time.createChild({
20555             tag: 'tr',
20556             cn: [
20557                 {
20558                     tag: 'td',
20559                     cn: [
20560                         {
20561                             tag: 'span',
20562                             cls: 'timepicker-hour',
20563                             html: '00'
20564                         }  
20565                     ]
20566                 },
20567                 {
20568                     tag: 'td',
20569                     cls: 'separator',
20570                     html: ':'
20571                 },
20572                 {
20573                     tag: 'td',
20574                     cn: [
20575                         {
20576                             tag: 'span',
20577                             cls: 'timepicker-minute',
20578                             html: '00'
20579                         }  
20580                     ]
20581                 },
20582                 {
20583                     tag: 'td',
20584                     cls: 'separator'
20585                 },
20586                 {
20587                     tag: 'td',
20588                     cn: [
20589                         {
20590                             tag: 'button',
20591                             type: 'button',
20592                             cls: 'btn btn-primary period',
20593                             html: 'AM'
20594                             
20595                         }
20596                     ]
20597                 }
20598             ]
20599         });
20600         
20601         time.createChild({
20602             tag: 'tr',
20603             cn: [
20604                 {
20605                     tag: 'td',
20606                     cn: [
20607                         {
20608                             tag: 'a',
20609                             href: '#',
20610                             cls: 'btn',
20611                             cn: [
20612                                 {
20613                                     tag: 'span',
20614                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20615                                 }
20616                             ]
20617                         }
20618                     ]
20619                 },
20620                 {
20621                     tag: 'td',
20622                     cls: 'separator'
20623                 },
20624                 {
20625                     tag: 'td',
20626                     cn: [
20627                         {
20628                             tag: 'a',
20629                             href: '#',
20630                             cls: 'btn',
20631                             cn: [
20632                                 {
20633                                     tag: 'span',
20634                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20635                                 }
20636                             ]
20637                         }
20638                     ]
20639                 },
20640                 {
20641                     tag: 'td',
20642                     cls: 'separator'
20643                 }
20644             ]
20645         });
20646         
20647     },
20648     
20649     update: function()
20650     {
20651         
20652         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20653         
20654         this.fill();
20655     },
20656     
20657     fill: function() 
20658     {
20659         var hours = this.time.getHours();
20660         var minutes = this.time.getMinutes();
20661         var period = 'AM';
20662         
20663         if(hours > 11){
20664             period = 'PM';
20665         }
20666         
20667         if(hours == 0){
20668             hours = 12;
20669         }
20670         
20671         
20672         if(hours > 12){
20673             hours = hours - 12;
20674         }
20675         
20676         if(hours < 10){
20677             hours = '0' + hours;
20678         }
20679         
20680         if(minutes < 10){
20681             minutes = '0' + minutes;
20682         }
20683         
20684         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20685         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20686         this.pop.select('button', true).first().dom.innerHTML = period;
20687         
20688     },
20689     
20690     place: function()
20691     {   
20692         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20693         
20694         var cls = ['bottom'];
20695         
20696         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20697             cls.pop();
20698             cls.push('top');
20699         }
20700         
20701         cls.push('right');
20702         
20703         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20704             cls.pop();
20705             cls.push('left');
20706         }
20707         
20708         this.picker().addClass(cls.join('-'));
20709         
20710         var _this = this;
20711         
20712         Roo.each(cls, function(c){
20713             if(c == 'bottom'){
20714                 _this.picker().setTop(_this.inputEl().getHeight());
20715                 return;
20716             }
20717             if(c == 'top'){
20718                 _this.picker().setTop(0 - _this.picker().getHeight());
20719                 return;
20720             }
20721             
20722             if(c == 'left'){
20723                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20724                 return;
20725             }
20726             if(c == 'right'){
20727                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20728                 return;
20729             }
20730         });
20731         
20732     },
20733   
20734     onFocus : function()
20735     {
20736         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20737         this.show();
20738     },
20739     
20740     onBlur : function()
20741     {
20742         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20743         this.hide();
20744     },
20745     
20746     show : function()
20747     {
20748         this.picker().show();
20749         this.pop.show();
20750         this.update();
20751         this.place();
20752         
20753         this.fireEvent('show', this, this.date);
20754     },
20755     
20756     hide : function()
20757     {
20758         this.picker().hide();
20759         this.pop.hide();
20760         
20761         this.fireEvent('hide', this, this.date);
20762     },
20763     
20764     setTime : function()
20765     {
20766         this.hide();
20767         this.setValue(this.time.format(this.format));
20768         
20769         this.fireEvent('select', this, this.date);
20770         
20771         
20772     },
20773     
20774     onMousedown: function(e){
20775         e.stopPropagation();
20776         e.preventDefault();
20777     },
20778     
20779     onIncrementHours: function()
20780     {
20781         Roo.log('onIncrementHours');
20782         this.time = this.time.add(Date.HOUR, 1);
20783         this.update();
20784         
20785     },
20786     
20787     onDecrementHours: function()
20788     {
20789         Roo.log('onDecrementHours');
20790         this.time = this.time.add(Date.HOUR, -1);
20791         this.update();
20792     },
20793     
20794     onIncrementMinutes: function()
20795     {
20796         Roo.log('onIncrementMinutes');
20797         this.time = this.time.add(Date.MINUTE, 1);
20798         this.update();
20799     },
20800     
20801     onDecrementMinutes: function()
20802     {
20803         Roo.log('onDecrementMinutes');
20804         this.time = this.time.add(Date.MINUTE, -1);
20805         this.update();
20806     },
20807     
20808     onTogglePeriod: function()
20809     {
20810         Roo.log('onTogglePeriod');
20811         this.time = this.time.add(Date.HOUR, 12);
20812         this.update();
20813     }
20814     
20815    
20816 });
20817
20818 Roo.apply(Roo.bootstrap.TimeField,  {
20819     
20820     content : {
20821         tag: 'tbody',
20822         cn: [
20823             {
20824                 tag: 'tr',
20825                 cn: [
20826                 {
20827                     tag: 'td',
20828                     colspan: '7'
20829                 }
20830                 ]
20831             }
20832         ]
20833     },
20834     
20835     footer : {
20836         tag: 'tfoot',
20837         cn: [
20838             {
20839                 tag: 'tr',
20840                 cn: [
20841                 {
20842                     tag: 'th',
20843                     colspan: '7',
20844                     cls: '',
20845                     cn: [
20846                         {
20847                             tag: 'button',
20848                             cls: 'btn btn-info ok',
20849                             html: 'OK'
20850                         }
20851                     ]
20852                 }
20853
20854                 ]
20855             }
20856         ]
20857     }
20858 });
20859
20860 Roo.apply(Roo.bootstrap.TimeField,  {
20861   
20862     template : {
20863         tag: 'div',
20864         cls: 'datepicker dropdown-menu',
20865         cn: [
20866             {
20867                 tag: 'div',
20868                 cls: 'datepicker-time',
20869                 cn: [
20870                 {
20871                     tag: 'table',
20872                     cls: 'table-condensed',
20873                     cn:[
20874                     Roo.bootstrap.TimeField.content,
20875                     Roo.bootstrap.TimeField.footer
20876                     ]
20877                 }
20878                 ]
20879             }
20880         ]
20881     }
20882 });
20883
20884  
20885
20886  /*
20887  * - LGPL
20888  *
20889  * MonthField
20890  * 
20891  */
20892
20893 /**
20894  * @class Roo.bootstrap.MonthField
20895  * @extends Roo.bootstrap.Input
20896  * Bootstrap MonthField class
20897  * 
20898  * @cfg {String} language default en
20899  * 
20900  * @constructor
20901  * Create a new MonthField
20902  * @param {Object} config The config object
20903  */
20904
20905 Roo.bootstrap.MonthField = function(config){
20906     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20907     
20908     this.addEvents({
20909         /**
20910          * @event show
20911          * Fires when this field show.
20912          * @param {Roo.bootstrap.MonthField} this
20913          * @param {Mixed} date The date value
20914          */
20915         show : true,
20916         /**
20917          * @event show
20918          * Fires when this field hide.
20919          * @param {Roo.bootstrap.MonthField} this
20920          * @param {Mixed} date The date value
20921          */
20922         hide : true,
20923         /**
20924          * @event select
20925          * Fires when select a date.
20926          * @param {Roo.bootstrap.MonthField} this
20927          * @param {String} oldvalue The old value
20928          * @param {String} newvalue The new value
20929          */
20930         select : true
20931     });
20932 };
20933
20934 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20935     
20936     onRender: function(ct, position)
20937     {
20938         
20939         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20940         
20941         this.language = this.language || 'en';
20942         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20943         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20944         
20945         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20946         this.isInline = false;
20947         this.isInput = true;
20948         this.component = this.el.select('.add-on', true).first() || false;
20949         this.component = (this.component && this.component.length === 0) ? false : this.component;
20950         this.hasInput = this.component && this.inputEL().length;
20951         
20952         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20953         
20954         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20955         
20956         this.picker().on('mousedown', this.onMousedown, this);
20957         this.picker().on('click', this.onClick, this);
20958         
20959         this.picker().addClass('datepicker-dropdown');
20960         
20961         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20962             v.setStyle('width', '189px');
20963         });
20964         
20965         this.fillMonths();
20966         
20967         this.update();
20968         
20969         if(this.isInline) {
20970             this.show();
20971         }
20972         
20973     },
20974     
20975     setValue: function(v, suppressEvent)
20976     {   
20977         var o = this.getValue();
20978         
20979         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20980         
20981         this.update();
20982
20983         if(suppressEvent !== true){
20984             this.fireEvent('select', this, o, v);
20985         }
20986         
20987     },
20988     
20989     getValue: function()
20990     {
20991         return this.value;
20992     },
20993     
20994     onClick: function(e) 
20995     {
20996         e.stopPropagation();
20997         e.preventDefault();
20998         
20999         var target = e.getTarget();
21000         
21001         if(target.nodeName.toLowerCase() === 'i'){
21002             target = Roo.get(target).dom.parentNode;
21003         }
21004         
21005         var nodeName = target.nodeName;
21006         var className = target.className;
21007         var html = target.innerHTML;
21008         
21009         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
21010             return;
21011         }
21012         
21013         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
21014         
21015         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21016         
21017         this.hide();
21018                         
21019     },
21020     
21021     picker : function()
21022     {
21023         return this.pickerEl;
21024     },
21025     
21026     fillMonths: function()
21027     {    
21028         var i = 0;
21029         var months = this.picker().select('>.datepicker-months td', true).first();
21030         
21031         months.dom.innerHTML = '';
21032         
21033         while (i < 12) {
21034             var month = {
21035                 tag: 'span',
21036                 cls: 'month',
21037                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
21038             };
21039             
21040             months.createChild(month);
21041         }
21042         
21043     },
21044     
21045     update: function()
21046     {
21047         var _this = this;
21048         
21049         if(typeof(this.vIndex) == 'undefined' && this.value.length){
21050             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
21051         }
21052         
21053         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
21054             e.removeClass('active');
21055             
21056             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
21057                 e.addClass('active');
21058             }
21059         })
21060     },
21061     
21062     place: function()
21063     {
21064         if(this.isInline) {
21065             return;
21066         }
21067         
21068         this.picker().removeClass(['bottom', 'top']);
21069         
21070         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21071             /*
21072              * place to the top of element!
21073              *
21074              */
21075             
21076             this.picker().addClass('top');
21077             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21078             
21079             return;
21080         }
21081         
21082         this.picker().addClass('bottom');
21083         
21084         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21085     },
21086     
21087     onFocus : function()
21088     {
21089         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
21090         this.show();
21091     },
21092     
21093     onBlur : function()
21094     {
21095         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
21096         
21097         var d = this.inputEl().getValue();
21098         
21099         this.setValue(d);
21100                 
21101         this.hide();
21102     },
21103     
21104     show : function()
21105     {
21106         this.picker().show();
21107         this.picker().select('>.datepicker-months', true).first().show();
21108         this.update();
21109         this.place();
21110         
21111         this.fireEvent('show', this, this.date);
21112     },
21113     
21114     hide : function()
21115     {
21116         if(this.isInline) {
21117             return;
21118         }
21119         this.picker().hide();
21120         this.fireEvent('hide', this, this.date);
21121         
21122     },
21123     
21124     onMousedown: function(e)
21125     {
21126         e.stopPropagation();
21127         e.preventDefault();
21128     },
21129     
21130     keyup: function(e)
21131     {
21132         Roo.bootstrap.MonthField.superclass.keyup.call(this);
21133         this.update();
21134     },
21135
21136     fireKey: function(e)
21137     {
21138         if (!this.picker().isVisible()){
21139             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
21140                 this.show();
21141             }
21142             return;
21143         }
21144         
21145         var dir;
21146         
21147         switch(e.keyCode){
21148             case 27: // escape
21149                 this.hide();
21150                 e.preventDefault();
21151                 break;
21152             case 37: // left
21153             case 39: // right
21154                 dir = e.keyCode == 37 ? -1 : 1;
21155                 
21156                 this.vIndex = this.vIndex + dir;
21157                 
21158                 if(this.vIndex < 0){
21159                     this.vIndex = 0;
21160                 }
21161                 
21162                 if(this.vIndex > 11){
21163                     this.vIndex = 11;
21164                 }
21165                 
21166                 if(isNaN(this.vIndex)){
21167                     this.vIndex = 0;
21168                 }
21169                 
21170                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21171                 
21172                 break;
21173             case 38: // up
21174             case 40: // down
21175                 
21176                 dir = e.keyCode == 38 ? -1 : 1;
21177                 
21178                 this.vIndex = this.vIndex + dir * 4;
21179                 
21180                 if(this.vIndex < 0){
21181                     this.vIndex = 0;
21182                 }
21183                 
21184                 if(this.vIndex > 11){
21185                     this.vIndex = 11;
21186                 }
21187                 
21188                 if(isNaN(this.vIndex)){
21189                     this.vIndex = 0;
21190                 }
21191                 
21192                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21193                 break;
21194                 
21195             case 13: // enter
21196                 
21197                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21198                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21199                 }
21200                 
21201                 this.hide();
21202                 e.preventDefault();
21203                 break;
21204             case 9: // tab
21205                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21206                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21207                 }
21208                 this.hide();
21209                 break;
21210             case 16: // shift
21211             case 17: // ctrl
21212             case 18: // alt
21213                 break;
21214             default :
21215                 this.hide();
21216                 
21217         }
21218     },
21219     
21220     remove: function() 
21221     {
21222         this.picker().remove();
21223     }
21224    
21225 });
21226
21227 Roo.apply(Roo.bootstrap.MonthField,  {
21228     
21229     content : {
21230         tag: 'tbody',
21231         cn: [
21232         {
21233             tag: 'tr',
21234             cn: [
21235             {
21236                 tag: 'td',
21237                 colspan: '7'
21238             }
21239             ]
21240         }
21241         ]
21242     },
21243     
21244     dates:{
21245         en: {
21246             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21247             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
21248         }
21249     }
21250 });
21251
21252 Roo.apply(Roo.bootstrap.MonthField,  {
21253   
21254     template : {
21255         tag: 'div',
21256         cls: 'datepicker dropdown-menu roo-dynamic',
21257         cn: [
21258             {
21259                 tag: 'div',
21260                 cls: 'datepicker-months',
21261                 cn: [
21262                 {
21263                     tag: 'table',
21264                     cls: 'table-condensed',
21265                     cn:[
21266                         Roo.bootstrap.DateField.content
21267                     ]
21268                 }
21269                 ]
21270             }
21271         ]
21272     }
21273 });
21274
21275  
21276
21277  
21278  /*
21279  * - LGPL
21280  *
21281  * CheckBox
21282  * 
21283  */
21284
21285 /**
21286  * @class Roo.bootstrap.CheckBox
21287  * @extends Roo.bootstrap.Input
21288  * Bootstrap CheckBox class
21289  * 
21290  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
21291  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
21292  * @cfg {String} boxLabel The text that appears beside the checkbox
21293  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
21294  * @cfg {Boolean} checked initnal the element
21295  * @cfg {Boolean} inline inline the element (default false)
21296  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
21297  * @cfg {String} tooltip label tooltip
21298  * 
21299  * @constructor
21300  * Create a new CheckBox
21301  * @param {Object} config The config object
21302  */
21303
21304 Roo.bootstrap.CheckBox = function(config){
21305     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
21306    
21307     this.addEvents({
21308         /**
21309         * @event check
21310         * Fires when the element is checked or unchecked.
21311         * @param {Roo.bootstrap.CheckBox} this This input
21312         * @param {Boolean} checked The new checked value
21313         */
21314        check : true,
21315        /**
21316         * @event click
21317         * Fires when the element is click.
21318         * @param {Roo.bootstrap.CheckBox} this This input
21319         */
21320        click : true
21321     });
21322     
21323 };
21324
21325 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
21326   
21327     inputType: 'checkbox',
21328     inputValue: 1,
21329     valueOff: 0,
21330     boxLabel: false,
21331     checked: false,
21332     weight : false,
21333     inline: false,
21334     tooltip : '',
21335     
21336     // checkbox success does not make any sense really.. 
21337     invalidClass : "",
21338     validClass : "",
21339     
21340     
21341     getAutoCreate : function()
21342     {
21343         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
21344         
21345         var id = Roo.id();
21346         
21347         var cfg = {};
21348         
21349         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
21350         
21351         if(this.inline){
21352             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
21353         }
21354         
21355         var input =  {
21356             tag: 'input',
21357             id : id,
21358             type : this.inputType,
21359             value : this.inputValue,
21360             cls : 'roo-' + this.inputType, //'form-box',
21361             placeholder : this.placeholder || ''
21362             
21363         };
21364         
21365         if(this.inputType != 'radio'){
21366             var hidden =  {
21367                 tag: 'input',
21368                 type : 'hidden',
21369                 cls : 'roo-hidden-value',
21370                 value : this.checked ? this.inputValue : this.valueOff
21371             };
21372         }
21373         
21374             
21375         if (this.weight) { // Validity check?
21376             cfg.cls += " " + this.inputType + "-" + this.weight;
21377         }
21378         
21379         if (this.disabled) {
21380             input.disabled=true;
21381         }
21382         
21383         if(this.checked){
21384             input.checked = this.checked;
21385         }
21386         
21387         if (this.name) {
21388             
21389             input.name = this.name;
21390             
21391             if(this.inputType != 'radio'){
21392                 hidden.name = this.name;
21393                 input.name = '_hidden_' + this.name;
21394             }
21395         }
21396         
21397         if (this.size) {
21398             input.cls += ' input-' + this.size;
21399         }
21400         
21401         var settings=this;
21402         
21403         ['xs','sm','md','lg'].map(function(size){
21404             if (settings[size]) {
21405                 cfg.cls += ' col-' + size + '-' + settings[size];
21406             }
21407         });
21408         
21409         var inputblock = input;
21410          
21411         if (this.before || this.after) {
21412             
21413             inputblock = {
21414                 cls : 'input-group',
21415                 cn :  [] 
21416             };
21417             
21418             if (this.before) {
21419                 inputblock.cn.push({
21420                     tag :'span',
21421                     cls : 'input-group-addon',
21422                     html : this.before
21423                 });
21424             }
21425             
21426             inputblock.cn.push(input);
21427             
21428             if(this.inputType != 'radio'){
21429                 inputblock.cn.push(hidden);
21430             }
21431             
21432             if (this.after) {
21433                 inputblock.cn.push({
21434                     tag :'span',
21435                     cls : 'input-group-addon',
21436                     html : this.after
21437                 });
21438             }
21439             
21440         }
21441         var boxLabelCfg = false;
21442         
21443         if(this.boxLabel){
21444            
21445             boxLabelCfg = {
21446                 tag: 'label',
21447                 //'for': id, // box label is handled by onclick - so no for...
21448                 cls: 'box-label',
21449                 html: this.boxLabel
21450             };
21451             if(this.tooltip){
21452                 boxLabelCfg.tooltip = this.tooltip;
21453             }
21454              
21455         }
21456         
21457         
21458         if (align ==='left' && this.fieldLabel.length) {
21459 //                Roo.log("left and has label");
21460             cfg.cn = [
21461                 {
21462                     tag: 'label',
21463                     'for' :  id,
21464                     cls : 'control-label',
21465                     html : this.fieldLabel
21466                 },
21467                 {
21468                     cls : "", 
21469                     cn: [
21470                         inputblock
21471                     ]
21472                 }
21473             ];
21474             
21475             if (boxLabelCfg) {
21476                 cfg.cn[1].cn.push(boxLabelCfg);
21477             }
21478             
21479             if(this.labelWidth > 12){
21480                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21481             }
21482             
21483             if(this.labelWidth < 13 && this.labelmd == 0){
21484                 this.labelmd = this.labelWidth;
21485             }
21486             
21487             if(this.labellg > 0){
21488                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21489                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21490             }
21491             
21492             if(this.labelmd > 0){
21493                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21494                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21495             }
21496             
21497             if(this.labelsm > 0){
21498                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21499                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21500             }
21501             
21502             if(this.labelxs > 0){
21503                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21504                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21505             }
21506             
21507         } else if ( this.fieldLabel.length) {
21508 //                Roo.log(" label");
21509                 cfg.cn = [
21510                    
21511                     {
21512                         tag: this.boxLabel ? 'span' : 'label',
21513                         'for': id,
21514                         cls: 'control-label box-input-label',
21515                         //cls : 'input-group-addon',
21516                         html : this.fieldLabel
21517                     },
21518                     
21519                     inputblock
21520                     
21521                 ];
21522                 if (boxLabelCfg) {
21523                     cfg.cn.push(boxLabelCfg);
21524                 }
21525
21526         } else {
21527             
21528 //                Roo.log(" no label && no align");
21529                 cfg.cn = [  inputblock ] ;
21530                 if (boxLabelCfg) {
21531                     cfg.cn.push(boxLabelCfg);
21532                 }
21533
21534                 
21535         }
21536         
21537        
21538         
21539         if(this.inputType != 'radio'){
21540             cfg.cn.push(hidden);
21541         }
21542         
21543         return cfg;
21544         
21545     },
21546     
21547     /**
21548      * return the real input element.
21549      */
21550     inputEl: function ()
21551     {
21552         return this.el.select('input.roo-' + this.inputType,true).first();
21553     },
21554     hiddenEl: function ()
21555     {
21556         return this.el.select('input.roo-hidden-value',true).first();
21557     },
21558     
21559     labelEl: function()
21560     {
21561         return this.el.select('label.control-label',true).first();
21562     },
21563     /* depricated... */
21564     
21565     label: function()
21566     {
21567         return this.labelEl();
21568     },
21569     
21570     boxLabelEl: function()
21571     {
21572         return this.el.select('label.box-label',true).first();
21573     },
21574     
21575     initEvents : function()
21576     {
21577 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21578         
21579         this.inputEl().on('click', this.onClick,  this);
21580         
21581         if (this.boxLabel) { 
21582             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21583         }
21584         
21585         this.startValue = this.getValue();
21586         
21587         if(this.groupId){
21588             Roo.bootstrap.CheckBox.register(this);
21589         }
21590     },
21591     
21592     onClick : function(e)
21593     {   
21594         if(this.fireEvent('click', this, e) !== false){
21595             this.setChecked(!this.checked);
21596         }
21597         
21598     },
21599     
21600     setChecked : function(state,suppressEvent)
21601     {
21602         this.startValue = this.getValue();
21603
21604         if(this.inputType == 'radio'){
21605             
21606             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21607                 e.dom.checked = false;
21608             });
21609             
21610             this.inputEl().dom.checked = true;
21611             
21612             this.inputEl().dom.value = this.inputValue;
21613             
21614             if(suppressEvent !== true){
21615                 this.fireEvent('check', this, true);
21616             }
21617             
21618             this.validate();
21619             
21620             return;
21621         }
21622         
21623         this.checked = state;
21624         
21625         this.inputEl().dom.checked = state;
21626         
21627         
21628         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21629         
21630         if(suppressEvent !== true){
21631             this.fireEvent('check', this, state);
21632         }
21633         
21634         this.validate();
21635     },
21636     
21637     getValue : function()
21638     {
21639         if(this.inputType == 'radio'){
21640             return this.getGroupValue();
21641         }
21642         
21643         return this.hiddenEl().dom.value;
21644         
21645     },
21646     
21647     getGroupValue : function()
21648     {
21649         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21650             return '';
21651         }
21652         
21653         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21654     },
21655     
21656     setValue : function(v,suppressEvent)
21657     {
21658         if(this.inputType == 'radio'){
21659             this.setGroupValue(v, suppressEvent);
21660             return;
21661         }
21662         
21663         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21664         
21665         this.validate();
21666     },
21667     
21668     setGroupValue : function(v, suppressEvent)
21669     {
21670         this.startValue = this.getValue();
21671         
21672         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21673             e.dom.checked = false;
21674             
21675             if(e.dom.value == v){
21676                 e.dom.checked = true;
21677             }
21678         });
21679         
21680         if(suppressEvent !== true){
21681             this.fireEvent('check', this, true);
21682         }
21683
21684         this.validate();
21685         
21686         return;
21687     },
21688     
21689     validate : function()
21690     {
21691         if(this.getVisibilityEl().hasClass('hidden')){
21692             return true;
21693         }
21694         
21695         if(
21696                 this.disabled || 
21697                 (this.inputType == 'radio' && this.validateRadio()) ||
21698                 (this.inputType == 'checkbox' && this.validateCheckbox())
21699         ){
21700             this.markValid();
21701             return true;
21702         }
21703         
21704         this.markInvalid();
21705         return false;
21706     },
21707     
21708     validateRadio : function()
21709     {
21710         if(this.getVisibilityEl().hasClass('hidden')){
21711             return true;
21712         }
21713         
21714         if(this.allowBlank){
21715             return true;
21716         }
21717         
21718         var valid = false;
21719         
21720         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21721             if(!e.dom.checked){
21722                 return;
21723             }
21724             
21725             valid = true;
21726             
21727             return false;
21728         });
21729         
21730         return valid;
21731     },
21732     
21733     validateCheckbox : function()
21734     {
21735         if(!this.groupId){
21736             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21737             //return (this.getValue() == this.inputValue) ? true : false;
21738         }
21739         
21740         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21741         
21742         if(!group){
21743             return false;
21744         }
21745         
21746         var r = false;
21747         
21748         for(var i in group){
21749             if(group[i].el.isVisible(true)){
21750                 r = false;
21751                 break;
21752             }
21753             
21754             r = true;
21755         }
21756         
21757         for(var i in group){
21758             if(r){
21759                 break;
21760             }
21761             
21762             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21763         }
21764         
21765         return r;
21766     },
21767     
21768     /**
21769      * Mark this field as valid
21770      */
21771     markValid : function()
21772     {
21773         var _this = this;
21774         
21775         this.fireEvent('valid', this);
21776         
21777         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21778         
21779         if(this.groupId){
21780             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21781         }
21782         
21783         if(label){
21784             label.markValid();
21785         }
21786
21787         if(this.inputType == 'radio'){
21788             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21789                 var fg = e.findParent('.form-group', false, true);
21790                 if (Roo.bootstrap.version == 3) {
21791                     fg.removeClass([_this.invalidClass, _this.validClass]);
21792                     fg.addClass(_this.validClass);
21793                 } else {
21794                     fg.removeClass(['is-valid', 'is-invalid']);
21795                     fg.addClass('is-valid');
21796                 }
21797             });
21798             
21799             return;
21800         }
21801
21802         if(!this.groupId){
21803             var fg = this.el.findParent('.form-group', false, true);
21804             if (Roo.bootstrap.version == 3) {
21805                 fg.removeClass([this.invalidClass, this.validClass]);
21806                 fg.addClass(this.validClass);
21807             } else {
21808                 fg.removeClass(['is-valid', 'is-invalid']);
21809                 fg.addClass('is-valid');
21810             }
21811             return;
21812         }
21813         
21814         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21815         
21816         if(!group){
21817             return;
21818         }
21819         
21820         for(var i in group){
21821             var fg = group[i].el.findParent('.form-group', false, true);
21822             if (Roo.bootstrap.version == 3) {
21823                 fg.removeClass([this.invalidClass, this.validClass]);
21824                 fg.addClass(this.validClass);
21825             } else {
21826                 fg.removeClass(['is-valid', 'is-invalid']);
21827                 fg.addClass('is-valid');
21828             }
21829         }
21830     },
21831     
21832      /**
21833      * Mark this field as invalid
21834      * @param {String} msg The validation message
21835      */
21836     markInvalid : function(msg)
21837     {
21838         if(this.allowBlank){
21839             return;
21840         }
21841         
21842         var _this = this;
21843         
21844         this.fireEvent('invalid', this, msg);
21845         
21846         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21847         
21848         if(this.groupId){
21849             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21850         }
21851         
21852         if(label){
21853             label.markInvalid();
21854         }
21855             
21856         if(this.inputType == 'radio'){
21857             
21858             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21859                 var fg = e.findParent('.form-group', false, true);
21860                 if (Roo.bootstrap.version == 3) {
21861                     fg.removeClass([_this.invalidClass, _this.validClass]);
21862                     fg.addClass(_this.invalidClass);
21863                 } else {
21864                     fg.removeClass(['is-invalid', 'is-valid']);
21865                     fg.addClass('is-invalid');
21866                 }
21867             });
21868             
21869             return;
21870         }
21871         
21872         if(!this.groupId){
21873             var fg = this.el.findParent('.form-group', false, true);
21874             if (Roo.bootstrap.version == 3) {
21875                 fg.removeClass([_this.invalidClass, _this.validClass]);
21876                 fg.addClass(_this.invalidClass);
21877             } else {
21878                 fg.removeClass(['is-invalid', 'is-valid']);
21879                 fg.addClass('is-invalid');
21880             }
21881             return;
21882         }
21883         
21884         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21885         
21886         if(!group){
21887             return;
21888         }
21889         
21890         for(var i in group){
21891             var fg = group[i].el.findParent('.form-group', false, true);
21892             if (Roo.bootstrap.version == 3) {
21893                 fg.removeClass([_this.invalidClass, _this.validClass]);
21894                 fg.addClass(_this.invalidClass);
21895             } else {
21896                 fg.removeClass(['is-invalid', 'is-valid']);
21897                 fg.addClass('is-invalid');
21898             }
21899         }
21900         
21901     },
21902     
21903     clearInvalid : function()
21904     {
21905         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21906         
21907         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21908         
21909         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21910         
21911         if (label && label.iconEl) {
21912             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21913             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21914         }
21915     },
21916     
21917     disable : function()
21918     {
21919         if(this.inputType != 'radio'){
21920             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21921             return;
21922         }
21923         
21924         var _this = this;
21925         
21926         if(this.rendered){
21927             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21928                 _this.getActionEl().addClass(this.disabledClass);
21929                 e.dom.disabled = true;
21930             });
21931         }
21932         
21933         this.disabled = true;
21934         this.fireEvent("disable", this);
21935         return this;
21936     },
21937
21938     enable : function()
21939     {
21940         if(this.inputType != 'radio'){
21941             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21942             return;
21943         }
21944         
21945         var _this = this;
21946         
21947         if(this.rendered){
21948             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21949                 _this.getActionEl().removeClass(this.disabledClass);
21950                 e.dom.disabled = false;
21951             });
21952         }
21953         
21954         this.disabled = false;
21955         this.fireEvent("enable", this);
21956         return this;
21957     },
21958     
21959     setBoxLabel : function(v)
21960     {
21961         this.boxLabel = v;
21962         
21963         if(this.rendered){
21964             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21965         }
21966     }
21967
21968 });
21969
21970 Roo.apply(Roo.bootstrap.CheckBox, {
21971     
21972     groups: {},
21973     
21974      /**
21975     * register a CheckBox Group
21976     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21977     */
21978     register : function(checkbox)
21979     {
21980         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21981             this.groups[checkbox.groupId] = {};
21982         }
21983         
21984         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21985             return;
21986         }
21987         
21988         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21989         
21990     },
21991     /**
21992     * fetch a CheckBox Group based on the group ID
21993     * @param {string} the group ID
21994     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21995     */
21996     get: function(groupId) {
21997         if (typeof(this.groups[groupId]) == 'undefined') {
21998             return false;
21999         }
22000         
22001         return this.groups[groupId] ;
22002     }
22003     
22004     
22005 });
22006 /*
22007  * - LGPL
22008  *
22009  * RadioItem
22010  * 
22011  */
22012
22013 /**
22014  * @class Roo.bootstrap.Radio
22015  * @extends Roo.bootstrap.Component
22016  * Bootstrap Radio class
22017  * @cfg {String} boxLabel - the label associated
22018  * @cfg {String} value - the value of radio
22019  * 
22020  * @constructor
22021  * Create a new Radio
22022  * @param {Object} config The config object
22023  */
22024 Roo.bootstrap.Radio = function(config){
22025     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
22026     
22027 };
22028
22029 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
22030     
22031     boxLabel : '',
22032     
22033     value : '',
22034     
22035     getAutoCreate : function()
22036     {
22037         var cfg = {
22038             tag : 'div',
22039             cls : 'form-group radio',
22040             cn : [
22041                 {
22042                     tag : 'label',
22043                     cls : 'box-label',
22044                     html : this.boxLabel
22045                 }
22046             ]
22047         };
22048         
22049         return cfg;
22050     },
22051     
22052     initEvents : function() 
22053     {
22054         this.parent().register(this);
22055         
22056         this.el.on('click', this.onClick, this);
22057         
22058     },
22059     
22060     onClick : function(e)
22061     {
22062         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
22063             this.setChecked(true);
22064         }
22065     },
22066     
22067     setChecked : function(state, suppressEvent)
22068     {
22069         this.parent().setValue(this.value, suppressEvent);
22070         
22071     },
22072     
22073     setBoxLabel : function(v)
22074     {
22075         this.boxLabel = v;
22076         
22077         if(this.rendered){
22078             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
22079         }
22080     }
22081     
22082 });
22083  
22084
22085  /*
22086  * - LGPL
22087  *
22088  * Input
22089  * 
22090  */
22091
22092 /**
22093  * @class Roo.bootstrap.SecurePass
22094  * @extends Roo.bootstrap.Input
22095  * Bootstrap SecurePass class
22096  *
22097  * 
22098  * @constructor
22099  * Create a new SecurePass
22100  * @param {Object} config The config object
22101  */
22102  
22103 Roo.bootstrap.SecurePass = function (config) {
22104     // these go here, so the translation tool can replace them..
22105     this.errors = {
22106         PwdEmpty: "Please type a password, and then retype it to confirm.",
22107         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22108         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22109         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22110         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22111         FNInPwd: "Your password can't contain your first name. Please type a different password.",
22112         LNInPwd: "Your password can't contain your last name. Please type a different password.",
22113         TooWeak: "Your password is Too Weak."
22114     },
22115     this.meterLabel = "Password strength:";
22116     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
22117     this.meterClass = [
22118         "roo-password-meter-tooweak", 
22119         "roo-password-meter-weak", 
22120         "roo-password-meter-medium", 
22121         "roo-password-meter-strong", 
22122         "roo-password-meter-grey"
22123     ];
22124     
22125     this.errors = {};
22126     
22127     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
22128 }
22129
22130 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
22131     /**
22132      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
22133      * {
22134      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
22135      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22136      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22137      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22138      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22139      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
22140      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
22141      * })
22142      */
22143     // private
22144     
22145     meterWidth: 300,
22146     errorMsg :'',    
22147     errors: false,
22148     imageRoot: '/',
22149     /**
22150      * @cfg {String/Object} Label for the strength meter (defaults to
22151      * 'Password strength:')
22152      */
22153     // private
22154     meterLabel: '',
22155     /**
22156      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
22157      * ['Weak', 'Medium', 'Strong'])
22158      */
22159     // private    
22160     pwdStrengths: false,    
22161     // private
22162     strength: 0,
22163     // private
22164     _lastPwd: null,
22165     // private
22166     kCapitalLetter: 0,
22167     kSmallLetter: 1,
22168     kDigit: 2,
22169     kPunctuation: 3,
22170     
22171     insecure: false,
22172     // private
22173     initEvents: function ()
22174     {
22175         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
22176
22177         if (this.el.is('input[type=password]') && Roo.isSafari) {
22178             this.el.on('keydown', this.SafariOnKeyDown, this);
22179         }
22180
22181         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
22182     },
22183     // private
22184     onRender: function (ct, position)
22185     {
22186         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
22187         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
22188         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
22189
22190         this.trigger.createChild({
22191                    cn: [
22192                     {
22193                     //id: 'PwdMeter',
22194                     tag: 'div',
22195                     cls: 'roo-password-meter-grey col-xs-12',
22196                     style: {
22197                         //width: 0,
22198                         //width: this.meterWidth + 'px'                                                
22199                         }
22200                     },
22201                     {                            
22202                          cls: 'roo-password-meter-text'                          
22203                     }
22204                 ]            
22205         });
22206
22207          
22208         if (this.hideTrigger) {
22209             this.trigger.setDisplayed(false);
22210         }
22211         this.setSize(this.width || '', this.height || '');
22212     },
22213     // private
22214     onDestroy: function ()
22215     {
22216         if (this.trigger) {
22217             this.trigger.removeAllListeners();
22218             this.trigger.remove();
22219         }
22220         if (this.wrap) {
22221             this.wrap.remove();
22222         }
22223         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
22224     },
22225     // private
22226     checkStrength: function ()
22227     {
22228         var pwd = this.inputEl().getValue();
22229         if (pwd == this._lastPwd) {
22230             return;
22231         }
22232
22233         var strength;
22234         if (this.ClientSideStrongPassword(pwd)) {
22235             strength = 3;
22236         } else if (this.ClientSideMediumPassword(pwd)) {
22237             strength = 2;
22238         } else if (this.ClientSideWeakPassword(pwd)) {
22239             strength = 1;
22240         } else {
22241             strength = 0;
22242         }
22243         
22244         Roo.log('strength1: ' + strength);
22245         
22246         //var pm = this.trigger.child('div/div/div').dom;
22247         var pm = this.trigger.child('div/div');
22248         pm.removeClass(this.meterClass);
22249         pm.addClass(this.meterClass[strength]);
22250                 
22251         
22252         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22253                 
22254         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
22255         
22256         this._lastPwd = pwd;
22257     },
22258     reset: function ()
22259     {
22260         Roo.bootstrap.SecurePass.superclass.reset.call(this);
22261         
22262         this._lastPwd = '';
22263         
22264         var pm = this.trigger.child('div/div');
22265         pm.removeClass(this.meterClass);
22266         pm.addClass('roo-password-meter-grey');        
22267         
22268         
22269         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22270         
22271         pt.innerHTML = '';
22272         this.inputEl().dom.type='password';
22273     },
22274     // private
22275     validateValue: function (value)
22276     {
22277         
22278         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
22279             return false;
22280         }
22281         if (value.length == 0) {
22282             if (this.allowBlank) {
22283                 this.clearInvalid();
22284                 return true;
22285             }
22286
22287             this.markInvalid(this.errors.PwdEmpty);
22288             this.errorMsg = this.errors.PwdEmpty;
22289             return false;
22290         }
22291         
22292         if(this.insecure){
22293             return true;
22294         }
22295         
22296         if ('[\x21-\x7e]*'.match(value)) {
22297             this.markInvalid(this.errors.PwdBadChar);
22298             this.errorMsg = this.errors.PwdBadChar;
22299             return false;
22300         }
22301         if (value.length < 6) {
22302             this.markInvalid(this.errors.PwdShort);
22303             this.errorMsg = this.errors.PwdShort;
22304             return false;
22305         }
22306         if (value.length > 16) {
22307             this.markInvalid(this.errors.PwdLong);
22308             this.errorMsg = this.errors.PwdLong;
22309             return false;
22310         }
22311         var strength;
22312         if (this.ClientSideStrongPassword(value)) {
22313             strength = 3;
22314         } else if (this.ClientSideMediumPassword(value)) {
22315             strength = 2;
22316         } else if (this.ClientSideWeakPassword(value)) {
22317             strength = 1;
22318         } else {
22319             strength = 0;
22320         }
22321
22322         
22323         if (strength < 2) {
22324             //this.markInvalid(this.errors.TooWeak);
22325             this.errorMsg = this.errors.TooWeak;
22326             //return false;
22327         }
22328         
22329         
22330         console.log('strength2: ' + strength);
22331         
22332         //var pm = this.trigger.child('div/div/div').dom;
22333         
22334         var pm = this.trigger.child('div/div');
22335         pm.removeClass(this.meterClass);
22336         pm.addClass(this.meterClass[strength]);
22337                 
22338         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22339                 
22340         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
22341         
22342         this.errorMsg = ''; 
22343         return true;
22344     },
22345     // private
22346     CharacterSetChecks: function (type)
22347     {
22348         this.type = type;
22349         this.fResult = false;
22350     },
22351     // private
22352     isctype: function (character, type)
22353     {
22354         switch (type) {  
22355             case this.kCapitalLetter:
22356                 if (character >= 'A' && character <= 'Z') {
22357                     return true;
22358                 }
22359                 break;
22360             
22361             case this.kSmallLetter:
22362                 if (character >= 'a' && character <= 'z') {
22363                     return true;
22364                 }
22365                 break;
22366             
22367             case this.kDigit:
22368                 if (character >= '0' && character <= '9') {
22369                     return true;
22370                 }
22371                 break;
22372             
22373             case this.kPunctuation:
22374                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
22375                     return true;
22376                 }
22377                 break;
22378             
22379             default:
22380                 return false;
22381         }
22382
22383     },
22384     // private
22385     IsLongEnough: function (pwd, size)
22386     {
22387         return !(pwd == null || isNaN(size) || pwd.length < size);
22388     },
22389     // private
22390     SpansEnoughCharacterSets: function (word, nb)
22391     {
22392         if (!this.IsLongEnough(word, nb))
22393         {
22394             return false;
22395         }
22396
22397         var characterSetChecks = new Array(
22398             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
22399             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
22400         );
22401         
22402         for (var index = 0; index < word.length; ++index) {
22403             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22404                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
22405                     characterSetChecks[nCharSet].fResult = true;
22406                     break;
22407                 }
22408             }
22409         }
22410
22411         var nCharSets = 0;
22412         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22413             if (characterSetChecks[nCharSet].fResult) {
22414                 ++nCharSets;
22415             }
22416         }
22417
22418         if (nCharSets < nb) {
22419             return false;
22420         }
22421         return true;
22422     },
22423     // private
22424     ClientSideStrongPassword: function (pwd)
22425     {
22426         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22427     },
22428     // private
22429     ClientSideMediumPassword: function (pwd)
22430     {
22431         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22432     },
22433     // private
22434     ClientSideWeakPassword: function (pwd)
22435     {
22436         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22437     }
22438           
22439 })//<script type="text/javascript">
22440
22441 /*
22442  * Based  Ext JS Library 1.1.1
22443  * Copyright(c) 2006-2007, Ext JS, LLC.
22444  * LGPL
22445  *
22446  */
22447  
22448 /**
22449  * @class Roo.HtmlEditorCore
22450  * @extends Roo.Component
22451  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22452  *
22453  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22454  */
22455
22456 Roo.HtmlEditorCore = function(config){
22457     
22458     
22459     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22460     
22461     
22462     this.addEvents({
22463         /**
22464          * @event initialize
22465          * Fires when the editor is fully initialized (including the iframe)
22466          * @param {Roo.HtmlEditorCore} this
22467          */
22468         initialize: true,
22469         /**
22470          * @event activate
22471          * Fires when the editor is first receives the focus. Any insertion must wait
22472          * until after this event.
22473          * @param {Roo.HtmlEditorCore} this
22474          */
22475         activate: true,
22476          /**
22477          * @event beforesync
22478          * Fires before the textarea is updated with content from the editor iframe. Return false
22479          * to cancel the sync.
22480          * @param {Roo.HtmlEditorCore} this
22481          * @param {String} html
22482          */
22483         beforesync: true,
22484          /**
22485          * @event beforepush
22486          * Fires before the iframe editor is updated with content from the textarea. Return false
22487          * to cancel the push.
22488          * @param {Roo.HtmlEditorCore} this
22489          * @param {String} html
22490          */
22491         beforepush: true,
22492          /**
22493          * @event sync
22494          * Fires when the textarea is updated with content from the editor iframe.
22495          * @param {Roo.HtmlEditorCore} this
22496          * @param {String} html
22497          */
22498         sync: true,
22499          /**
22500          * @event push
22501          * Fires when the iframe editor is updated with content from the textarea.
22502          * @param {Roo.HtmlEditorCore} this
22503          * @param {String} html
22504          */
22505         push: true,
22506         
22507         /**
22508          * @event editorevent
22509          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22510          * @param {Roo.HtmlEditorCore} this
22511          */
22512         editorevent: true
22513         
22514     });
22515     
22516     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22517     
22518     // defaults : white / black...
22519     this.applyBlacklists();
22520     
22521     
22522     
22523 };
22524
22525
22526 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22527
22528
22529      /**
22530      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22531      */
22532     
22533     owner : false,
22534     
22535      /**
22536      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22537      *                        Roo.resizable.
22538      */
22539     resizable : false,
22540      /**
22541      * @cfg {Number} height (in pixels)
22542      */   
22543     height: 300,
22544    /**
22545      * @cfg {Number} width (in pixels)
22546      */   
22547     width: 500,
22548     
22549     /**
22550      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22551      * 
22552      */
22553     stylesheets: false,
22554     
22555     // id of frame..
22556     frameId: false,
22557     
22558     // private properties
22559     validationEvent : false,
22560     deferHeight: true,
22561     initialized : false,
22562     activated : false,
22563     sourceEditMode : false,
22564     onFocus : Roo.emptyFn,
22565     iframePad:3,
22566     hideMode:'offsets',
22567     
22568     clearUp: true,
22569     
22570     // blacklist + whitelisted elements..
22571     black: false,
22572     white: false,
22573      
22574     bodyCls : '',
22575
22576     /**
22577      * Protected method that will not generally be called directly. It
22578      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22579      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22580      */
22581     getDocMarkup : function(){
22582         // body styles..
22583         var st = '';
22584         
22585         // inherit styels from page...?? 
22586         if (this.stylesheets === false) {
22587             
22588             Roo.get(document.head).select('style').each(function(node) {
22589                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22590             });
22591             
22592             Roo.get(document.head).select('link').each(function(node) { 
22593                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22594             });
22595             
22596         } else if (!this.stylesheets.length) {
22597                 // simple..
22598                 st = '<style type="text/css">' +
22599                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22600                    '</style>';
22601         } else { 
22602             st = '<style type="text/css">' +
22603                     this.stylesheets +
22604                 '</style>';
22605         }
22606         
22607         st +=  '<style type="text/css">' +
22608             'IMG { cursor: pointer } ' +
22609         '</style>';
22610
22611         var cls = 'roo-htmleditor-body';
22612         
22613         if(this.bodyCls.length){
22614             cls += ' ' + this.bodyCls;
22615         }
22616         
22617         return '<html><head>' + st  +
22618             //<style type="text/css">' +
22619             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22620             //'</style>' +
22621             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
22622     },
22623
22624     // private
22625     onRender : function(ct, position)
22626     {
22627         var _t = this;
22628         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22629         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22630         
22631         
22632         this.el.dom.style.border = '0 none';
22633         this.el.dom.setAttribute('tabIndex', -1);
22634         this.el.addClass('x-hidden hide');
22635         
22636         
22637         
22638         if(Roo.isIE){ // fix IE 1px bogus margin
22639             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22640         }
22641        
22642         
22643         this.frameId = Roo.id();
22644         
22645          
22646         
22647         var iframe = this.owner.wrap.createChild({
22648             tag: 'iframe',
22649             cls: 'form-control', // bootstrap..
22650             id: this.frameId,
22651             name: this.frameId,
22652             frameBorder : 'no',
22653             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22654         }, this.el
22655         );
22656         
22657         
22658         this.iframe = iframe.dom;
22659
22660          this.assignDocWin();
22661         
22662         this.doc.designMode = 'on';
22663        
22664         this.doc.open();
22665         this.doc.write(this.getDocMarkup());
22666         this.doc.close();
22667
22668         
22669         var task = { // must defer to wait for browser to be ready
22670             run : function(){
22671                 //console.log("run task?" + this.doc.readyState);
22672                 this.assignDocWin();
22673                 if(this.doc.body || this.doc.readyState == 'complete'){
22674                     try {
22675                         this.doc.designMode="on";
22676                     } catch (e) {
22677                         return;
22678                     }
22679                     Roo.TaskMgr.stop(task);
22680                     this.initEditor.defer(10, this);
22681                 }
22682             },
22683             interval : 10,
22684             duration: 10000,
22685             scope: this
22686         };
22687         Roo.TaskMgr.start(task);
22688
22689     },
22690
22691     // private
22692     onResize : function(w, h)
22693     {
22694          Roo.log('resize: ' +w + ',' + h );
22695         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22696         if(!this.iframe){
22697             return;
22698         }
22699         if(typeof w == 'number'){
22700             
22701             this.iframe.style.width = w + 'px';
22702         }
22703         if(typeof h == 'number'){
22704             
22705             this.iframe.style.height = h + 'px';
22706             if(this.doc){
22707                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22708             }
22709         }
22710         
22711     },
22712
22713     /**
22714      * Toggles the editor between standard and source edit mode.
22715      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22716      */
22717     toggleSourceEdit : function(sourceEditMode){
22718         
22719         this.sourceEditMode = sourceEditMode === true;
22720         
22721         if(this.sourceEditMode){
22722  
22723             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22724             
22725         }else{
22726             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22727             //this.iframe.className = '';
22728             this.deferFocus();
22729         }
22730         //this.setSize(this.owner.wrap.getSize());
22731         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22732     },
22733
22734     
22735   
22736
22737     /**
22738      * Protected method that will not generally be called directly. If you need/want
22739      * custom HTML cleanup, this is the method you should override.
22740      * @param {String} html The HTML to be cleaned
22741      * return {String} The cleaned HTML
22742      */
22743     cleanHtml : function(html){
22744         html = String(html);
22745         if(html.length > 5){
22746             if(Roo.isSafari){ // strip safari nonsense
22747                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22748             }
22749         }
22750         if(html == '&nbsp;'){
22751             html = '';
22752         }
22753         return html;
22754     },
22755
22756     /**
22757      * HTML Editor -> Textarea
22758      * Protected method that will not generally be called directly. Syncs the contents
22759      * of the editor iframe with the textarea.
22760      */
22761     syncValue : function(){
22762         if(this.initialized){
22763             var bd = (this.doc.body || this.doc.documentElement);
22764             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22765             var html = bd.innerHTML;
22766             if(Roo.isSafari){
22767                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22768                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22769                 if(m && m[1]){
22770                     html = '<div style="'+m[0]+'">' + html + '</div>';
22771                 }
22772             }
22773             html = this.cleanHtml(html);
22774             // fix up the special chars.. normaly like back quotes in word...
22775             // however we do not want to do this with chinese..
22776             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
22777                 
22778                 var cc = match.charCodeAt();
22779
22780                 // Get the character value, handling surrogate pairs
22781                 if (match.length == 2) {
22782                     // It's a surrogate pair, calculate the Unicode code point
22783                     var high = match.charCodeAt(0) - 0xD800;
22784                     var low  = match.charCodeAt(1) - 0xDC00;
22785                     cc = (high * 0x400) + low + 0x10000;
22786                 }  else if (
22787                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22788                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22789                     (cc >= 0xf900 && cc < 0xfb00 )
22790                 ) {
22791                         return match;
22792                 }  
22793          
22794                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
22795                 return "&#" + cc + ";";
22796                 
22797                 
22798             });
22799             
22800             
22801              
22802             if(this.owner.fireEvent('beforesync', this, html) !== false){
22803                 this.el.dom.value = html;
22804                 this.owner.fireEvent('sync', this, html);
22805             }
22806         }
22807     },
22808
22809     /**
22810      * Protected method that will not generally be called directly. Pushes the value of the textarea
22811      * into the iframe editor.
22812      */
22813     pushValue : function(){
22814         if(this.initialized){
22815             var v = this.el.dom.value.trim();
22816             
22817 //            if(v.length < 1){
22818 //                v = '&#160;';
22819 //            }
22820             
22821             if(this.owner.fireEvent('beforepush', this, v) !== false){
22822                 var d = (this.doc.body || this.doc.documentElement);
22823                 d.innerHTML = v;
22824                 this.cleanUpPaste();
22825                 this.el.dom.value = d.innerHTML;
22826                 this.owner.fireEvent('push', this, v);
22827             }
22828         }
22829     },
22830
22831     // private
22832     deferFocus : function(){
22833         this.focus.defer(10, this);
22834     },
22835
22836     // doc'ed in Field
22837     focus : function(){
22838         if(this.win && !this.sourceEditMode){
22839             this.win.focus();
22840         }else{
22841             this.el.focus();
22842         }
22843     },
22844     
22845     assignDocWin: function()
22846     {
22847         var iframe = this.iframe;
22848         
22849          if(Roo.isIE){
22850             this.doc = iframe.contentWindow.document;
22851             this.win = iframe.contentWindow;
22852         } else {
22853 //            if (!Roo.get(this.frameId)) {
22854 //                return;
22855 //            }
22856 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22857 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22858             
22859             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22860                 return;
22861             }
22862             
22863             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22864             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22865         }
22866     },
22867     
22868     // private
22869     initEditor : function(){
22870         //console.log("INIT EDITOR");
22871         this.assignDocWin();
22872         
22873         
22874         
22875         this.doc.designMode="on";
22876         this.doc.open();
22877         this.doc.write(this.getDocMarkup());
22878         this.doc.close();
22879         
22880         var dbody = (this.doc.body || this.doc.documentElement);
22881         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22882         // this copies styles from the containing element into thsi one..
22883         // not sure why we need all of this..
22884         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22885         
22886         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22887         //ss['background-attachment'] = 'fixed'; // w3c
22888         dbody.bgProperties = 'fixed'; // ie
22889         //Roo.DomHelper.applyStyles(dbody, ss);
22890         Roo.EventManager.on(this.doc, {
22891             //'mousedown': this.onEditorEvent,
22892             'mouseup': this.onEditorEvent,
22893             'dblclick': this.onEditorEvent,
22894             'click': this.onEditorEvent,
22895             'keyup': this.onEditorEvent,
22896             buffer:100,
22897             scope: this
22898         });
22899         if(Roo.isGecko){
22900             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22901         }
22902         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22903             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22904         }
22905         this.initialized = true;
22906
22907         this.owner.fireEvent('initialize', this);
22908         this.pushValue();
22909     },
22910
22911     // private
22912     onDestroy : function(){
22913         
22914         
22915         
22916         if(this.rendered){
22917             
22918             //for (var i =0; i < this.toolbars.length;i++) {
22919             //    // fixme - ask toolbars for heights?
22920             //    this.toolbars[i].onDestroy();
22921            // }
22922             
22923             //this.wrap.dom.innerHTML = '';
22924             //this.wrap.remove();
22925         }
22926     },
22927
22928     // private
22929     onFirstFocus : function(){
22930         
22931         this.assignDocWin();
22932         
22933         
22934         this.activated = true;
22935          
22936     
22937         if(Roo.isGecko){ // prevent silly gecko errors
22938             this.win.focus();
22939             var s = this.win.getSelection();
22940             if(!s.focusNode || s.focusNode.nodeType != 3){
22941                 var r = s.getRangeAt(0);
22942                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22943                 r.collapse(true);
22944                 this.deferFocus();
22945             }
22946             try{
22947                 this.execCmd('useCSS', true);
22948                 this.execCmd('styleWithCSS', false);
22949             }catch(e){}
22950         }
22951         this.owner.fireEvent('activate', this);
22952     },
22953
22954     // private
22955     adjustFont: function(btn){
22956         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22957         //if(Roo.isSafari){ // safari
22958         //    adjust *= 2;
22959        // }
22960         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22961         if(Roo.isSafari){ // safari
22962             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22963             v =  (v < 10) ? 10 : v;
22964             v =  (v > 48) ? 48 : v;
22965             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22966             
22967         }
22968         
22969         
22970         v = Math.max(1, v+adjust);
22971         
22972         this.execCmd('FontSize', v  );
22973     },
22974
22975     onEditorEvent : function(e)
22976     {
22977         this.owner.fireEvent('editorevent', this, e);
22978       //  this.updateToolbar();
22979         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22980     },
22981
22982     insertTag : function(tg)
22983     {
22984         // could be a bit smarter... -> wrap the current selected tRoo..
22985         if (tg.toLowerCase() == 'span' ||
22986             tg.toLowerCase() == 'code' ||
22987             tg.toLowerCase() == 'sup' ||
22988             tg.toLowerCase() == 'sub' 
22989             ) {
22990             
22991             range = this.createRange(this.getSelection());
22992             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22993             wrappingNode.appendChild(range.extractContents());
22994             range.insertNode(wrappingNode);
22995
22996             return;
22997             
22998             
22999             
23000         }
23001         this.execCmd("formatblock",   tg);
23002         
23003     },
23004     
23005     insertText : function(txt)
23006     {
23007         
23008         
23009         var range = this.createRange();
23010         range.deleteContents();
23011                //alert(Sender.getAttribute('label'));
23012                
23013         range.insertNode(this.doc.createTextNode(txt));
23014     } ,
23015     
23016      
23017
23018     /**
23019      * Executes a Midas editor command on the editor document and performs necessary focus and
23020      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
23021      * @param {String} cmd The Midas command
23022      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
23023      */
23024     relayCmd : function(cmd, value){
23025         this.win.focus();
23026         this.execCmd(cmd, value);
23027         this.owner.fireEvent('editorevent', this);
23028         //this.updateToolbar();
23029         this.owner.deferFocus();
23030     },
23031
23032     /**
23033      * Executes a Midas editor command directly on the editor document.
23034      * For visual commands, you should use {@link #relayCmd} instead.
23035      * <b>This should only be called after the editor is initialized.</b>
23036      * @param {String} cmd The Midas command
23037      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
23038      */
23039     execCmd : function(cmd, value){
23040         this.doc.execCommand(cmd, false, value === undefined ? null : value);
23041         this.syncValue();
23042     },
23043  
23044  
23045    
23046     /**
23047      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
23048      * to insert tRoo.
23049      * @param {String} text | dom node.. 
23050      */
23051     insertAtCursor : function(text)
23052     {
23053         
23054         if(!this.activated){
23055             return;
23056         }
23057         /*
23058         if(Roo.isIE){
23059             this.win.focus();
23060             var r = this.doc.selection.createRange();
23061             if(r){
23062                 r.collapse(true);
23063                 r.pasteHTML(text);
23064                 this.syncValue();
23065                 this.deferFocus();
23066             
23067             }
23068             return;
23069         }
23070         */
23071         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
23072             this.win.focus();
23073             
23074             
23075             // from jquery ui (MIT licenced)
23076             var range, node;
23077             var win = this.win;
23078             
23079             if (win.getSelection && win.getSelection().getRangeAt) {
23080                 range = win.getSelection().getRangeAt(0);
23081                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
23082                 range.insertNode(node);
23083             } else if (win.document.selection && win.document.selection.createRange) {
23084                 // no firefox support
23085                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23086                 win.document.selection.createRange().pasteHTML(txt);
23087             } else {
23088                 // no firefox support
23089                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23090                 this.execCmd('InsertHTML', txt);
23091             } 
23092             
23093             this.syncValue();
23094             
23095             this.deferFocus();
23096         }
23097     },
23098  // private
23099     mozKeyPress : function(e){
23100         if(e.ctrlKey){
23101             var c = e.getCharCode(), cmd;
23102           
23103             if(c > 0){
23104                 c = String.fromCharCode(c).toLowerCase();
23105                 switch(c){
23106                     case 'b':
23107                         cmd = 'bold';
23108                         break;
23109                     case 'i':
23110                         cmd = 'italic';
23111                         break;
23112                     
23113                     case 'u':
23114                         cmd = 'underline';
23115                         break;
23116                     
23117                     case 'v':
23118                         this.cleanUpPaste.defer(100, this);
23119                         return;
23120                         
23121                 }
23122                 if(cmd){
23123                     this.win.focus();
23124                     this.execCmd(cmd);
23125                     this.deferFocus();
23126                     e.preventDefault();
23127                 }
23128                 
23129             }
23130         }
23131     },
23132
23133     // private
23134     fixKeys : function(){ // load time branching for fastest keydown performance
23135         if(Roo.isIE){
23136             return function(e){
23137                 var k = e.getKey(), r;
23138                 if(k == e.TAB){
23139                     e.stopEvent();
23140                     r = this.doc.selection.createRange();
23141                     if(r){
23142                         r.collapse(true);
23143                         r.pasteHTML('&#160;&#160;&#160;&#160;');
23144                         this.deferFocus();
23145                     }
23146                     return;
23147                 }
23148                 
23149                 if(k == e.ENTER){
23150                     r = this.doc.selection.createRange();
23151                     if(r){
23152                         var target = r.parentElement();
23153                         if(!target || target.tagName.toLowerCase() != 'li'){
23154                             e.stopEvent();
23155                             r.pasteHTML('<br />');
23156                             r.collapse(false);
23157                             r.select();
23158                         }
23159                     }
23160                 }
23161                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23162                     this.cleanUpPaste.defer(100, this);
23163                     return;
23164                 }
23165                 
23166                 
23167             };
23168         }else if(Roo.isOpera){
23169             return function(e){
23170                 var k = e.getKey();
23171                 if(k == e.TAB){
23172                     e.stopEvent();
23173                     this.win.focus();
23174                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
23175                     this.deferFocus();
23176                 }
23177                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23178                     this.cleanUpPaste.defer(100, this);
23179                     return;
23180                 }
23181                 
23182             };
23183         }else if(Roo.isSafari){
23184             return function(e){
23185                 var k = e.getKey();
23186                 
23187                 if(k == e.TAB){
23188                     e.stopEvent();
23189                     this.execCmd('InsertText','\t');
23190                     this.deferFocus();
23191                     return;
23192                 }
23193                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23194                     this.cleanUpPaste.defer(100, this);
23195                     return;
23196                 }
23197                 
23198              };
23199         }
23200     }(),
23201     
23202     getAllAncestors: function()
23203     {
23204         var p = this.getSelectedNode();
23205         var a = [];
23206         if (!p) {
23207             a.push(p); // push blank onto stack..
23208             p = this.getParentElement();
23209         }
23210         
23211         
23212         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
23213             a.push(p);
23214             p = p.parentNode;
23215         }
23216         a.push(this.doc.body);
23217         return a;
23218     },
23219     lastSel : false,
23220     lastSelNode : false,
23221     
23222     
23223     getSelection : function() 
23224     {
23225         this.assignDocWin();
23226         return Roo.isIE ? this.doc.selection : this.win.getSelection();
23227     },
23228     
23229     getSelectedNode: function() 
23230     {
23231         // this may only work on Gecko!!!
23232         
23233         // should we cache this!!!!
23234         
23235         
23236         
23237          
23238         var range = this.createRange(this.getSelection()).cloneRange();
23239         
23240         if (Roo.isIE) {
23241             var parent = range.parentElement();
23242             while (true) {
23243                 var testRange = range.duplicate();
23244                 testRange.moveToElementText(parent);
23245                 if (testRange.inRange(range)) {
23246                     break;
23247                 }
23248                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
23249                     break;
23250                 }
23251                 parent = parent.parentElement;
23252             }
23253             return parent;
23254         }
23255         
23256         // is ancestor a text element.
23257         var ac =  range.commonAncestorContainer;
23258         if (ac.nodeType == 3) {
23259             ac = ac.parentNode;
23260         }
23261         
23262         var ar = ac.childNodes;
23263          
23264         var nodes = [];
23265         var other_nodes = [];
23266         var has_other_nodes = false;
23267         for (var i=0;i<ar.length;i++) {
23268             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
23269                 continue;
23270             }
23271             // fullly contained node.
23272             
23273             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
23274                 nodes.push(ar[i]);
23275                 continue;
23276             }
23277             
23278             // probably selected..
23279             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
23280                 other_nodes.push(ar[i]);
23281                 continue;
23282             }
23283             // outer..
23284             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
23285                 continue;
23286             }
23287             
23288             
23289             has_other_nodes = true;
23290         }
23291         if (!nodes.length && other_nodes.length) {
23292             nodes= other_nodes;
23293         }
23294         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
23295             return false;
23296         }
23297         
23298         return nodes[0];
23299     },
23300     createRange: function(sel)
23301     {
23302         // this has strange effects when using with 
23303         // top toolbar - not sure if it's a great idea.
23304         //this.editor.contentWindow.focus();
23305         if (typeof sel != "undefined") {
23306             try {
23307                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
23308             } catch(e) {
23309                 return this.doc.createRange();
23310             }
23311         } else {
23312             return this.doc.createRange();
23313         }
23314     },
23315     getParentElement: function()
23316     {
23317         
23318         this.assignDocWin();
23319         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
23320         
23321         var range = this.createRange(sel);
23322          
23323         try {
23324             var p = range.commonAncestorContainer;
23325             while (p.nodeType == 3) { // text node
23326                 p = p.parentNode;
23327             }
23328             return p;
23329         } catch (e) {
23330             return null;
23331         }
23332     
23333     },
23334     /***
23335      *
23336      * Range intersection.. the hard stuff...
23337      *  '-1' = before
23338      *  '0' = hits..
23339      *  '1' = after.
23340      *         [ -- selected range --- ]
23341      *   [fail]                        [fail]
23342      *
23343      *    basically..
23344      *      if end is before start or  hits it. fail.
23345      *      if start is after end or hits it fail.
23346      *
23347      *   if either hits (but other is outside. - then it's not 
23348      *   
23349      *    
23350      **/
23351     
23352     
23353     // @see http://www.thismuchiknow.co.uk/?p=64.
23354     rangeIntersectsNode : function(range, node)
23355     {
23356         var nodeRange = node.ownerDocument.createRange();
23357         try {
23358             nodeRange.selectNode(node);
23359         } catch (e) {
23360             nodeRange.selectNodeContents(node);
23361         }
23362     
23363         var rangeStartRange = range.cloneRange();
23364         rangeStartRange.collapse(true);
23365     
23366         var rangeEndRange = range.cloneRange();
23367         rangeEndRange.collapse(false);
23368     
23369         var nodeStartRange = nodeRange.cloneRange();
23370         nodeStartRange.collapse(true);
23371     
23372         var nodeEndRange = nodeRange.cloneRange();
23373         nodeEndRange.collapse(false);
23374     
23375         return rangeStartRange.compareBoundaryPoints(
23376                  Range.START_TO_START, nodeEndRange) == -1 &&
23377                rangeEndRange.compareBoundaryPoints(
23378                  Range.START_TO_START, nodeStartRange) == 1;
23379         
23380          
23381     },
23382     rangeCompareNode : function(range, node)
23383     {
23384         var nodeRange = node.ownerDocument.createRange();
23385         try {
23386             nodeRange.selectNode(node);
23387         } catch (e) {
23388             nodeRange.selectNodeContents(node);
23389         }
23390         
23391         
23392         range.collapse(true);
23393     
23394         nodeRange.collapse(true);
23395      
23396         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23397         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
23398          
23399         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23400         
23401         var nodeIsBefore   =  ss == 1;
23402         var nodeIsAfter    = ee == -1;
23403         
23404         if (nodeIsBefore && nodeIsAfter) {
23405             return 0; // outer
23406         }
23407         if (!nodeIsBefore && nodeIsAfter) {
23408             return 1; //right trailed.
23409         }
23410         
23411         if (nodeIsBefore && !nodeIsAfter) {
23412             return 2;  // left trailed.
23413         }
23414         // fully contined.
23415         return 3;
23416     },
23417
23418     // private? - in a new class?
23419     cleanUpPaste :  function()
23420     {
23421         // cleans up the whole document..
23422         Roo.log('cleanuppaste');
23423         
23424         this.cleanUpChildren(this.doc.body);
23425         var clean = this.cleanWordChars(this.doc.body.innerHTML);
23426         if (clean != this.doc.body.innerHTML) {
23427             this.doc.body.innerHTML = clean;
23428         }
23429         
23430     },
23431     
23432     cleanWordChars : function(input) {// change the chars to hex code
23433         var he = Roo.HtmlEditorCore;
23434         
23435         var output = input;
23436         Roo.each(he.swapCodes, function(sw) { 
23437             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23438             
23439             output = output.replace(swapper, sw[1]);
23440         });
23441         
23442         return output;
23443     },
23444     
23445     
23446     cleanUpChildren : function (n)
23447     {
23448         if (!n.childNodes.length) {
23449             return;
23450         }
23451         for (var i = n.childNodes.length-1; i > -1 ; i--) {
23452            this.cleanUpChild(n.childNodes[i]);
23453         }
23454     },
23455     
23456     
23457         
23458     
23459     cleanUpChild : function (node)
23460     {
23461         var ed = this;
23462         //console.log(node);
23463         if (node.nodeName == "#text") {
23464             // clean up silly Windows -- stuff?
23465             return; 
23466         }
23467         if (node.nodeName == "#comment") {
23468             node.parentNode.removeChild(node);
23469             // clean up silly Windows -- stuff?
23470             return; 
23471         }
23472         var lcname = node.tagName.toLowerCase();
23473         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23474         // whitelist of tags..
23475         
23476         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23477             // remove node.
23478             node.parentNode.removeChild(node);
23479             return;
23480             
23481         }
23482         
23483         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23484         
23485         // spans with no attributes - just remove them..
23486         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
23487             remove_keep_children = true;
23488         }
23489         
23490         // remove <a name=....> as rendering on yahoo mailer is borked with this.
23491         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23492         
23493         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23494         //    remove_keep_children = true;
23495         //}
23496         
23497         if (remove_keep_children) {
23498             this.cleanUpChildren(node);
23499             // inserts everything just before this node...
23500             while (node.childNodes.length) {
23501                 var cn = node.childNodes[0];
23502                 node.removeChild(cn);
23503                 node.parentNode.insertBefore(cn, node);
23504             }
23505             node.parentNode.removeChild(node);
23506             return;
23507         }
23508         
23509         if (!node.attributes || !node.attributes.length) {
23510             
23511           
23512             
23513             
23514             this.cleanUpChildren(node);
23515             return;
23516         }
23517         
23518         function cleanAttr(n,v)
23519         {
23520             
23521             if (v.match(/^\./) || v.match(/^\//)) {
23522                 return;
23523             }
23524             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23525                 return;
23526             }
23527             if (v.match(/^#/)) {
23528                 return;
23529             }
23530 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23531             node.removeAttribute(n);
23532             
23533         }
23534         
23535         var cwhite = this.cwhite;
23536         var cblack = this.cblack;
23537             
23538         function cleanStyle(n,v)
23539         {
23540             if (v.match(/expression/)) { //XSS?? should we even bother..
23541                 node.removeAttribute(n);
23542                 return;
23543             }
23544             
23545             var parts = v.split(/;/);
23546             var clean = [];
23547             
23548             Roo.each(parts, function(p) {
23549                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23550                 if (!p.length) {
23551                     return true;
23552                 }
23553                 var l = p.split(':').shift().replace(/\s+/g,'');
23554                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23555                 
23556                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23557 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23558                     //node.removeAttribute(n);
23559                     return true;
23560                 }
23561                 //Roo.log()
23562                 // only allow 'c whitelisted system attributes'
23563                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23564 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23565                     //node.removeAttribute(n);
23566                     return true;
23567                 }
23568                 
23569                 
23570                  
23571                 
23572                 clean.push(p);
23573                 return true;
23574             });
23575             if (clean.length) { 
23576                 node.setAttribute(n, clean.join(';'));
23577             } else {
23578                 node.removeAttribute(n);
23579             }
23580             
23581         }
23582         
23583         
23584         for (var i = node.attributes.length-1; i > -1 ; i--) {
23585             var a = node.attributes[i];
23586             //console.log(a);
23587             
23588             if (a.name.toLowerCase().substr(0,2)=='on')  {
23589                 node.removeAttribute(a.name);
23590                 continue;
23591             }
23592             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23593                 node.removeAttribute(a.name);
23594                 continue;
23595             }
23596             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23597                 cleanAttr(a.name,a.value); // fixme..
23598                 continue;
23599             }
23600             if (a.name == 'style') {
23601                 cleanStyle(a.name,a.value);
23602                 continue;
23603             }
23604             /// clean up MS crap..
23605             // tecnically this should be a list of valid class'es..
23606             
23607             
23608             if (a.name == 'class') {
23609                 if (a.value.match(/^Mso/)) {
23610                     node.removeAttribute('class');
23611                 }
23612                 
23613                 if (a.value.match(/^body$/)) {
23614                     node.removeAttribute('class');
23615                 }
23616                 continue;
23617             }
23618             
23619             // style cleanup!?
23620             // class cleanup?
23621             
23622         }
23623         
23624         
23625         this.cleanUpChildren(node);
23626         
23627         
23628     },
23629     
23630     /**
23631      * Clean up MS wordisms...
23632      */
23633     cleanWord : function(node)
23634     {
23635         if (!node) {
23636             this.cleanWord(this.doc.body);
23637             return;
23638         }
23639         
23640         if(
23641                 node.nodeName == 'SPAN' &&
23642                 !node.hasAttributes() &&
23643                 node.childNodes.length == 1 &&
23644                 node.firstChild.nodeName == "#text"  
23645         ) {
23646             var textNode = node.firstChild;
23647             node.removeChild(textNode);
23648             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
23649                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
23650             }
23651             node.parentNode.insertBefore(textNode, node);
23652             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
23653                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
23654             }
23655             node.parentNode.removeChild(node);
23656         }
23657         
23658         if (node.nodeName == "#text") {
23659             // clean up silly Windows -- stuff?
23660             return; 
23661         }
23662         if (node.nodeName == "#comment") {
23663             node.parentNode.removeChild(node);
23664             // clean up silly Windows -- stuff?
23665             return; 
23666         }
23667         
23668         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23669             node.parentNode.removeChild(node);
23670             return;
23671         }
23672         //Roo.log(node.tagName);
23673         // remove - but keep children..
23674         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
23675             //Roo.log('-- removed');
23676             while (node.childNodes.length) {
23677                 var cn = node.childNodes[0];
23678                 node.removeChild(cn);
23679                 node.parentNode.insertBefore(cn, node);
23680                 // move node to parent - and clean it..
23681                 this.cleanWord(cn);
23682             }
23683             node.parentNode.removeChild(node);
23684             /// no need to iterate chidlren = it's got none..
23685             //this.iterateChildren(node, this.cleanWord);
23686             return;
23687         }
23688         // clean styles
23689         if (node.className.length) {
23690             
23691             var cn = node.className.split(/\W+/);
23692             var cna = [];
23693             Roo.each(cn, function(cls) {
23694                 if (cls.match(/Mso[a-zA-Z]+/)) {
23695                     return;
23696                 }
23697                 cna.push(cls);
23698             });
23699             node.className = cna.length ? cna.join(' ') : '';
23700             if (!cna.length) {
23701                 node.removeAttribute("class");
23702             }
23703         }
23704         
23705         if (node.hasAttribute("lang")) {
23706             node.removeAttribute("lang");
23707         }
23708         
23709         if (node.hasAttribute("style")) {
23710             
23711             var styles = node.getAttribute("style").split(";");
23712             var nstyle = [];
23713             Roo.each(styles, function(s) {
23714                 if (!s.match(/:/)) {
23715                     return;
23716                 }
23717                 var kv = s.split(":");
23718                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23719                     return;
23720                 }
23721                 // what ever is left... we allow.
23722                 nstyle.push(s);
23723             });
23724             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23725             if (!nstyle.length) {
23726                 node.removeAttribute('style');
23727             }
23728         }
23729         this.iterateChildren(node, this.cleanWord);
23730         
23731         
23732         
23733     },
23734     /**
23735      * iterateChildren of a Node, calling fn each time, using this as the scole..
23736      * @param {DomNode} node node to iterate children of.
23737      * @param {Function} fn method of this class to call on each item.
23738      */
23739     iterateChildren : function(node, fn)
23740     {
23741         if (!node.childNodes.length) {
23742                 return;
23743         }
23744         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23745            fn.call(this, node.childNodes[i])
23746         }
23747     },
23748     
23749     
23750     /**
23751      * cleanTableWidths.
23752      *
23753      * Quite often pasting from word etc.. results in tables with column and widths.
23754      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23755      *
23756      */
23757     cleanTableWidths : function(node)
23758     {
23759          
23760          
23761         if (!node) {
23762             this.cleanTableWidths(this.doc.body);
23763             return;
23764         }
23765         
23766         // ignore list...
23767         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23768             return; 
23769         }
23770         Roo.log(node.tagName);
23771         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23772             this.iterateChildren(node, this.cleanTableWidths);
23773             return;
23774         }
23775         if (node.hasAttribute('width')) {
23776             node.removeAttribute('width');
23777         }
23778         
23779          
23780         if (node.hasAttribute("style")) {
23781             // pretty basic...
23782             
23783             var styles = node.getAttribute("style").split(";");
23784             var nstyle = [];
23785             Roo.each(styles, function(s) {
23786                 if (!s.match(/:/)) {
23787                     return;
23788                 }
23789                 var kv = s.split(":");
23790                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23791                     return;
23792                 }
23793                 // what ever is left... we allow.
23794                 nstyle.push(s);
23795             });
23796             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23797             if (!nstyle.length) {
23798                 node.removeAttribute('style');
23799             }
23800         }
23801         
23802         this.iterateChildren(node, this.cleanTableWidths);
23803         
23804         
23805     },
23806     
23807     
23808     
23809     
23810     domToHTML : function(currentElement, depth, nopadtext) {
23811         
23812         depth = depth || 0;
23813         nopadtext = nopadtext || false;
23814     
23815         if (!currentElement) {
23816             return this.domToHTML(this.doc.body);
23817         }
23818         
23819         //Roo.log(currentElement);
23820         var j;
23821         var allText = false;
23822         var nodeName = currentElement.nodeName;
23823         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23824         
23825         if  (nodeName == '#text') {
23826             
23827             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23828         }
23829         
23830         
23831         var ret = '';
23832         if (nodeName != 'BODY') {
23833              
23834             var i = 0;
23835             // Prints the node tagName, such as <A>, <IMG>, etc
23836             if (tagName) {
23837                 var attr = [];
23838                 for(i = 0; i < currentElement.attributes.length;i++) {
23839                     // quoting?
23840                     var aname = currentElement.attributes.item(i).name;
23841                     if (!currentElement.attributes.item(i).value.length) {
23842                         continue;
23843                     }
23844                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23845                 }
23846                 
23847                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23848             } 
23849             else {
23850                 
23851                 // eack
23852             }
23853         } else {
23854             tagName = false;
23855         }
23856         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23857             return ret;
23858         }
23859         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23860             nopadtext = true;
23861         }
23862         
23863         
23864         // Traverse the tree
23865         i = 0;
23866         var currentElementChild = currentElement.childNodes.item(i);
23867         var allText = true;
23868         var innerHTML  = '';
23869         lastnode = '';
23870         while (currentElementChild) {
23871             // Formatting code (indent the tree so it looks nice on the screen)
23872             var nopad = nopadtext;
23873             if (lastnode == 'SPAN') {
23874                 nopad  = true;
23875             }
23876             // text
23877             if  (currentElementChild.nodeName == '#text') {
23878                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23879                 toadd = nopadtext ? toadd : toadd.trim();
23880                 if (!nopad && toadd.length > 80) {
23881                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23882                 }
23883                 innerHTML  += toadd;
23884                 
23885                 i++;
23886                 currentElementChild = currentElement.childNodes.item(i);
23887                 lastNode = '';
23888                 continue;
23889             }
23890             allText = false;
23891             
23892             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23893                 
23894             // Recursively traverse the tree structure of the child node
23895             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23896             lastnode = currentElementChild.nodeName;
23897             i++;
23898             currentElementChild=currentElement.childNodes.item(i);
23899         }
23900         
23901         ret += innerHTML;
23902         
23903         if (!allText) {
23904                 // The remaining code is mostly for formatting the tree
23905             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23906         }
23907         
23908         
23909         if (tagName) {
23910             ret+= "</"+tagName+">";
23911         }
23912         return ret;
23913         
23914     },
23915         
23916     applyBlacklists : function()
23917     {
23918         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23919         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23920         
23921         this.white = [];
23922         this.black = [];
23923         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23924             if (b.indexOf(tag) > -1) {
23925                 return;
23926             }
23927             this.white.push(tag);
23928             
23929         }, this);
23930         
23931         Roo.each(w, function(tag) {
23932             if (b.indexOf(tag) > -1) {
23933                 return;
23934             }
23935             if (this.white.indexOf(tag) > -1) {
23936                 return;
23937             }
23938             this.white.push(tag);
23939             
23940         }, this);
23941         
23942         
23943         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23944             if (w.indexOf(tag) > -1) {
23945                 return;
23946             }
23947             this.black.push(tag);
23948             
23949         }, this);
23950         
23951         Roo.each(b, function(tag) {
23952             if (w.indexOf(tag) > -1) {
23953                 return;
23954             }
23955             if (this.black.indexOf(tag) > -1) {
23956                 return;
23957             }
23958             this.black.push(tag);
23959             
23960         }, this);
23961         
23962         
23963         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23964         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23965         
23966         this.cwhite = [];
23967         this.cblack = [];
23968         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23969             if (b.indexOf(tag) > -1) {
23970                 return;
23971             }
23972             this.cwhite.push(tag);
23973             
23974         }, this);
23975         
23976         Roo.each(w, function(tag) {
23977             if (b.indexOf(tag) > -1) {
23978                 return;
23979             }
23980             if (this.cwhite.indexOf(tag) > -1) {
23981                 return;
23982             }
23983             this.cwhite.push(tag);
23984             
23985         }, this);
23986         
23987         
23988         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23989             if (w.indexOf(tag) > -1) {
23990                 return;
23991             }
23992             this.cblack.push(tag);
23993             
23994         }, this);
23995         
23996         Roo.each(b, function(tag) {
23997             if (w.indexOf(tag) > -1) {
23998                 return;
23999             }
24000             if (this.cblack.indexOf(tag) > -1) {
24001                 return;
24002             }
24003             this.cblack.push(tag);
24004             
24005         }, this);
24006     },
24007     
24008     setStylesheets : function(stylesheets)
24009     {
24010         if(typeof(stylesheets) == 'string'){
24011             Roo.get(this.iframe.contentDocument.head).createChild({
24012                 tag : 'link',
24013                 rel : 'stylesheet',
24014                 type : 'text/css',
24015                 href : stylesheets
24016             });
24017             
24018             return;
24019         }
24020         var _this = this;
24021      
24022         Roo.each(stylesheets, function(s) {
24023             if(!s.length){
24024                 return;
24025             }
24026             
24027             Roo.get(_this.iframe.contentDocument.head).createChild({
24028                 tag : 'link',
24029                 rel : 'stylesheet',
24030                 type : 'text/css',
24031                 href : s
24032             });
24033         });
24034
24035         
24036     },
24037     
24038     removeStylesheets : function()
24039     {
24040         var _this = this;
24041         
24042         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
24043             s.remove();
24044         });
24045     },
24046     
24047     setStyle : function(style)
24048     {
24049         Roo.get(this.iframe.contentDocument.head).createChild({
24050             tag : 'style',
24051             type : 'text/css',
24052             html : style
24053         });
24054
24055         return;
24056     }
24057     
24058     // hide stuff that is not compatible
24059     /**
24060      * @event blur
24061      * @hide
24062      */
24063     /**
24064      * @event change
24065      * @hide
24066      */
24067     /**
24068      * @event focus
24069      * @hide
24070      */
24071     /**
24072      * @event specialkey
24073      * @hide
24074      */
24075     /**
24076      * @cfg {String} fieldClass @hide
24077      */
24078     /**
24079      * @cfg {String} focusClass @hide
24080      */
24081     /**
24082      * @cfg {String} autoCreate @hide
24083      */
24084     /**
24085      * @cfg {String} inputType @hide
24086      */
24087     /**
24088      * @cfg {String} invalidClass @hide
24089      */
24090     /**
24091      * @cfg {String} invalidText @hide
24092      */
24093     /**
24094      * @cfg {String} msgFx @hide
24095      */
24096     /**
24097      * @cfg {String} validateOnBlur @hide
24098      */
24099 });
24100
24101 Roo.HtmlEditorCore.white = [
24102         'area', 'br', 'img', 'input', 'hr', 'wbr',
24103         
24104        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24105        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24106        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24107        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24108        'table',   'ul',         'xmp', 
24109        
24110        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
24111       'thead',   'tr', 
24112      
24113       'dir', 'menu', 'ol', 'ul', 'dl',
24114        
24115       'embed',  'object'
24116 ];
24117
24118
24119 Roo.HtmlEditorCore.black = [
24120     //    'embed',  'object', // enable - backend responsiblity to clean thiese
24121         'applet', // 
24122         'base',   'basefont', 'bgsound', 'blink',  'body', 
24123         'frame',  'frameset', 'head',    'html',   'ilayer', 
24124         'iframe', 'layer',  'link',     'meta',    'object',   
24125         'script', 'style' ,'title',  'xml' // clean later..
24126 ];
24127 Roo.HtmlEditorCore.clean = [
24128     'script', 'style', 'title', 'xml'
24129 ];
24130 Roo.HtmlEditorCore.remove = [
24131     'font'
24132 ];
24133 // attributes..
24134
24135 Roo.HtmlEditorCore.ablack = [
24136     'on'
24137 ];
24138     
24139 Roo.HtmlEditorCore.aclean = [ 
24140     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
24141 ];
24142
24143 // protocols..
24144 Roo.HtmlEditorCore.pwhite= [
24145         'http',  'https',  'mailto'
24146 ];
24147
24148 // white listed style attributes.
24149 Roo.HtmlEditorCore.cwhite= [
24150       //  'text-align', /// default is to allow most things..
24151       
24152          
24153 //        'font-size'//??
24154 ];
24155
24156 // black listed style attributes.
24157 Roo.HtmlEditorCore.cblack= [
24158       //  'font-size' -- this can be set by the project 
24159 ];
24160
24161
24162 Roo.HtmlEditorCore.swapCodes   =[ 
24163     [    8211, "--" ], 
24164     [    8212, "--" ], 
24165     [    8216,  "'" ],  
24166     [    8217, "'" ],  
24167     [    8220, '"' ],  
24168     [    8221, '"' ],  
24169     [    8226, "*" ],  
24170     [    8230, "..." ]
24171 ]; 
24172
24173     /*
24174  * - LGPL
24175  *
24176  * HtmlEditor
24177  * 
24178  */
24179
24180 /**
24181  * @class Roo.bootstrap.HtmlEditor
24182  * @extends Roo.bootstrap.TextArea
24183  * Bootstrap HtmlEditor class
24184
24185  * @constructor
24186  * Create a new HtmlEditor
24187  * @param {Object} config The config object
24188  */
24189
24190 Roo.bootstrap.HtmlEditor = function(config){
24191     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
24192     if (!this.toolbars) {
24193         this.toolbars = [];
24194     }
24195     
24196     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
24197     this.addEvents({
24198             /**
24199              * @event initialize
24200              * Fires when the editor is fully initialized (including the iframe)
24201              * @param {HtmlEditor} this
24202              */
24203             initialize: true,
24204             /**
24205              * @event activate
24206              * Fires when the editor is first receives the focus. Any insertion must wait
24207              * until after this event.
24208              * @param {HtmlEditor} this
24209              */
24210             activate: true,
24211              /**
24212              * @event beforesync
24213              * Fires before the textarea is updated with content from the editor iframe. Return false
24214              * to cancel the sync.
24215              * @param {HtmlEditor} this
24216              * @param {String} html
24217              */
24218             beforesync: true,
24219              /**
24220              * @event beforepush
24221              * Fires before the iframe editor is updated with content from the textarea. Return false
24222              * to cancel the push.
24223              * @param {HtmlEditor} this
24224              * @param {String} html
24225              */
24226             beforepush: true,
24227              /**
24228              * @event sync
24229              * Fires when the textarea is updated with content from the editor iframe.
24230              * @param {HtmlEditor} this
24231              * @param {String} html
24232              */
24233             sync: true,
24234              /**
24235              * @event push
24236              * Fires when the iframe editor is updated with content from the textarea.
24237              * @param {HtmlEditor} this
24238              * @param {String} html
24239              */
24240             push: true,
24241              /**
24242              * @event editmodechange
24243              * Fires when the editor switches edit modes
24244              * @param {HtmlEditor} this
24245              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24246              */
24247             editmodechange: true,
24248             /**
24249              * @event editorevent
24250              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24251              * @param {HtmlEditor} this
24252              */
24253             editorevent: true,
24254             /**
24255              * @event firstfocus
24256              * Fires when on first focus - needed by toolbars..
24257              * @param {HtmlEditor} this
24258              */
24259             firstfocus: true,
24260             /**
24261              * @event autosave
24262              * Auto save the htmlEditor value as a file into Events
24263              * @param {HtmlEditor} this
24264              */
24265             autosave: true,
24266             /**
24267              * @event savedpreview
24268              * preview the saved version of htmlEditor
24269              * @param {HtmlEditor} this
24270              */
24271             savedpreview: true
24272         });
24273 };
24274
24275
24276 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
24277     
24278     
24279       /**
24280      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24281      */
24282     toolbars : false,
24283     
24284      /**
24285     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
24286     */
24287     btns : [],
24288    
24289      /**
24290      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24291      *                        Roo.resizable.
24292      */
24293     resizable : false,
24294      /**
24295      * @cfg {Number} height (in pixels)
24296      */   
24297     height: 300,
24298    /**
24299      * @cfg {Number} width (in pixels)
24300      */   
24301     width: false,
24302     
24303     /**
24304      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24305      * 
24306      */
24307     stylesheets: false,
24308     
24309     // id of frame..
24310     frameId: false,
24311     
24312     // private properties
24313     validationEvent : false,
24314     deferHeight: true,
24315     initialized : false,
24316     activated : false,
24317     
24318     onFocus : Roo.emptyFn,
24319     iframePad:3,
24320     hideMode:'offsets',
24321     
24322     tbContainer : false,
24323     
24324     bodyCls : '',
24325     
24326     toolbarContainer :function() {
24327         return this.wrap.select('.x-html-editor-tb',true).first();
24328     },
24329
24330     /**
24331      * Protected method that will not generally be called directly. It
24332      * is called when the editor creates its toolbar. Override this method if you need to
24333      * add custom toolbar buttons.
24334      * @param {HtmlEditor} editor
24335      */
24336     createToolbar : function(){
24337         Roo.log('renewing');
24338         Roo.log("create toolbars");
24339         
24340         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
24341         this.toolbars[0].render(this.toolbarContainer());
24342         
24343         return;
24344         
24345 //        if (!editor.toolbars || !editor.toolbars.length) {
24346 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
24347 //        }
24348 //        
24349 //        for (var i =0 ; i < editor.toolbars.length;i++) {
24350 //            editor.toolbars[i] = Roo.factory(
24351 //                    typeof(editor.toolbars[i]) == 'string' ?
24352 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
24353 //                Roo.bootstrap.HtmlEditor);
24354 //            editor.toolbars[i].init(editor);
24355 //        }
24356     },
24357
24358      
24359     // private
24360     onRender : function(ct, position)
24361     {
24362        // Roo.log("Call onRender: " + this.xtype);
24363         var _t = this;
24364         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
24365       
24366         this.wrap = this.inputEl().wrap({
24367             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24368         });
24369         
24370         this.editorcore.onRender(ct, position);
24371          
24372         if (this.resizable) {
24373             this.resizeEl = new Roo.Resizable(this.wrap, {
24374                 pinned : true,
24375                 wrap: true,
24376                 dynamic : true,
24377                 minHeight : this.height,
24378                 height: this.height,
24379                 handles : this.resizable,
24380                 width: this.width,
24381                 listeners : {
24382                     resize : function(r, w, h) {
24383                         _t.onResize(w,h); // -something
24384                     }
24385                 }
24386             });
24387             
24388         }
24389         this.createToolbar(this);
24390        
24391         
24392         if(!this.width && this.resizable){
24393             this.setSize(this.wrap.getSize());
24394         }
24395         if (this.resizeEl) {
24396             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24397             // should trigger onReize..
24398         }
24399         
24400     },
24401
24402     // private
24403     onResize : function(w, h)
24404     {
24405         Roo.log('resize: ' +w + ',' + h );
24406         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
24407         var ew = false;
24408         var eh = false;
24409         
24410         if(this.inputEl() ){
24411             if(typeof w == 'number'){
24412                 var aw = w - this.wrap.getFrameWidth('lr');
24413                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
24414                 ew = aw;
24415             }
24416             if(typeof h == 'number'){
24417                  var tbh = -11;  // fixme it needs to tool bar size!
24418                 for (var i =0; i < this.toolbars.length;i++) {
24419                     // fixme - ask toolbars for heights?
24420                     tbh += this.toolbars[i].el.getHeight();
24421                     //if (this.toolbars[i].footer) {
24422                     //    tbh += this.toolbars[i].footer.el.getHeight();
24423                     //}
24424                 }
24425               
24426                 
24427                 
24428                 
24429                 
24430                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24431                 ah -= 5; // knock a few pixes off for look..
24432                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24433                 var eh = ah;
24434             }
24435         }
24436         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24437         this.editorcore.onResize(ew,eh);
24438         
24439     },
24440
24441     /**
24442      * Toggles the editor between standard and source edit mode.
24443      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24444      */
24445     toggleSourceEdit : function(sourceEditMode)
24446     {
24447         this.editorcore.toggleSourceEdit(sourceEditMode);
24448         
24449         if(this.editorcore.sourceEditMode){
24450             Roo.log('editor - showing textarea');
24451             
24452 //            Roo.log('in');
24453 //            Roo.log(this.syncValue());
24454             this.syncValue();
24455             this.inputEl().removeClass(['hide', 'x-hidden']);
24456             this.inputEl().dom.removeAttribute('tabIndex');
24457             this.inputEl().focus();
24458         }else{
24459             Roo.log('editor - hiding textarea');
24460 //            Roo.log('out')
24461 //            Roo.log(this.pushValue()); 
24462             this.pushValue();
24463             
24464             this.inputEl().addClass(['hide', 'x-hidden']);
24465             this.inputEl().dom.setAttribute('tabIndex', -1);
24466             //this.deferFocus();
24467         }
24468          
24469         if(this.resizable){
24470             this.setSize(this.wrap.getSize());
24471         }
24472         
24473         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24474     },
24475  
24476     // private (for BoxComponent)
24477     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24478
24479     // private (for BoxComponent)
24480     getResizeEl : function(){
24481         return this.wrap;
24482     },
24483
24484     // private (for BoxComponent)
24485     getPositionEl : function(){
24486         return this.wrap;
24487     },
24488
24489     // private
24490     initEvents : function(){
24491         this.originalValue = this.getValue();
24492     },
24493
24494 //    /**
24495 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24496 //     * @method
24497 //     */
24498 //    markInvalid : Roo.emptyFn,
24499 //    /**
24500 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24501 //     * @method
24502 //     */
24503 //    clearInvalid : Roo.emptyFn,
24504
24505     setValue : function(v){
24506         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24507         this.editorcore.pushValue();
24508     },
24509
24510      
24511     // private
24512     deferFocus : function(){
24513         this.focus.defer(10, this);
24514     },
24515
24516     // doc'ed in Field
24517     focus : function(){
24518         this.editorcore.focus();
24519         
24520     },
24521       
24522
24523     // private
24524     onDestroy : function(){
24525         
24526         
24527         
24528         if(this.rendered){
24529             
24530             for (var i =0; i < this.toolbars.length;i++) {
24531                 // fixme - ask toolbars for heights?
24532                 this.toolbars[i].onDestroy();
24533             }
24534             
24535             this.wrap.dom.innerHTML = '';
24536             this.wrap.remove();
24537         }
24538     },
24539
24540     // private
24541     onFirstFocus : function(){
24542         //Roo.log("onFirstFocus");
24543         this.editorcore.onFirstFocus();
24544          for (var i =0; i < this.toolbars.length;i++) {
24545             this.toolbars[i].onFirstFocus();
24546         }
24547         
24548     },
24549     
24550     // private
24551     syncValue : function()
24552     {   
24553         this.editorcore.syncValue();
24554     },
24555     
24556     pushValue : function()
24557     {   
24558         this.editorcore.pushValue();
24559     }
24560      
24561     
24562     // hide stuff that is not compatible
24563     /**
24564      * @event blur
24565      * @hide
24566      */
24567     /**
24568      * @event change
24569      * @hide
24570      */
24571     /**
24572      * @event focus
24573      * @hide
24574      */
24575     /**
24576      * @event specialkey
24577      * @hide
24578      */
24579     /**
24580      * @cfg {String} fieldClass @hide
24581      */
24582     /**
24583      * @cfg {String} focusClass @hide
24584      */
24585     /**
24586      * @cfg {String} autoCreate @hide
24587      */
24588     /**
24589      * @cfg {String} inputType @hide
24590      */
24591      
24592     /**
24593      * @cfg {String} invalidText @hide
24594      */
24595     /**
24596      * @cfg {String} msgFx @hide
24597      */
24598     /**
24599      * @cfg {String} validateOnBlur @hide
24600      */
24601 });
24602  
24603     
24604    
24605    
24606    
24607       
24608 Roo.namespace('Roo.bootstrap.htmleditor');
24609 /**
24610  * @class Roo.bootstrap.HtmlEditorToolbar1
24611  * Basic Toolbar
24612  * 
24613  * @example
24614  * Usage:
24615  *
24616  new Roo.bootstrap.HtmlEditor({
24617     ....
24618     toolbars : [
24619         new Roo.bootstrap.HtmlEditorToolbar1({
24620             disable : { fonts: 1 , format: 1, ..., ... , ...],
24621             btns : [ .... ]
24622         })
24623     }
24624      
24625  * 
24626  * @cfg {Object} disable List of elements to disable..
24627  * @cfg {Array} btns List of additional buttons.
24628  * 
24629  * 
24630  * NEEDS Extra CSS? 
24631  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24632  */
24633  
24634 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24635 {
24636     
24637     Roo.apply(this, config);
24638     
24639     // default disabled, based on 'good practice'..
24640     this.disable = this.disable || {};
24641     Roo.applyIf(this.disable, {
24642         fontSize : true,
24643         colors : true,
24644         specialElements : true
24645     });
24646     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24647     
24648     this.editor = config.editor;
24649     this.editorcore = config.editor.editorcore;
24650     
24651     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24652     
24653     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24654     // dont call parent... till later.
24655 }
24656 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24657      
24658     bar : true,
24659     
24660     editor : false,
24661     editorcore : false,
24662     
24663     
24664     formats : [
24665         "p" ,  
24666         "h1","h2","h3","h4","h5","h6", 
24667         "pre", "code", 
24668         "abbr", "acronym", "address", "cite", "samp", "var",
24669         'div','span'
24670     ],
24671     
24672     onRender : function(ct, position)
24673     {
24674        // Roo.log("Call onRender: " + this.xtype);
24675         
24676        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24677        Roo.log(this.el);
24678        this.el.dom.style.marginBottom = '0';
24679        var _this = this;
24680        var editorcore = this.editorcore;
24681        var editor= this.editor;
24682        
24683        var children = [];
24684        var btn = function(id,cmd , toggle, handler, html){
24685        
24686             var  event = toggle ? 'toggle' : 'click';
24687        
24688             var a = {
24689                 size : 'sm',
24690                 xtype: 'Button',
24691                 xns: Roo.bootstrap,
24692                 //glyphicon : id,
24693                 fa: id,
24694                 cmd : id || cmd,
24695                 enableToggle:toggle !== false,
24696                 html : html || '',
24697                 pressed : toggle ? false : null,
24698                 listeners : {}
24699             };
24700             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24701                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24702             };
24703             children.push(a);
24704             return a;
24705        }
24706        
24707     //    var cb_box = function...
24708         
24709         var style = {
24710                 xtype: 'Button',
24711                 size : 'sm',
24712                 xns: Roo.bootstrap,
24713                 fa : 'font',
24714                 //html : 'submit'
24715                 menu : {
24716                     xtype: 'Menu',
24717                     xns: Roo.bootstrap,
24718                     items:  []
24719                 }
24720         };
24721         Roo.each(this.formats, function(f) {
24722             style.menu.items.push({
24723                 xtype :'MenuItem',
24724                 xns: Roo.bootstrap,
24725                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24726                 tagname : f,
24727                 listeners : {
24728                     click : function()
24729                     {
24730                         editorcore.insertTag(this.tagname);
24731                         editor.focus();
24732                     }
24733                 }
24734                 
24735             });
24736         });
24737         children.push(style);   
24738         
24739         btn('bold',false,true);
24740         btn('italic',false,true);
24741         btn('align-left', 'justifyleft',true);
24742         btn('align-center', 'justifycenter',true);
24743         btn('align-right' , 'justifyright',true);
24744         btn('link', false, false, function(btn) {
24745             //Roo.log("create link?");
24746             var url = prompt(this.createLinkText, this.defaultLinkValue);
24747             if(url && url != 'http:/'+'/'){
24748                 this.editorcore.relayCmd('createlink', url);
24749             }
24750         }),
24751         btn('list','insertunorderedlist',true);
24752         btn('pencil', false,true, function(btn){
24753                 Roo.log(this);
24754                 this.toggleSourceEdit(btn.pressed);
24755         });
24756         
24757         if (this.editor.btns.length > 0) {
24758             for (var i = 0; i<this.editor.btns.length; i++) {
24759                 children.push(this.editor.btns[i]);
24760             }
24761         }
24762         
24763         /*
24764         var cog = {
24765                 xtype: 'Button',
24766                 size : 'sm',
24767                 xns: Roo.bootstrap,
24768                 glyphicon : 'cog',
24769                 //html : 'submit'
24770                 menu : {
24771                     xtype: 'Menu',
24772                     xns: Roo.bootstrap,
24773                     items:  []
24774                 }
24775         };
24776         
24777         cog.menu.items.push({
24778             xtype :'MenuItem',
24779             xns: Roo.bootstrap,
24780             html : Clean styles,
24781             tagname : f,
24782             listeners : {
24783                 click : function()
24784                 {
24785                     editorcore.insertTag(this.tagname);
24786                     editor.focus();
24787                 }
24788             }
24789             
24790         });
24791        */
24792         
24793          
24794        this.xtype = 'NavSimplebar';
24795         
24796         for(var i=0;i< children.length;i++) {
24797             
24798             this.buttons.add(this.addxtypeChild(children[i]));
24799             
24800         }
24801         
24802         editor.on('editorevent', this.updateToolbar, this);
24803     },
24804     onBtnClick : function(id)
24805     {
24806        this.editorcore.relayCmd(id);
24807        this.editorcore.focus();
24808     },
24809     
24810     /**
24811      * Protected method that will not generally be called directly. It triggers
24812      * a toolbar update by reading the markup state of the current selection in the editor.
24813      */
24814     updateToolbar: function(){
24815
24816         if(!this.editorcore.activated){
24817             this.editor.onFirstFocus(); // is this neeed?
24818             return;
24819         }
24820
24821         var btns = this.buttons; 
24822         var doc = this.editorcore.doc;
24823         btns.get('bold').setActive(doc.queryCommandState('bold'));
24824         btns.get('italic').setActive(doc.queryCommandState('italic'));
24825         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24826         
24827         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24828         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24829         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24830         
24831         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24832         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24833          /*
24834         
24835         var ans = this.editorcore.getAllAncestors();
24836         if (this.formatCombo) {
24837             
24838             
24839             var store = this.formatCombo.store;
24840             this.formatCombo.setValue("");
24841             for (var i =0; i < ans.length;i++) {
24842                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24843                     // select it..
24844                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24845                     break;
24846                 }
24847             }
24848         }
24849         
24850         
24851         
24852         // hides menus... - so this cant be on a menu...
24853         Roo.bootstrap.MenuMgr.hideAll();
24854         */
24855         Roo.bootstrap.MenuMgr.hideAll();
24856         //this.editorsyncValue();
24857     },
24858     onFirstFocus: function() {
24859         this.buttons.each(function(item){
24860            item.enable();
24861         });
24862     },
24863     toggleSourceEdit : function(sourceEditMode){
24864         
24865           
24866         if(sourceEditMode){
24867             Roo.log("disabling buttons");
24868            this.buttons.each( function(item){
24869                 if(item.cmd != 'pencil'){
24870                     item.disable();
24871                 }
24872             });
24873           
24874         }else{
24875             Roo.log("enabling buttons");
24876             if(this.editorcore.initialized){
24877                 this.buttons.each( function(item){
24878                     item.enable();
24879                 });
24880             }
24881             
24882         }
24883         Roo.log("calling toggole on editor");
24884         // tell the editor that it's been pressed..
24885         this.editor.toggleSourceEdit(sourceEditMode);
24886        
24887     }
24888 });
24889
24890
24891
24892
24893
24894 /**
24895  * @class Roo.bootstrap.Table.AbstractSelectionModel
24896  * @extends Roo.util.Observable
24897  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24898  * implemented by descendant classes.  This class should not be directly instantiated.
24899  * @constructor
24900  */
24901 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24902     this.locked = false;
24903     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24904 };
24905
24906
24907 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24908     /** @ignore Called by the grid automatically. Do not call directly. */
24909     init : function(grid){
24910         this.grid = grid;
24911         this.initEvents();
24912     },
24913
24914     /**
24915      * Locks the selections.
24916      */
24917     lock : function(){
24918         this.locked = true;
24919     },
24920
24921     /**
24922      * Unlocks the selections.
24923      */
24924     unlock : function(){
24925         this.locked = false;
24926     },
24927
24928     /**
24929      * Returns true if the selections are locked.
24930      * @return {Boolean}
24931      */
24932     isLocked : function(){
24933         return this.locked;
24934     },
24935     
24936     
24937     initEvents : function ()
24938     {
24939         
24940     }
24941 });
24942 /**
24943  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24944  * @class Roo.bootstrap.Table.RowSelectionModel
24945  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24946  * It supports multiple selections and keyboard selection/navigation. 
24947  * @constructor
24948  * @param {Object} config
24949  */
24950
24951 Roo.bootstrap.Table.RowSelectionModel = function(config){
24952     Roo.apply(this, config);
24953     this.selections = new Roo.util.MixedCollection(false, function(o){
24954         return o.id;
24955     });
24956
24957     this.last = false;
24958     this.lastActive = false;
24959
24960     this.addEvents({
24961         /**
24962              * @event selectionchange
24963              * Fires when the selection changes
24964              * @param {SelectionModel} this
24965              */
24966             "selectionchange" : true,
24967         /**
24968              * @event afterselectionchange
24969              * Fires after the selection changes (eg. by key press or clicking)
24970              * @param {SelectionModel} this
24971              */
24972             "afterselectionchange" : true,
24973         /**
24974              * @event beforerowselect
24975              * Fires when a row is selected being selected, return false to cancel.
24976              * @param {SelectionModel} this
24977              * @param {Number} rowIndex The selected index
24978              * @param {Boolean} keepExisting False if other selections will be cleared
24979              */
24980             "beforerowselect" : true,
24981         /**
24982              * @event rowselect
24983              * Fires when a row is selected.
24984              * @param {SelectionModel} this
24985              * @param {Number} rowIndex The selected index
24986              * @param {Roo.data.Record} r The record
24987              */
24988             "rowselect" : true,
24989         /**
24990              * @event rowdeselect
24991              * Fires when a row is deselected.
24992              * @param {SelectionModel} this
24993              * @param {Number} rowIndex The selected index
24994              */
24995         "rowdeselect" : true
24996     });
24997     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24998     this.locked = false;
24999  };
25000
25001 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
25002     /**
25003      * @cfg {Boolean} singleSelect
25004      * True to allow selection of only one row at a time (defaults to false)
25005      */
25006     singleSelect : false,
25007
25008     // private
25009     initEvents : function()
25010     {
25011
25012         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
25013         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
25014         //}else{ // allow click to work like normal
25015          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
25016         //}
25017         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
25018         this.grid.on("rowclick", this.handleMouseDown, this);
25019         
25020         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
25021             "up" : function(e){
25022                 if(!e.shiftKey){
25023                     this.selectPrevious(e.shiftKey);
25024                 }else if(this.last !== false && this.lastActive !== false){
25025                     var last = this.last;
25026                     this.selectRange(this.last,  this.lastActive-1);
25027                     this.grid.getView().focusRow(this.lastActive);
25028                     if(last !== false){
25029                         this.last = last;
25030                     }
25031                 }else{
25032                     this.selectFirstRow();
25033                 }
25034                 this.fireEvent("afterselectionchange", this);
25035             },
25036             "down" : function(e){
25037                 if(!e.shiftKey){
25038                     this.selectNext(e.shiftKey);
25039                 }else if(this.last !== false && this.lastActive !== false){
25040                     var last = this.last;
25041                     this.selectRange(this.last,  this.lastActive+1);
25042                     this.grid.getView().focusRow(this.lastActive);
25043                     if(last !== false){
25044                         this.last = last;
25045                     }
25046                 }else{
25047                     this.selectFirstRow();
25048                 }
25049                 this.fireEvent("afterselectionchange", this);
25050             },
25051             scope: this
25052         });
25053         this.grid.store.on('load', function(){
25054             this.selections.clear();
25055         },this);
25056         /*
25057         var view = this.grid.view;
25058         view.on("refresh", this.onRefresh, this);
25059         view.on("rowupdated", this.onRowUpdated, this);
25060         view.on("rowremoved", this.onRemove, this);
25061         */
25062     },
25063
25064     // private
25065     onRefresh : function()
25066     {
25067         var ds = this.grid.store, i, v = this.grid.view;
25068         var s = this.selections;
25069         s.each(function(r){
25070             if((i = ds.indexOfId(r.id)) != -1){
25071                 v.onRowSelect(i);
25072             }else{
25073                 s.remove(r);
25074             }
25075         });
25076     },
25077
25078     // private
25079     onRemove : function(v, index, r){
25080         this.selections.remove(r);
25081     },
25082
25083     // private
25084     onRowUpdated : function(v, index, r){
25085         if(this.isSelected(r)){
25086             v.onRowSelect(index);
25087         }
25088     },
25089
25090     /**
25091      * Select records.
25092      * @param {Array} records The records to select
25093      * @param {Boolean} keepExisting (optional) True to keep existing selections
25094      */
25095     selectRecords : function(records, keepExisting)
25096     {
25097         if(!keepExisting){
25098             this.clearSelections();
25099         }
25100             var ds = this.grid.store;
25101         for(var i = 0, len = records.length; i < len; i++){
25102             this.selectRow(ds.indexOf(records[i]), true);
25103         }
25104     },
25105
25106     /**
25107      * Gets the number of selected rows.
25108      * @return {Number}
25109      */
25110     getCount : function(){
25111         return this.selections.length;
25112     },
25113
25114     /**
25115      * Selects the first row in the grid.
25116      */
25117     selectFirstRow : function(){
25118         this.selectRow(0);
25119     },
25120
25121     /**
25122      * Select the last row.
25123      * @param {Boolean} keepExisting (optional) True to keep existing selections
25124      */
25125     selectLastRow : function(keepExisting){
25126         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
25127         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
25128     },
25129
25130     /**
25131      * Selects the row immediately following the last selected row.
25132      * @param {Boolean} keepExisting (optional) True to keep existing selections
25133      */
25134     selectNext : function(keepExisting)
25135     {
25136             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
25137             this.selectRow(this.last+1, keepExisting);
25138             this.grid.getView().focusRow(this.last);
25139         }
25140     },
25141
25142     /**
25143      * Selects the row that precedes the last selected row.
25144      * @param {Boolean} keepExisting (optional) True to keep existing selections
25145      */
25146     selectPrevious : function(keepExisting){
25147         if(this.last){
25148             this.selectRow(this.last-1, keepExisting);
25149             this.grid.getView().focusRow(this.last);
25150         }
25151     },
25152
25153     /**
25154      * Returns the selected records
25155      * @return {Array} Array of selected records
25156      */
25157     getSelections : function(){
25158         return [].concat(this.selections.items);
25159     },
25160
25161     /**
25162      * Returns the first selected record.
25163      * @return {Record}
25164      */
25165     getSelected : function(){
25166         return this.selections.itemAt(0);
25167     },
25168
25169
25170     /**
25171      * Clears all selections.
25172      */
25173     clearSelections : function(fast)
25174     {
25175         if(this.locked) {
25176             return;
25177         }
25178         if(fast !== true){
25179                 var ds = this.grid.store;
25180             var s = this.selections;
25181             s.each(function(r){
25182                 this.deselectRow(ds.indexOfId(r.id));
25183             }, this);
25184             s.clear();
25185         }else{
25186             this.selections.clear();
25187         }
25188         this.last = false;
25189     },
25190
25191
25192     /**
25193      * Selects all rows.
25194      */
25195     selectAll : function(){
25196         if(this.locked) {
25197             return;
25198         }
25199         this.selections.clear();
25200         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
25201             this.selectRow(i, true);
25202         }
25203     },
25204
25205     /**
25206      * Returns True if there is a selection.
25207      * @return {Boolean}
25208      */
25209     hasSelection : function(){
25210         return this.selections.length > 0;
25211     },
25212
25213     /**
25214      * Returns True if the specified row is selected.
25215      * @param {Number/Record} record The record or index of the record to check
25216      * @return {Boolean}
25217      */
25218     isSelected : function(index){
25219             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
25220         return (r && this.selections.key(r.id) ? true : false);
25221     },
25222
25223     /**
25224      * Returns True if the specified record id is selected.
25225      * @param {String} id The id of record to check
25226      * @return {Boolean}
25227      */
25228     isIdSelected : function(id){
25229         return (this.selections.key(id) ? true : false);
25230     },
25231
25232
25233     // private
25234     handleMouseDBClick : function(e, t){
25235         
25236     },
25237     // private
25238     handleMouseDown : function(e, t)
25239     {
25240             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
25241         if(this.isLocked() || rowIndex < 0 ){
25242             return;
25243         };
25244         if(e.shiftKey && this.last !== false){
25245             var last = this.last;
25246             this.selectRange(last, rowIndex, e.ctrlKey);
25247             this.last = last; // reset the last
25248             t.focus();
25249     
25250         }else{
25251             var isSelected = this.isSelected(rowIndex);
25252             //Roo.log("select row:" + rowIndex);
25253             if(isSelected){
25254                 this.deselectRow(rowIndex);
25255             } else {
25256                         this.selectRow(rowIndex, true);
25257             }
25258     
25259             /*
25260                 if(e.button !== 0 && isSelected){
25261                 alert('rowIndex 2: ' + rowIndex);
25262                     view.focusRow(rowIndex);
25263                 }else if(e.ctrlKey && isSelected){
25264                     this.deselectRow(rowIndex);
25265                 }else if(!isSelected){
25266                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
25267                     view.focusRow(rowIndex);
25268                 }
25269             */
25270         }
25271         this.fireEvent("afterselectionchange", this);
25272     },
25273     // private
25274     handleDragableRowClick :  function(grid, rowIndex, e) 
25275     {
25276         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
25277             this.selectRow(rowIndex, false);
25278             grid.view.focusRow(rowIndex);
25279              this.fireEvent("afterselectionchange", this);
25280         }
25281     },
25282     
25283     /**
25284      * Selects multiple rows.
25285      * @param {Array} rows Array of the indexes of the row to select
25286      * @param {Boolean} keepExisting (optional) True to keep existing selections
25287      */
25288     selectRows : function(rows, keepExisting){
25289         if(!keepExisting){
25290             this.clearSelections();
25291         }
25292         for(var i = 0, len = rows.length; i < len; i++){
25293             this.selectRow(rows[i], true);
25294         }
25295     },
25296
25297     /**
25298      * Selects a range of rows. All rows in between startRow and endRow are also selected.
25299      * @param {Number} startRow The index of the first row in the range
25300      * @param {Number} endRow The index of the last row in the range
25301      * @param {Boolean} keepExisting (optional) True to retain existing selections
25302      */
25303     selectRange : function(startRow, endRow, keepExisting){
25304         if(this.locked) {
25305             return;
25306         }
25307         if(!keepExisting){
25308             this.clearSelections();
25309         }
25310         if(startRow <= endRow){
25311             for(var i = startRow; i <= endRow; i++){
25312                 this.selectRow(i, true);
25313             }
25314         }else{
25315             for(var i = startRow; i >= endRow; i--){
25316                 this.selectRow(i, true);
25317             }
25318         }
25319     },
25320
25321     /**
25322      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
25323      * @param {Number} startRow The index of the first row in the range
25324      * @param {Number} endRow The index of the last row in the range
25325      */
25326     deselectRange : function(startRow, endRow, preventViewNotify){
25327         if(this.locked) {
25328             return;
25329         }
25330         for(var i = startRow; i <= endRow; i++){
25331             this.deselectRow(i, preventViewNotify);
25332         }
25333     },
25334
25335     /**
25336      * Selects a row.
25337      * @param {Number} row The index of the row to select
25338      * @param {Boolean} keepExisting (optional) True to keep existing selections
25339      */
25340     selectRow : function(index, keepExisting, preventViewNotify)
25341     {
25342             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
25343             return;
25344         }
25345         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
25346             if(!keepExisting || this.singleSelect){
25347                 this.clearSelections();
25348             }
25349             
25350             var r = this.grid.store.getAt(index);
25351             //console.log('selectRow - record id :' + r.id);
25352             
25353             this.selections.add(r);
25354             this.last = this.lastActive = index;
25355             if(!preventViewNotify){
25356                 var proxy = new Roo.Element(
25357                                 this.grid.getRowDom(index)
25358                 );
25359                 proxy.addClass('bg-info info');
25360             }
25361             this.fireEvent("rowselect", this, index, r);
25362             this.fireEvent("selectionchange", this);
25363         }
25364     },
25365
25366     /**
25367      * Deselects a row.
25368      * @param {Number} row The index of the row to deselect
25369      */
25370     deselectRow : function(index, preventViewNotify)
25371     {
25372         if(this.locked) {
25373             return;
25374         }
25375         if(this.last == index){
25376             this.last = false;
25377         }
25378         if(this.lastActive == index){
25379             this.lastActive = false;
25380         }
25381         
25382         var r = this.grid.store.getAt(index);
25383         if (!r) {
25384             return;
25385         }
25386         
25387         this.selections.remove(r);
25388         //.console.log('deselectRow - record id :' + r.id);
25389         if(!preventViewNotify){
25390         
25391             var proxy = new Roo.Element(
25392                 this.grid.getRowDom(index)
25393             );
25394             proxy.removeClass('bg-info info');
25395         }
25396         this.fireEvent("rowdeselect", this, index);
25397         this.fireEvent("selectionchange", this);
25398     },
25399
25400     // private
25401     restoreLast : function(){
25402         if(this._last){
25403             this.last = this._last;
25404         }
25405     },
25406
25407     // private
25408     acceptsNav : function(row, col, cm){
25409         return !cm.isHidden(col) && cm.isCellEditable(col, row);
25410     },
25411
25412     // private
25413     onEditorKey : function(field, e){
25414         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
25415         if(k == e.TAB){
25416             e.stopEvent();
25417             ed.completeEdit();
25418             if(e.shiftKey){
25419                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
25420             }else{
25421                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
25422             }
25423         }else if(k == e.ENTER && !e.ctrlKey){
25424             e.stopEvent();
25425             ed.completeEdit();
25426             if(e.shiftKey){
25427                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25428             }else{
25429                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25430             }
25431         }else if(k == e.ESC){
25432             ed.cancelEdit();
25433         }
25434         if(newCell){
25435             g.startEditing(newCell[0], newCell[1]);
25436         }
25437     }
25438 });
25439 /*
25440  * Based on:
25441  * Ext JS Library 1.1.1
25442  * Copyright(c) 2006-2007, Ext JS, LLC.
25443  *
25444  * Originally Released Under LGPL - original licence link has changed is not relivant.
25445  *
25446  * Fork - LGPL
25447  * <script type="text/javascript">
25448  */
25449  
25450 /**
25451  * @class Roo.bootstrap.PagingToolbar
25452  * @extends Roo.bootstrap.NavSimplebar
25453  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
25454  * @constructor
25455  * Create a new PagingToolbar
25456  * @param {Object} config The config object
25457  * @param {Roo.data.Store} store
25458  */
25459 Roo.bootstrap.PagingToolbar = function(config)
25460 {
25461     // old args format still supported... - xtype is prefered..
25462         // created from xtype...
25463     
25464     this.ds = config.dataSource;
25465     
25466     if (config.store && !this.ds) {
25467         this.store= Roo.factory(config.store, Roo.data);
25468         this.ds = this.store;
25469         this.ds.xmodule = this.xmodule || false;
25470     }
25471     
25472     this.toolbarItems = [];
25473     if (config.items) {
25474         this.toolbarItems = config.items;
25475     }
25476     
25477     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25478     
25479     this.cursor = 0;
25480     
25481     if (this.ds) { 
25482         this.bind(this.ds);
25483     }
25484     
25485     if (Roo.bootstrap.version == 4) {
25486         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
25487     } else {
25488         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
25489     }
25490     
25491 };
25492
25493 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
25494     /**
25495      * @cfg {Roo.data.Store} dataSource
25496      * The underlying data store providing the paged data
25497      */
25498     /**
25499      * @cfg {String/HTMLElement/Element} container
25500      * container The id or element that will contain the toolbar
25501      */
25502     /**
25503      * @cfg {Boolean} displayInfo
25504      * True to display the displayMsg (defaults to false)
25505      */
25506     /**
25507      * @cfg {Number} pageSize
25508      * The number of records to display per page (defaults to 20)
25509      */
25510     pageSize: 20,
25511     /**
25512      * @cfg {String} displayMsg
25513      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25514      */
25515     displayMsg : 'Displaying {0} - {1} of {2}',
25516     /**
25517      * @cfg {String} emptyMsg
25518      * The message to display when no records are found (defaults to "No data to display")
25519      */
25520     emptyMsg : 'No data to display',
25521     /**
25522      * Customizable piece of the default paging text (defaults to "Page")
25523      * @type String
25524      */
25525     beforePageText : "Page",
25526     /**
25527      * Customizable piece of the default paging text (defaults to "of %0")
25528      * @type String
25529      */
25530     afterPageText : "of {0}",
25531     /**
25532      * Customizable piece of the default paging text (defaults to "First Page")
25533      * @type String
25534      */
25535     firstText : "First Page",
25536     /**
25537      * Customizable piece of the default paging text (defaults to "Previous Page")
25538      * @type String
25539      */
25540     prevText : "Previous Page",
25541     /**
25542      * Customizable piece of the default paging text (defaults to "Next Page")
25543      * @type String
25544      */
25545     nextText : "Next Page",
25546     /**
25547      * Customizable piece of the default paging text (defaults to "Last Page")
25548      * @type String
25549      */
25550     lastText : "Last Page",
25551     /**
25552      * Customizable piece of the default paging text (defaults to "Refresh")
25553      * @type String
25554      */
25555     refreshText : "Refresh",
25556
25557     buttons : false,
25558     // private
25559     onRender : function(ct, position) 
25560     {
25561         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25562         this.navgroup.parentId = this.id;
25563         this.navgroup.onRender(this.el, null);
25564         // add the buttons to the navgroup
25565         
25566         if(this.displayInfo){
25567             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25568             this.displayEl = this.el.select('.x-paging-info', true).first();
25569 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25570 //            this.displayEl = navel.el.select('span',true).first();
25571         }
25572         
25573         var _this = this;
25574         
25575         if(this.buttons){
25576             Roo.each(_this.buttons, function(e){ // this might need to use render????
25577                Roo.factory(e).render(_this.el);
25578             });
25579         }
25580             
25581         Roo.each(_this.toolbarItems, function(e) {
25582             _this.navgroup.addItem(e);
25583         });
25584         
25585         
25586         this.first = this.navgroup.addItem({
25587             tooltip: this.firstText,
25588             cls: "prev btn-outline-secondary",
25589             html : ' <i class="fa fa-step-backward"></i>',
25590             disabled: true,
25591             preventDefault: true,
25592             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25593         });
25594         
25595         this.prev =  this.navgroup.addItem({
25596             tooltip: this.prevText,
25597             cls: "prev btn-outline-secondary",
25598             html : ' <i class="fa fa-backward"></i>',
25599             disabled: true,
25600             preventDefault: true,
25601             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25602         });
25603     //this.addSeparator();
25604         
25605         
25606         var field = this.navgroup.addItem( {
25607             tagtype : 'span',
25608             cls : 'x-paging-position  btn-outline-secondary',
25609              disabled: true,
25610             html : this.beforePageText  +
25611                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25612                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25613          } ); //?? escaped?
25614         
25615         this.field = field.el.select('input', true).first();
25616         this.field.on("keydown", this.onPagingKeydown, this);
25617         this.field.on("focus", function(){this.dom.select();});
25618     
25619     
25620         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25621         //this.field.setHeight(18);
25622         //this.addSeparator();
25623         this.next = this.navgroup.addItem({
25624             tooltip: this.nextText,
25625             cls: "next btn-outline-secondary",
25626             html : ' <i class="fa fa-forward"></i>',
25627             disabled: true,
25628             preventDefault: true,
25629             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25630         });
25631         this.last = this.navgroup.addItem({
25632             tooltip: this.lastText,
25633             html : ' <i class="fa fa-step-forward"></i>',
25634             cls: "next btn-outline-secondary",
25635             disabled: true,
25636             preventDefault: true,
25637             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25638         });
25639     //this.addSeparator();
25640         this.loading = this.navgroup.addItem({
25641             tooltip: this.refreshText,
25642             cls: "btn-outline-secondary",
25643             html : ' <i class="fa fa-refresh"></i>',
25644             preventDefault: true,
25645             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25646         });
25647         
25648     },
25649
25650     // private
25651     updateInfo : function(){
25652         if(this.displayEl){
25653             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25654             var msg = count == 0 ?
25655                 this.emptyMsg :
25656                 String.format(
25657                     this.displayMsg,
25658                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25659                 );
25660             this.displayEl.update(msg);
25661         }
25662     },
25663
25664     // private
25665     onLoad : function(ds, r, o)
25666     {
25667         this.cursor = o.params.start ? o.params.start : 0;
25668         
25669         var d = this.getPageData(),
25670             ap = d.activePage,
25671             ps = d.pages;
25672         
25673         
25674         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25675         this.field.dom.value = ap;
25676         this.first.setDisabled(ap == 1);
25677         this.prev.setDisabled(ap == 1);
25678         this.next.setDisabled(ap == ps);
25679         this.last.setDisabled(ap == ps);
25680         this.loading.enable();
25681         this.updateInfo();
25682     },
25683
25684     // private
25685     getPageData : function(){
25686         var total = this.ds.getTotalCount();
25687         return {
25688             total : total,
25689             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25690             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25691         };
25692     },
25693
25694     // private
25695     onLoadError : function(){
25696         this.loading.enable();
25697     },
25698
25699     // private
25700     onPagingKeydown : function(e){
25701         var k = e.getKey();
25702         var d = this.getPageData();
25703         if(k == e.RETURN){
25704             var v = this.field.dom.value, pageNum;
25705             if(!v || isNaN(pageNum = parseInt(v, 10))){
25706                 this.field.dom.value = d.activePage;
25707                 return;
25708             }
25709             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25710             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25711             e.stopEvent();
25712         }
25713         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))
25714         {
25715           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25716           this.field.dom.value = pageNum;
25717           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25718           e.stopEvent();
25719         }
25720         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25721         {
25722           var v = this.field.dom.value, pageNum; 
25723           var increment = (e.shiftKey) ? 10 : 1;
25724           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25725                 increment *= -1;
25726           }
25727           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25728             this.field.dom.value = d.activePage;
25729             return;
25730           }
25731           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25732           {
25733             this.field.dom.value = parseInt(v, 10) + increment;
25734             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25735             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25736           }
25737           e.stopEvent();
25738         }
25739     },
25740
25741     // private
25742     beforeLoad : function(){
25743         if(this.loading){
25744             this.loading.disable();
25745         }
25746     },
25747
25748     // private
25749     onClick : function(which){
25750         
25751         var ds = this.ds;
25752         if (!ds) {
25753             return;
25754         }
25755         
25756         switch(which){
25757             case "first":
25758                 ds.load({params:{start: 0, limit: this.pageSize}});
25759             break;
25760             case "prev":
25761                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25762             break;
25763             case "next":
25764                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25765             break;
25766             case "last":
25767                 var total = ds.getTotalCount();
25768                 var extra = total % this.pageSize;
25769                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25770                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25771             break;
25772             case "refresh":
25773                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25774             break;
25775         }
25776     },
25777
25778     /**
25779      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25780      * @param {Roo.data.Store} store The data store to unbind
25781      */
25782     unbind : function(ds){
25783         ds.un("beforeload", this.beforeLoad, this);
25784         ds.un("load", this.onLoad, this);
25785         ds.un("loadexception", this.onLoadError, this);
25786         ds.un("remove", this.updateInfo, this);
25787         ds.un("add", this.updateInfo, this);
25788         this.ds = undefined;
25789     },
25790
25791     /**
25792      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25793      * @param {Roo.data.Store} store The data store to bind
25794      */
25795     bind : function(ds){
25796         ds.on("beforeload", this.beforeLoad, this);
25797         ds.on("load", this.onLoad, this);
25798         ds.on("loadexception", this.onLoadError, this);
25799         ds.on("remove", this.updateInfo, this);
25800         ds.on("add", this.updateInfo, this);
25801         this.ds = ds;
25802     }
25803 });/*
25804  * - LGPL
25805  *
25806  * element
25807  * 
25808  */
25809
25810 /**
25811  * @class Roo.bootstrap.MessageBar
25812  * @extends Roo.bootstrap.Component
25813  * Bootstrap MessageBar class
25814  * @cfg {String} html contents of the MessageBar
25815  * @cfg {String} weight (info | success | warning | danger) default info
25816  * @cfg {String} beforeClass insert the bar before the given class
25817  * @cfg {Boolean} closable (true | false) default false
25818  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25819  * 
25820  * @constructor
25821  * Create a new Element
25822  * @param {Object} config The config object
25823  */
25824
25825 Roo.bootstrap.MessageBar = function(config){
25826     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25827 };
25828
25829 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25830     
25831     html: '',
25832     weight: 'info',
25833     closable: false,
25834     fixed: false,
25835     beforeClass: 'bootstrap-sticky-wrap',
25836     
25837     getAutoCreate : function(){
25838         
25839         var cfg = {
25840             tag: 'div',
25841             cls: 'alert alert-dismissable alert-' + this.weight,
25842             cn: [
25843                 {
25844                     tag: 'span',
25845                     cls: 'message',
25846                     html: this.html || ''
25847                 }
25848             ]
25849         };
25850         
25851         if(this.fixed){
25852             cfg.cls += ' alert-messages-fixed';
25853         }
25854         
25855         if(this.closable){
25856             cfg.cn.push({
25857                 tag: 'button',
25858                 cls: 'close',
25859                 html: 'x'
25860             });
25861         }
25862         
25863         return cfg;
25864     },
25865     
25866     onRender : function(ct, position)
25867     {
25868         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25869         
25870         if(!this.el){
25871             var cfg = Roo.apply({},  this.getAutoCreate());
25872             cfg.id = Roo.id();
25873             
25874             if (this.cls) {
25875                 cfg.cls += ' ' + this.cls;
25876             }
25877             if (this.style) {
25878                 cfg.style = this.style;
25879             }
25880             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25881             
25882             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25883         }
25884         
25885         this.el.select('>button.close').on('click', this.hide, this);
25886         
25887     },
25888     
25889     show : function()
25890     {
25891         if (!this.rendered) {
25892             this.render();
25893         }
25894         
25895         this.el.show();
25896         
25897         this.fireEvent('show', this);
25898         
25899     },
25900     
25901     hide : function()
25902     {
25903         if (!this.rendered) {
25904             this.render();
25905         }
25906         
25907         this.el.hide();
25908         
25909         this.fireEvent('hide', this);
25910     },
25911     
25912     update : function()
25913     {
25914 //        var e = this.el.dom.firstChild;
25915 //        
25916 //        if(this.closable){
25917 //            e = e.nextSibling;
25918 //        }
25919 //        
25920 //        e.data = this.html || '';
25921
25922         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25923     }
25924    
25925 });
25926
25927  
25928
25929      /*
25930  * - LGPL
25931  *
25932  * Graph
25933  * 
25934  */
25935
25936
25937 /**
25938  * @class Roo.bootstrap.Graph
25939  * @extends Roo.bootstrap.Component
25940  * Bootstrap Graph class
25941 > Prameters
25942  -sm {number} sm 4
25943  -md {number} md 5
25944  @cfg {String} graphtype  bar | vbar | pie
25945  @cfg {number} g_x coodinator | centre x (pie)
25946  @cfg {number} g_y coodinator | centre y (pie)
25947  @cfg {number} g_r radius (pie)
25948  @cfg {number} g_height height of the chart (respected by all elements in the set)
25949  @cfg {number} g_width width of the chart (respected by all elements in the set)
25950  @cfg {Object} title The title of the chart
25951     
25952  -{Array}  values
25953  -opts (object) options for the chart 
25954      o {
25955      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25956      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25957      o vgutter (number)
25958      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.
25959      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25960      o to
25961      o stretch (boolean)
25962      o }
25963  -opts (object) options for the pie
25964      o{
25965      o cut
25966      o startAngle (number)
25967      o endAngle (number)
25968      } 
25969  *
25970  * @constructor
25971  * Create a new Input
25972  * @param {Object} config The config object
25973  */
25974
25975 Roo.bootstrap.Graph = function(config){
25976     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25977     
25978     this.addEvents({
25979         // img events
25980         /**
25981          * @event click
25982          * The img click event for the img.
25983          * @param {Roo.EventObject} e
25984          */
25985         "click" : true
25986     });
25987 };
25988
25989 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25990     
25991     sm: 4,
25992     md: 5,
25993     graphtype: 'bar',
25994     g_height: 250,
25995     g_width: 400,
25996     g_x: 50,
25997     g_y: 50,
25998     g_r: 30,
25999     opts:{
26000         //g_colors: this.colors,
26001         g_type: 'soft',
26002         g_gutter: '20%'
26003
26004     },
26005     title : false,
26006
26007     getAutoCreate : function(){
26008         
26009         var cfg = {
26010             tag: 'div',
26011             html : null
26012         };
26013         
26014         
26015         return  cfg;
26016     },
26017
26018     onRender : function(ct,position){
26019         
26020         
26021         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
26022         
26023         if (typeof(Raphael) == 'undefined') {
26024             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
26025             return;
26026         }
26027         
26028         this.raphael = Raphael(this.el.dom);
26029         
26030                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26031                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26032                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26033                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
26034                 /*
26035                 r.text(160, 10, "Single Series Chart").attr(txtattr);
26036                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
26037                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
26038                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
26039                 
26040                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
26041                 r.barchart(330, 10, 300, 220, data1);
26042                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
26043                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
26044                 */
26045                 
26046                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
26047                 // r.barchart(30, 30, 560, 250,  xdata, {
26048                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
26049                 //     axis : "0 0 1 1",
26050                 //     axisxlabels :  xdata
26051                 //     //yvalues : cols,
26052                    
26053                 // });
26054 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
26055 //        
26056 //        this.load(null,xdata,{
26057 //                axis : "0 0 1 1",
26058 //                axisxlabels :  xdata
26059 //                });
26060
26061     },
26062
26063     load : function(graphtype,xdata,opts)
26064     {
26065         this.raphael.clear();
26066         if(!graphtype) {
26067             graphtype = this.graphtype;
26068         }
26069         if(!opts){
26070             opts = this.opts;
26071         }
26072         var r = this.raphael,
26073             fin = function () {
26074                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
26075             },
26076             fout = function () {
26077                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
26078             },
26079             pfin = function() {
26080                 this.sector.stop();
26081                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
26082
26083                 if (this.label) {
26084                     this.label[0].stop();
26085                     this.label[0].attr({ r: 7.5 });
26086                     this.label[1].attr({ "font-weight": 800 });
26087                 }
26088             },
26089             pfout = function() {
26090                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
26091
26092                 if (this.label) {
26093                     this.label[0].animate({ r: 5 }, 500, "bounce");
26094                     this.label[1].attr({ "font-weight": 400 });
26095                 }
26096             };
26097
26098         switch(graphtype){
26099             case 'bar':
26100                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26101                 break;
26102             case 'hbar':
26103                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26104                 break;
26105             case 'pie':
26106 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
26107 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
26108 //            
26109                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
26110                 
26111                 break;
26112
26113         }
26114         
26115         if(this.title){
26116             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
26117         }
26118         
26119     },
26120     
26121     setTitle: function(o)
26122     {
26123         this.title = o;
26124     },
26125     
26126     initEvents: function() {
26127         
26128         if(!this.href){
26129             this.el.on('click', this.onClick, this);
26130         }
26131     },
26132     
26133     onClick : function(e)
26134     {
26135         Roo.log('img onclick');
26136         this.fireEvent('click', this, e);
26137     }
26138    
26139 });
26140
26141  
26142 /*
26143  * - LGPL
26144  *
26145  * numberBox
26146  * 
26147  */
26148 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26149
26150 /**
26151  * @class Roo.bootstrap.dash.NumberBox
26152  * @extends Roo.bootstrap.Component
26153  * Bootstrap NumberBox class
26154  * @cfg {String} headline Box headline
26155  * @cfg {String} content Box content
26156  * @cfg {String} icon Box icon
26157  * @cfg {String} footer Footer text
26158  * @cfg {String} fhref Footer href
26159  * 
26160  * @constructor
26161  * Create a new NumberBox
26162  * @param {Object} config The config object
26163  */
26164
26165
26166 Roo.bootstrap.dash.NumberBox = function(config){
26167     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
26168     
26169 };
26170
26171 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
26172     
26173     headline : '',
26174     content : '',
26175     icon : '',
26176     footer : '',
26177     fhref : '',
26178     ficon : '',
26179     
26180     getAutoCreate : function(){
26181         
26182         var cfg = {
26183             tag : 'div',
26184             cls : 'small-box ',
26185             cn : [
26186                 {
26187                     tag : 'div',
26188                     cls : 'inner',
26189                     cn :[
26190                         {
26191                             tag : 'h3',
26192                             cls : 'roo-headline',
26193                             html : this.headline
26194                         },
26195                         {
26196                             tag : 'p',
26197                             cls : 'roo-content',
26198                             html : this.content
26199                         }
26200                     ]
26201                 }
26202             ]
26203         };
26204         
26205         if(this.icon){
26206             cfg.cn.push({
26207                 tag : 'div',
26208                 cls : 'icon',
26209                 cn :[
26210                     {
26211                         tag : 'i',
26212                         cls : 'ion ' + this.icon
26213                     }
26214                 ]
26215             });
26216         }
26217         
26218         if(this.footer){
26219             var footer = {
26220                 tag : 'a',
26221                 cls : 'small-box-footer',
26222                 href : this.fhref || '#',
26223                 html : this.footer
26224             };
26225             
26226             cfg.cn.push(footer);
26227             
26228         }
26229         
26230         return  cfg;
26231     },
26232
26233     onRender : function(ct,position){
26234         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
26235
26236
26237        
26238                 
26239     },
26240
26241     setHeadline: function (value)
26242     {
26243         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
26244     },
26245     
26246     setFooter: function (value, href)
26247     {
26248         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
26249         
26250         if(href){
26251             this.el.select('a.small-box-footer',true).first().attr('href', href);
26252         }
26253         
26254     },
26255
26256     setContent: function (value)
26257     {
26258         this.el.select('.roo-content',true).first().dom.innerHTML = value;
26259     },
26260
26261     initEvents: function() 
26262     {   
26263         
26264     }
26265     
26266 });
26267
26268  
26269 /*
26270  * - LGPL
26271  *
26272  * TabBox
26273  * 
26274  */
26275 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26276
26277 /**
26278  * @class Roo.bootstrap.dash.TabBox
26279  * @extends Roo.bootstrap.Component
26280  * Bootstrap TabBox class
26281  * @cfg {String} title Title of the TabBox
26282  * @cfg {String} icon Icon of the TabBox
26283  * @cfg {Boolean} showtabs (true|false) show the tabs default true
26284  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
26285  * 
26286  * @constructor
26287  * Create a new TabBox
26288  * @param {Object} config The config object
26289  */
26290
26291
26292 Roo.bootstrap.dash.TabBox = function(config){
26293     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
26294     this.addEvents({
26295         // raw events
26296         /**
26297          * @event addpane
26298          * When a pane is added
26299          * @param {Roo.bootstrap.dash.TabPane} pane
26300          */
26301         "addpane" : true,
26302         /**
26303          * @event activatepane
26304          * When a pane is activated
26305          * @param {Roo.bootstrap.dash.TabPane} pane
26306          */
26307         "activatepane" : true
26308         
26309          
26310     });
26311     
26312     this.panes = [];
26313 };
26314
26315 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
26316
26317     title : '',
26318     icon : false,
26319     showtabs : true,
26320     tabScrollable : false,
26321     
26322     getChildContainer : function()
26323     {
26324         return this.el.select('.tab-content', true).first();
26325     },
26326     
26327     getAutoCreate : function(){
26328         
26329         var header = {
26330             tag: 'li',
26331             cls: 'pull-left header',
26332             html: this.title,
26333             cn : []
26334         };
26335         
26336         if(this.icon){
26337             header.cn.push({
26338                 tag: 'i',
26339                 cls: 'fa ' + this.icon
26340             });
26341         }
26342         
26343         var h = {
26344             tag: 'ul',
26345             cls: 'nav nav-tabs pull-right',
26346             cn: [
26347                 header
26348             ]
26349         };
26350         
26351         if(this.tabScrollable){
26352             h = {
26353                 tag: 'div',
26354                 cls: 'tab-header',
26355                 cn: [
26356                     {
26357                         tag: 'ul',
26358                         cls: 'nav nav-tabs pull-right',
26359                         cn: [
26360                             header
26361                         ]
26362                     }
26363                 ]
26364             };
26365         }
26366         
26367         var cfg = {
26368             tag: 'div',
26369             cls: 'nav-tabs-custom',
26370             cn: [
26371                 h,
26372                 {
26373                     tag: 'div',
26374                     cls: 'tab-content no-padding',
26375                     cn: []
26376                 }
26377             ]
26378         };
26379
26380         return  cfg;
26381     },
26382     initEvents : function()
26383     {
26384         //Roo.log('add add pane handler');
26385         this.on('addpane', this.onAddPane, this);
26386     },
26387      /**
26388      * Updates the box title
26389      * @param {String} html to set the title to.
26390      */
26391     setTitle : function(value)
26392     {
26393         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
26394     },
26395     onAddPane : function(pane)
26396     {
26397         this.panes.push(pane);
26398         //Roo.log('addpane');
26399         //Roo.log(pane);
26400         // tabs are rendere left to right..
26401         if(!this.showtabs){
26402             return;
26403         }
26404         
26405         var ctr = this.el.select('.nav-tabs', true).first();
26406          
26407          
26408         var existing = ctr.select('.nav-tab',true);
26409         var qty = existing.getCount();;
26410         
26411         
26412         var tab = ctr.createChild({
26413             tag : 'li',
26414             cls : 'nav-tab' + (qty ? '' : ' active'),
26415             cn : [
26416                 {
26417                     tag : 'a',
26418                     href:'#',
26419                     html : pane.title
26420                 }
26421             ]
26422         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
26423         pane.tab = tab;
26424         
26425         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
26426         if (!qty) {
26427             pane.el.addClass('active');
26428         }
26429         
26430                 
26431     },
26432     onTabClick : function(ev,un,ob,pane)
26433     {
26434         //Roo.log('tab - prev default');
26435         ev.preventDefault();
26436         
26437         
26438         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26439         pane.tab.addClass('active');
26440         //Roo.log(pane.title);
26441         this.getChildContainer().select('.tab-pane',true).removeClass('active');
26442         // technically we should have a deactivate event.. but maybe add later.
26443         // and it should not de-activate the selected tab...
26444         this.fireEvent('activatepane', pane);
26445         pane.el.addClass('active');
26446         pane.fireEvent('activate');
26447         
26448         
26449     },
26450     
26451     getActivePane : function()
26452     {
26453         var r = false;
26454         Roo.each(this.panes, function(p) {
26455             if(p.el.hasClass('active')){
26456                 r = p;
26457                 return false;
26458             }
26459             
26460             return;
26461         });
26462         
26463         return r;
26464     }
26465     
26466     
26467 });
26468
26469  
26470 /*
26471  * - LGPL
26472  *
26473  * Tab pane
26474  * 
26475  */
26476 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26477 /**
26478  * @class Roo.bootstrap.TabPane
26479  * @extends Roo.bootstrap.Component
26480  * Bootstrap TabPane class
26481  * @cfg {Boolean} active (false | true) Default false
26482  * @cfg {String} title title of panel
26483
26484  * 
26485  * @constructor
26486  * Create a new TabPane
26487  * @param {Object} config The config object
26488  */
26489
26490 Roo.bootstrap.dash.TabPane = function(config){
26491     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
26492     
26493     this.addEvents({
26494         // raw events
26495         /**
26496          * @event activate
26497          * When a pane is activated
26498          * @param {Roo.bootstrap.dash.TabPane} pane
26499          */
26500         "activate" : true
26501          
26502     });
26503 };
26504
26505 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
26506     
26507     active : false,
26508     title : '',
26509     
26510     // the tabBox that this is attached to.
26511     tab : false,
26512      
26513     getAutoCreate : function() 
26514     {
26515         var cfg = {
26516             tag: 'div',
26517             cls: 'tab-pane'
26518         };
26519         
26520         if(this.active){
26521             cfg.cls += ' active';
26522         }
26523         
26524         return cfg;
26525     },
26526     initEvents  : function()
26527     {
26528         //Roo.log('trigger add pane handler');
26529         this.parent().fireEvent('addpane', this)
26530     },
26531     
26532      /**
26533      * Updates the tab title 
26534      * @param {String} html to set the title to.
26535      */
26536     setTitle: function(str)
26537     {
26538         if (!this.tab) {
26539             return;
26540         }
26541         this.title = str;
26542         this.tab.select('a', true).first().dom.innerHTML = str;
26543         
26544     }
26545     
26546     
26547     
26548 });
26549
26550  
26551
26552
26553  /*
26554  * - LGPL
26555  *
26556  * menu
26557  * 
26558  */
26559 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26560
26561 /**
26562  * @class Roo.bootstrap.menu.Menu
26563  * @extends Roo.bootstrap.Component
26564  * Bootstrap Menu class - container for Menu
26565  * @cfg {String} html Text of the menu
26566  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26567  * @cfg {String} icon Font awesome icon
26568  * @cfg {String} pos Menu align to (top | bottom) default bottom
26569  * 
26570  * 
26571  * @constructor
26572  * Create a new Menu
26573  * @param {Object} config The config object
26574  */
26575
26576
26577 Roo.bootstrap.menu.Menu = function(config){
26578     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26579     
26580     this.addEvents({
26581         /**
26582          * @event beforeshow
26583          * Fires before this menu is displayed
26584          * @param {Roo.bootstrap.menu.Menu} this
26585          */
26586         beforeshow : true,
26587         /**
26588          * @event beforehide
26589          * Fires before this menu is hidden
26590          * @param {Roo.bootstrap.menu.Menu} this
26591          */
26592         beforehide : true,
26593         /**
26594          * @event show
26595          * Fires after this menu is displayed
26596          * @param {Roo.bootstrap.menu.Menu} this
26597          */
26598         show : true,
26599         /**
26600          * @event hide
26601          * Fires after this menu is hidden
26602          * @param {Roo.bootstrap.menu.Menu} this
26603          */
26604         hide : true,
26605         /**
26606          * @event click
26607          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26608          * @param {Roo.bootstrap.menu.Menu} this
26609          * @param {Roo.EventObject} e
26610          */
26611         click : true
26612     });
26613     
26614 };
26615
26616 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26617     
26618     submenu : false,
26619     html : '',
26620     weight : 'default',
26621     icon : false,
26622     pos : 'bottom',
26623     
26624     
26625     getChildContainer : function() {
26626         if(this.isSubMenu){
26627             return this.el;
26628         }
26629         
26630         return this.el.select('ul.dropdown-menu', true).first();  
26631     },
26632     
26633     getAutoCreate : function()
26634     {
26635         var text = [
26636             {
26637                 tag : 'span',
26638                 cls : 'roo-menu-text',
26639                 html : this.html
26640             }
26641         ];
26642         
26643         if(this.icon){
26644             text.unshift({
26645                 tag : 'i',
26646                 cls : 'fa ' + this.icon
26647             })
26648         }
26649         
26650         
26651         var cfg = {
26652             tag : 'div',
26653             cls : 'btn-group',
26654             cn : [
26655                 {
26656                     tag : 'button',
26657                     cls : 'dropdown-button btn btn-' + this.weight,
26658                     cn : text
26659                 },
26660                 {
26661                     tag : 'button',
26662                     cls : 'dropdown-toggle btn btn-' + this.weight,
26663                     cn : [
26664                         {
26665                             tag : 'span',
26666                             cls : 'caret'
26667                         }
26668                     ]
26669                 },
26670                 {
26671                     tag : 'ul',
26672                     cls : 'dropdown-menu'
26673                 }
26674             ]
26675             
26676         };
26677         
26678         if(this.pos == 'top'){
26679             cfg.cls += ' dropup';
26680         }
26681         
26682         if(this.isSubMenu){
26683             cfg = {
26684                 tag : 'ul',
26685                 cls : 'dropdown-menu'
26686             }
26687         }
26688         
26689         return cfg;
26690     },
26691     
26692     onRender : function(ct, position)
26693     {
26694         this.isSubMenu = ct.hasClass('dropdown-submenu');
26695         
26696         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26697     },
26698     
26699     initEvents : function() 
26700     {
26701         if(this.isSubMenu){
26702             return;
26703         }
26704         
26705         this.hidden = true;
26706         
26707         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26708         this.triggerEl.on('click', this.onTriggerPress, this);
26709         
26710         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26711         this.buttonEl.on('click', this.onClick, this);
26712         
26713     },
26714     
26715     list : function()
26716     {
26717         if(this.isSubMenu){
26718             return this.el;
26719         }
26720         
26721         return this.el.select('ul.dropdown-menu', true).first();
26722     },
26723     
26724     onClick : function(e)
26725     {
26726         this.fireEvent("click", this, e);
26727     },
26728     
26729     onTriggerPress  : function(e)
26730     {   
26731         if (this.isVisible()) {
26732             this.hide();
26733         } else {
26734             this.show();
26735         }
26736     },
26737     
26738     isVisible : function(){
26739         return !this.hidden;
26740     },
26741     
26742     show : function()
26743     {
26744         this.fireEvent("beforeshow", this);
26745         
26746         this.hidden = false;
26747         this.el.addClass('open');
26748         
26749         Roo.get(document).on("mouseup", this.onMouseUp, this);
26750         
26751         this.fireEvent("show", this);
26752         
26753         
26754     },
26755     
26756     hide : function()
26757     {
26758         this.fireEvent("beforehide", this);
26759         
26760         this.hidden = true;
26761         this.el.removeClass('open');
26762         
26763         Roo.get(document).un("mouseup", this.onMouseUp);
26764         
26765         this.fireEvent("hide", this);
26766     },
26767     
26768     onMouseUp : function()
26769     {
26770         this.hide();
26771     }
26772     
26773 });
26774
26775  
26776  /*
26777  * - LGPL
26778  *
26779  * menu item
26780  * 
26781  */
26782 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26783
26784 /**
26785  * @class Roo.bootstrap.menu.Item
26786  * @extends Roo.bootstrap.Component
26787  * Bootstrap MenuItem class
26788  * @cfg {Boolean} submenu (true | false) default false
26789  * @cfg {String} html text of the item
26790  * @cfg {String} href the link
26791  * @cfg {Boolean} disable (true | false) default false
26792  * @cfg {Boolean} preventDefault (true | false) default true
26793  * @cfg {String} icon Font awesome icon
26794  * @cfg {String} pos Submenu align to (left | right) default right 
26795  * 
26796  * 
26797  * @constructor
26798  * Create a new Item
26799  * @param {Object} config The config object
26800  */
26801
26802
26803 Roo.bootstrap.menu.Item = function(config){
26804     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26805     this.addEvents({
26806         /**
26807          * @event mouseover
26808          * Fires when the mouse is hovering over this menu
26809          * @param {Roo.bootstrap.menu.Item} this
26810          * @param {Roo.EventObject} e
26811          */
26812         mouseover : true,
26813         /**
26814          * @event mouseout
26815          * Fires when the mouse exits this menu
26816          * @param {Roo.bootstrap.menu.Item} this
26817          * @param {Roo.EventObject} e
26818          */
26819         mouseout : true,
26820         // raw events
26821         /**
26822          * @event click
26823          * The raw click event for the entire grid.
26824          * @param {Roo.EventObject} e
26825          */
26826         click : true
26827     });
26828 };
26829
26830 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26831     
26832     submenu : false,
26833     href : '',
26834     html : '',
26835     preventDefault: true,
26836     disable : false,
26837     icon : false,
26838     pos : 'right',
26839     
26840     getAutoCreate : function()
26841     {
26842         var text = [
26843             {
26844                 tag : 'span',
26845                 cls : 'roo-menu-item-text',
26846                 html : this.html
26847             }
26848         ];
26849         
26850         if(this.icon){
26851             text.unshift({
26852                 tag : 'i',
26853                 cls : 'fa ' + this.icon
26854             })
26855         }
26856         
26857         var cfg = {
26858             tag : 'li',
26859             cn : [
26860                 {
26861                     tag : 'a',
26862                     href : this.href || '#',
26863                     cn : text
26864                 }
26865             ]
26866         };
26867         
26868         if(this.disable){
26869             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26870         }
26871         
26872         if(this.submenu){
26873             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26874             
26875             if(this.pos == 'left'){
26876                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26877             }
26878         }
26879         
26880         return cfg;
26881     },
26882     
26883     initEvents : function() 
26884     {
26885         this.el.on('mouseover', this.onMouseOver, this);
26886         this.el.on('mouseout', this.onMouseOut, this);
26887         
26888         this.el.select('a', true).first().on('click', this.onClick, this);
26889         
26890     },
26891     
26892     onClick : function(e)
26893     {
26894         if(this.preventDefault){
26895             e.preventDefault();
26896         }
26897         
26898         this.fireEvent("click", this, e);
26899     },
26900     
26901     onMouseOver : function(e)
26902     {
26903         if(this.submenu && this.pos == 'left'){
26904             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26905         }
26906         
26907         this.fireEvent("mouseover", this, e);
26908     },
26909     
26910     onMouseOut : function(e)
26911     {
26912         this.fireEvent("mouseout", this, e);
26913     }
26914 });
26915
26916  
26917
26918  /*
26919  * - LGPL
26920  *
26921  * menu separator
26922  * 
26923  */
26924 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26925
26926 /**
26927  * @class Roo.bootstrap.menu.Separator
26928  * @extends Roo.bootstrap.Component
26929  * Bootstrap Separator class
26930  * 
26931  * @constructor
26932  * Create a new Separator
26933  * @param {Object} config The config object
26934  */
26935
26936
26937 Roo.bootstrap.menu.Separator = function(config){
26938     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26939 };
26940
26941 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26942     
26943     getAutoCreate : function(){
26944         var cfg = {
26945             tag : 'li',
26946             cls: 'divider'
26947         };
26948         
26949         return cfg;
26950     }
26951    
26952 });
26953
26954  
26955
26956  /*
26957  * - LGPL
26958  *
26959  * Tooltip
26960  * 
26961  */
26962
26963 /**
26964  * @class Roo.bootstrap.Tooltip
26965  * Bootstrap Tooltip class
26966  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26967  * to determine which dom element triggers the tooltip.
26968  * 
26969  * It needs to add support for additional attributes like tooltip-position
26970  * 
26971  * @constructor
26972  * Create a new Toolti
26973  * @param {Object} config The config object
26974  */
26975
26976 Roo.bootstrap.Tooltip = function(config){
26977     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26978     
26979     this.alignment = Roo.bootstrap.Tooltip.alignment;
26980     
26981     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26982         this.alignment = config.alignment;
26983     }
26984     
26985 };
26986
26987 Roo.apply(Roo.bootstrap.Tooltip, {
26988     /**
26989      * @function init initialize tooltip monitoring.
26990      * @static
26991      */
26992     currentEl : false,
26993     currentTip : false,
26994     currentRegion : false,
26995     
26996     //  init : delay?
26997     
26998     init : function()
26999     {
27000         Roo.get(document).on('mouseover', this.enter ,this);
27001         Roo.get(document).on('mouseout', this.leave, this);
27002          
27003         
27004         this.currentTip = new Roo.bootstrap.Tooltip();
27005     },
27006     
27007     enter : function(ev)
27008     {
27009         var dom = ev.getTarget();
27010         
27011         //Roo.log(['enter',dom]);
27012         var el = Roo.fly(dom);
27013         if (this.currentEl) {
27014             //Roo.log(dom);
27015             //Roo.log(this.currentEl);
27016             //Roo.log(this.currentEl.contains(dom));
27017             if (this.currentEl == el) {
27018                 return;
27019             }
27020             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
27021                 return;
27022             }
27023
27024         }
27025         
27026         if (this.currentTip.el) {
27027             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
27028         }    
27029         //Roo.log(ev);
27030         
27031         if(!el || el.dom == document){
27032             return;
27033         }
27034         
27035         var bindEl = el;
27036         
27037         // you can not look for children, as if el is the body.. then everythign is the child..
27038         if (!el.attr('tooltip')) { //
27039             if (!el.select("[tooltip]").elements.length) {
27040                 return;
27041             }
27042             // is the mouse over this child...?
27043             bindEl = el.select("[tooltip]").first();
27044             var xy = ev.getXY();
27045             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
27046                 //Roo.log("not in region.");
27047                 return;
27048             }
27049             //Roo.log("child element over..");
27050             
27051         }
27052         this.currentEl = bindEl;
27053         this.currentTip.bind(bindEl);
27054         this.currentRegion = Roo.lib.Region.getRegion(dom);
27055         this.currentTip.enter();
27056         
27057     },
27058     leave : function(ev)
27059     {
27060         var dom = ev.getTarget();
27061         //Roo.log(['leave',dom]);
27062         if (!this.currentEl) {
27063             return;
27064         }
27065         
27066         
27067         if (dom != this.currentEl.dom) {
27068             return;
27069         }
27070         var xy = ev.getXY();
27071         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
27072             return;
27073         }
27074         // only activate leave if mouse cursor is outside... bounding box..
27075         
27076         
27077         
27078         
27079         if (this.currentTip) {
27080             this.currentTip.leave();
27081         }
27082         //Roo.log('clear currentEl');
27083         this.currentEl = false;
27084         
27085         
27086     },
27087     alignment : {
27088         'left' : ['r-l', [-2,0], 'right'],
27089         'right' : ['l-r', [2,0], 'left'],
27090         'bottom' : ['t-b', [0,2], 'top'],
27091         'top' : [ 'b-t', [0,-2], 'bottom']
27092     }
27093     
27094 });
27095
27096
27097 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
27098     
27099     
27100     bindEl : false,
27101     
27102     delay : null, // can be { show : 300 , hide: 500}
27103     
27104     timeout : null,
27105     
27106     hoverState : null, //???
27107     
27108     placement : 'bottom', 
27109     
27110     alignment : false,
27111     
27112     getAutoCreate : function(){
27113     
27114         var cfg = {
27115            cls : 'tooltip',
27116            role : 'tooltip',
27117            cn : [
27118                 {
27119                     cls : 'tooltip-arrow'
27120                 },
27121                 {
27122                     cls : 'tooltip-inner'
27123                 }
27124            ]
27125         };
27126         
27127         return cfg;
27128     },
27129     bind : function(el)
27130     {
27131         this.bindEl = el;
27132     },
27133       
27134     
27135     enter : function () {
27136        
27137         if (this.timeout != null) {
27138             clearTimeout(this.timeout);
27139         }
27140         
27141         this.hoverState = 'in';
27142          //Roo.log("enter - show");
27143         if (!this.delay || !this.delay.show) {
27144             this.show();
27145             return;
27146         }
27147         var _t = this;
27148         this.timeout = setTimeout(function () {
27149             if (_t.hoverState == 'in') {
27150                 _t.show();
27151             }
27152         }, this.delay.show);
27153     },
27154     leave : function()
27155     {
27156         clearTimeout(this.timeout);
27157     
27158         this.hoverState = 'out';
27159          if (!this.delay || !this.delay.hide) {
27160             this.hide();
27161             return;
27162         }
27163        
27164         var _t = this;
27165         this.timeout = setTimeout(function () {
27166             //Roo.log("leave - timeout");
27167             
27168             if (_t.hoverState == 'out') {
27169                 _t.hide();
27170                 Roo.bootstrap.Tooltip.currentEl = false;
27171             }
27172         }, delay);
27173     },
27174     
27175     show : function (msg)
27176     {
27177         if (!this.el) {
27178             this.render(document.body);
27179         }
27180         // set content.
27181         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
27182         
27183         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
27184         
27185         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
27186         
27187         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
27188         
27189         var placement = typeof this.placement == 'function' ?
27190             this.placement.call(this, this.el, on_el) :
27191             this.placement;
27192             
27193         var autoToken = /\s?auto?\s?/i;
27194         var autoPlace = autoToken.test(placement);
27195         if (autoPlace) {
27196             placement = placement.replace(autoToken, '') || 'top';
27197         }
27198         
27199         //this.el.detach()
27200         //this.el.setXY([0,0]);
27201         this.el.show();
27202         //this.el.dom.style.display='block';
27203         
27204         //this.el.appendTo(on_el);
27205         
27206         var p = this.getPosition();
27207         var box = this.el.getBox();
27208         
27209         if (autoPlace) {
27210             // fixme..
27211         }
27212         
27213         var align = this.alignment[placement];
27214         
27215         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
27216         
27217         if(placement == 'top' || placement == 'bottom'){
27218             if(xy[0] < 0){
27219                 placement = 'right';
27220             }
27221             
27222             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
27223                 placement = 'left';
27224             }
27225             
27226             var scroll = Roo.select('body', true).first().getScroll();
27227             
27228             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
27229                 placement = 'top';
27230             }
27231             
27232             align = this.alignment[placement];
27233         }
27234         
27235         this.el.alignTo(this.bindEl, align[0],align[1]);
27236         //var arrow = this.el.select('.arrow',true).first();
27237         //arrow.set(align[2], 
27238         
27239         this.el.addClass(placement);
27240         
27241         this.el.addClass('in fade');
27242         
27243         this.hoverState = null;
27244         
27245         if (this.el.hasClass('fade')) {
27246             // fade it?
27247         }
27248         
27249     },
27250     hide : function()
27251     {
27252          
27253         if (!this.el) {
27254             return;
27255         }
27256         //this.el.setXY([0,0]);
27257         this.el.removeClass('in');
27258         //this.el.hide();
27259         
27260     }
27261     
27262 });
27263  
27264
27265  /*
27266  * - LGPL
27267  *
27268  * Location Picker
27269  * 
27270  */
27271
27272 /**
27273  * @class Roo.bootstrap.LocationPicker
27274  * @extends Roo.bootstrap.Component
27275  * Bootstrap LocationPicker class
27276  * @cfg {Number} latitude Position when init default 0
27277  * @cfg {Number} longitude Position when init default 0
27278  * @cfg {Number} zoom default 15
27279  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
27280  * @cfg {Boolean} mapTypeControl default false
27281  * @cfg {Boolean} disableDoubleClickZoom default false
27282  * @cfg {Boolean} scrollwheel default true
27283  * @cfg {Boolean} streetViewControl default false
27284  * @cfg {Number} radius default 0
27285  * @cfg {String} locationName
27286  * @cfg {Boolean} draggable default true
27287  * @cfg {Boolean} enableAutocomplete default false
27288  * @cfg {Boolean} enableReverseGeocode default true
27289  * @cfg {String} markerTitle
27290  * 
27291  * @constructor
27292  * Create a new LocationPicker
27293  * @param {Object} config The config object
27294  */
27295
27296
27297 Roo.bootstrap.LocationPicker = function(config){
27298     
27299     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
27300     
27301     this.addEvents({
27302         /**
27303          * @event initial
27304          * Fires when the picker initialized.
27305          * @param {Roo.bootstrap.LocationPicker} this
27306          * @param {Google Location} location
27307          */
27308         initial : true,
27309         /**
27310          * @event positionchanged
27311          * Fires when the picker position changed.
27312          * @param {Roo.bootstrap.LocationPicker} this
27313          * @param {Google Location} location
27314          */
27315         positionchanged : true,
27316         /**
27317          * @event resize
27318          * Fires when the map resize.
27319          * @param {Roo.bootstrap.LocationPicker} this
27320          */
27321         resize : true,
27322         /**
27323          * @event show
27324          * Fires when the map show.
27325          * @param {Roo.bootstrap.LocationPicker} this
27326          */
27327         show : true,
27328         /**
27329          * @event hide
27330          * Fires when the map hide.
27331          * @param {Roo.bootstrap.LocationPicker} this
27332          */
27333         hide : true,
27334         /**
27335          * @event mapClick
27336          * Fires when click the map.
27337          * @param {Roo.bootstrap.LocationPicker} this
27338          * @param {Map event} e
27339          */
27340         mapClick : true,
27341         /**
27342          * @event mapRightClick
27343          * Fires when right click the map.
27344          * @param {Roo.bootstrap.LocationPicker} this
27345          * @param {Map event} e
27346          */
27347         mapRightClick : true,
27348         /**
27349          * @event markerClick
27350          * Fires when click the marker.
27351          * @param {Roo.bootstrap.LocationPicker} this
27352          * @param {Map event} e
27353          */
27354         markerClick : true,
27355         /**
27356          * @event markerRightClick
27357          * Fires when right click the marker.
27358          * @param {Roo.bootstrap.LocationPicker} this
27359          * @param {Map event} e
27360          */
27361         markerRightClick : true,
27362         /**
27363          * @event OverlayViewDraw
27364          * Fires when OverlayView Draw
27365          * @param {Roo.bootstrap.LocationPicker} this
27366          */
27367         OverlayViewDraw : true,
27368         /**
27369          * @event OverlayViewOnAdd
27370          * Fires when OverlayView Draw
27371          * @param {Roo.bootstrap.LocationPicker} this
27372          */
27373         OverlayViewOnAdd : true,
27374         /**
27375          * @event OverlayViewOnRemove
27376          * Fires when OverlayView Draw
27377          * @param {Roo.bootstrap.LocationPicker} this
27378          */
27379         OverlayViewOnRemove : true,
27380         /**
27381          * @event OverlayViewShow
27382          * Fires when OverlayView Draw
27383          * @param {Roo.bootstrap.LocationPicker} this
27384          * @param {Pixel} cpx
27385          */
27386         OverlayViewShow : true,
27387         /**
27388          * @event OverlayViewHide
27389          * Fires when OverlayView Draw
27390          * @param {Roo.bootstrap.LocationPicker} this
27391          */
27392         OverlayViewHide : true,
27393         /**
27394          * @event loadexception
27395          * Fires when load google lib failed.
27396          * @param {Roo.bootstrap.LocationPicker} this
27397          */
27398         loadexception : true
27399     });
27400         
27401 };
27402
27403 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
27404     
27405     gMapContext: false,
27406     
27407     latitude: 0,
27408     longitude: 0,
27409     zoom: 15,
27410     mapTypeId: false,
27411     mapTypeControl: false,
27412     disableDoubleClickZoom: false,
27413     scrollwheel: true,
27414     streetViewControl: false,
27415     radius: 0,
27416     locationName: '',
27417     draggable: true,
27418     enableAutocomplete: false,
27419     enableReverseGeocode: true,
27420     markerTitle: '',
27421     
27422     getAutoCreate: function()
27423     {
27424
27425         var cfg = {
27426             tag: 'div',
27427             cls: 'roo-location-picker'
27428         };
27429         
27430         return cfg
27431     },
27432     
27433     initEvents: function(ct, position)
27434     {       
27435         if(!this.el.getWidth() || this.isApplied()){
27436             return;
27437         }
27438         
27439         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27440         
27441         this.initial();
27442     },
27443     
27444     initial: function()
27445     {
27446         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27447             this.fireEvent('loadexception', this);
27448             return;
27449         }
27450         
27451         if(!this.mapTypeId){
27452             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
27453         }
27454         
27455         this.gMapContext = this.GMapContext();
27456         
27457         this.initOverlayView();
27458         
27459         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
27460         
27461         var _this = this;
27462                 
27463         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
27464             _this.setPosition(_this.gMapContext.marker.position);
27465         });
27466         
27467         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
27468             _this.fireEvent('mapClick', this, event);
27469             
27470         });
27471
27472         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
27473             _this.fireEvent('mapRightClick', this, event);
27474             
27475         });
27476         
27477         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27478             _this.fireEvent('markerClick', this, event);
27479             
27480         });
27481
27482         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27483             _this.fireEvent('markerRightClick', this, event);
27484             
27485         });
27486         
27487         this.setPosition(this.gMapContext.location);
27488         
27489         this.fireEvent('initial', this, this.gMapContext.location);
27490     },
27491     
27492     initOverlayView: function()
27493     {
27494         var _this = this;
27495         
27496         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27497             
27498             draw: function()
27499             {
27500                 _this.fireEvent('OverlayViewDraw', _this);
27501             },
27502             
27503             onAdd: function()
27504             {
27505                 _this.fireEvent('OverlayViewOnAdd', _this);
27506             },
27507             
27508             onRemove: function()
27509             {
27510                 _this.fireEvent('OverlayViewOnRemove', _this);
27511             },
27512             
27513             show: function(cpx)
27514             {
27515                 _this.fireEvent('OverlayViewShow', _this, cpx);
27516             },
27517             
27518             hide: function()
27519             {
27520                 _this.fireEvent('OverlayViewHide', _this);
27521             }
27522             
27523         });
27524     },
27525     
27526     fromLatLngToContainerPixel: function(event)
27527     {
27528         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27529     },
27530     
27531     isApplied: function() 
27532     {
27533         return this.getGmapContext() == false ? false : true;
27534     },
27535     
27536     getGmapContext: function() 
27537     {
27538         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27539     },
27540     
27541     GMapContext: function() 
27542     {
27543         var position = new google.maps.LatLng(this.latitude, this.longitude);
27544         
27545         var _map = new google.maps.Map(this.el.dom, {
27546             center: position,
27547             zoom: this.zoom,
27548             mapTypeId: this.mapTypeId,
27549             mapTypeControl: this.mapTypeControl,
27550             disableDoubleClickZoom: this.disableDoubleClickZoom,
27551             scrollwheel: this.scrollwheel,
27552             streetViewControl: this.streetViewControl,
27553             locationName: this.locationName,
27554             draggable: this.draggable,
27555             enableAutocomplete: this.enableAutocomplete,
27556             enableReverseGeocode: this.enableReverseGeocode
27557         });
27558         
27559         var _marker = new google.maps.Marker({
27560             position: position,
27561             map: _map,
27562             title: this.markerTitle,
27563             draggable: this.draggable
27564         });
27565         
27566         return {
27567             map: _map,
27568             marker: _marker,
27569             circle: null,
27570             location: position,
27571             radius: this.radius,
27572             locationName: this.locationName,
27573             addressComponents: {
27574                 formatted_address: null,
27575                 addressLine1: null,
27576                 addressLine2: null,
27577                 streetName: null,
27578                 streetNumber: null,
27579                 city: null,
27580                 district: null,
27581                 state: null,
27582                 stateOrProvince: null
27583             },
27584             settings: this,
27585             domContainer: this.el.dom,
27586             geodecoder: new google.maps.Geocoder()
27587         };
27588     },
27589     
27590     drawCircle: function(center, radius, options) 
27591     {
27592         if (this.gMapContext.circle != null) {
27593             this.gMapContext.circle.setMap(null);
27594         }
27595         if (radius > 0) {
27596             radius *= 1;
27597             options = Roo.apply({}, options, {
27598                 strokeColor: "#0000FF",
27599                 strokeOpacity: .35,
27600                 strokeWeight: 2,
27601                 fillColor: "#0000FF",
27602                 fillOpacity: .2
27603             });
27604             
27605             options.map = this.gMapContext.map;
27606             options.radius = radius;
27607             options.center = center;
27608             this.gMapContext.circle = new google.maps.Circle(options);
27609             return this.gMapContext.circle;
27610         }
27611         
27612         return null;
27613     },
27614     
27615     setPosition: function(location) 
27616     {
27617         this.gMapContext.location = location;
27618         this.gMapContext.marker.setPosition(location);
27619         this.gMapContext.map.panTo(location);
27620         this.drawCircle(location, this.gMapContext.radius, {});
27621         
27622         var _this = this;
27623         
27624         if (this.gMapContext.settings.enableReverseGeocode) {
27625             this.gMapContext.geodecoder.geocode({
27626                 latLng: this.gMapContext.location
27627             }, function(results, status) {
27628                 
27629                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27630                     _this.gMapContext.locationName = results[0].formatted_address;
27631                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27632                     
27633                     _this.fireEvent('positionchanged', this, location);
27634                 }
27635             });
27636             
27637             return;
27638         }
27639         
27640         this.fireEvent('positionchanged', this, location);
27641     },
27642     
27643     resize: function()
27644     {
27645         google.maps.event.trigger(this.gMapContext.map, "resize");
27646         
27647         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27648         
27649         this.fireEvent('resize', this);
27650     },
27651     
27652     setPositionByLatLng: function(latitude, longitude)
27653     {
27654         this.setPosition(new google.maps.LatLng(latitude, longitude));
27655     },
27656     
27657     getCurrentPosition: function() 
27658     {
27659         return {
27660             latitude: this.gMapContext.location.lat(),
27661             longitude: this.gMapContext.location.lng()
27662         };
27663     },
27664     
27665     getAddressName: function() 
27666     {
27667         return this.gMapContext.locationName;
27668     },
27669     
27670     getAddressComponents: function() 
27671     {
27672         return this.gMapContext.addressComponents;
27673     },
27674     
27675     address_component_from_google_geocode: function(address_components) 
27676     {
27677         var result = {};
27678         
27679         for (var i = 0; i < address_components.length; i++) {
27680             var component = address_components[i];
27681             if (component.types.indexOf("postal_code") >= 0) {
27682                 result.postalCode = component.short_name;
27683             } else if (component.types.indexOf("street_number") >= 0) {
27684                 result.streetNumber = component.short_name;
27685             } else if (component.types.indexOf("route") >= 0) {
27686                 result.streetName = component.short_name;
27687             } else if (component.types.indexOf("neighborhood") >= 0) {
27688                 result.city = component.short_name;
27689             } else if (component.types.indexOf("locality") >= 0) {
27690                 result.city = component.short_name;
27691             } else if (component.types.indexOf("sublocality") >= 0) {
27692                 result.district = component.short_name;
27693             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27694                 result.stateOrProvince = component.short_name;
27695             } else if (component.types.indexOf("country") >= 0) {
27696                 result.country = component.short_name;
27697             }
27698         }
27699         
27700         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27701         result.addressLine2 = "";
27702         return result;
27703     },
27704     
27705     setZoomLevel: function(zoom)
27706     {
27707         this.gMapContext.map.setZoom(zoom);
27708     },
27709     
27710     show: function()
27711     {
27712         if(!this.el){
27713             return;
27714         }
27715         
27716         this.el.show();
27717         
27718         this.resize();
27719         
27720         this.fireEvent('show', this);
27721     },
27722     
27723     hide: function()
27724     {
27725         if(!this.el){
27726             return;
27727         }
27728         
27729         this.el.hide();
27730         
27731         this.fireEvent('hide', this);
27732     }
27733     
27734 });
27735
27736 Roo.apply(Roo.bootstrap.LocationPicker, {
27737     
27738     OverlayView : function(map, options)
27739     {
27740         options = options || {};
27741         
27742         this.setMap(map);
27743     }
27744     
27745     
27746 });/**
27747  * @class Roo.bootstrap.Alert
27748  * @extends Roo.bootstrap.Component
27749  * Bootstrap Alert class - shows an alert area box
27750  * eg
27751  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27752   Enter a valid email address
27753 </div>
27754  * @licence LGPL
27755  * @cfg {String} title The title of alert
27756  * @cfg {String} html The content of alert
27757  * @cfg {String} weight (  success | info | warning | danger )
27758  * @cfg {String} faicon font-awesomeicon
27759  * 
27760  * @constructor
27761  * Create a new alert
27762  * @param {Object} config The config object
27763  */
27764
27765
27766 Roo.bootstrap.Alert = function(config){
27767     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27768     
27769 };
27770
27771 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27772     
27773     title: '',
27774     html: '',
27775     weight: false,
27776     faicon: false,
27777     
27778     getAutoCreate : function()
27779     {
27780         
27781         var cfg = {
27782             tag : 'div',
27783             cls : 'alert',
27784             cn : [
27785                 {
27786                     tag : 'i',
27787                     cls : 'roo-alert-icon'
27788                     
27789                 },
27790                 {
27791                     tag : 'b',
27792                     cls : 'roo-alert-title',
27793                     html : this.title
27794                 },
27795                 {
27796                     tag : 'span',
27797                     cls : 'roo-alert-text',
27798                     html : this.html
27799                 }
27800             ]
27801         };
27802         
27803         if(this.faicon){
27804             cfg.cn[0].cls += ' fa ' + this.faicon;
27805         }
27806         
27807         if(this.weight){
27808             cfg.cls += ' alert-' + this.weight;
27809         }
27810         
27811         return cfg;
27812     },
27813     
27814     initEvents: function() 
27815     {
27816         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27817     },
27818     
27819     setTitle : function(str)
27820     {
27821         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27822     },
27823     
27824     setText : function(str)
27825     {
27826         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27827     },
27828     
27829     setWeight : function(weight)
27830     {
27831         if(this.weight){
27832             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27833         }
27834         
27835         this.weight = weight;
27836         
27837         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27838     },
27839     
27840     setIcon : function(icon)
27841     {
27842         if(this.faicon){
27843             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27844         }
27845         
27846         this.faicon = icon;
27847         
27848         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27849     },
27850     
27851     hide: function() 
27852     {
27853         this.el.hide();   
27854     },
27855     
27856     show: function() 
27857     {  
27858         this.el.show();   
27859     }
27860     
27861 });
27862
27863  
27864 /*
27865 * Licence: LGPL
27866 */
27867
27868 /**
27869  * @class Roo.bootstrap.UploadCropbox
27870  * @extends Roo.bootstrap.Component
27871  * Bootstrap UploadCropbox class
27872  * @cfg {String} emptyText show when image has been loaded
27873  * @cfg {String} rotateNotify show when image too small to rotate
27874  * @cfg {Number} errorTimeout default 3000
27875  * @cfg {Number} minWidth default 300
27876  * @cfg {Number} minHeight default 300
27877  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27878  * @cfg {Boolean} isDocument (true|false) default false
27879  * @cfg {String} url action url
27880  * @cfg {String} paramName default 'imageUpload'
27881  * @cfg {String} method default POST
27882  * @cfg {Boolean} loadMask (true|false) default true
27883  * @cfg {Boolean} loadingText default 'Loading...'
27884  * 
27885  * @constructor
27886  * Create a new UploadCropbox
27887  * @param {Object} config The config object
27888  */
27889
27890 Roo.bootstrap.UploadCropbox = function(config){
27891     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27892     
27893     this.addEvents({
27894         /**
27895          * @event beforeselectfile
27896          * Fire before select file
27897          * @param {Roo.bootstrap.UploadCropbox} this
27898          */
27899         "beforeselectfile" : true,
27900         /**
27901          * @event initial
27902          * Fire after initEvent
27903          * @param {Roo.bootstrap.UploadCropbox} this
27904          */
27905         "initial" : true,
27906         /**
27907          * @event crop
27908          * Fire after initEvent
27909          * @param {Roo.bootstrap.UploadCropbox} this
27910          * @param {String} data
27911          */
27912         "crop" : true,
27913         /**
27914          * @event prepare
27915          * Fire when preparing the file data
27916          * @param {Roo.bootstrap.UploadCropbox} this
27917          * @param {Object} file
27918          */
27919         "prepare" : true,
27920         /**
27921          * @event exception
27922          * Fire when get exception
27923          * @param {Roo.bootstrap.UploadCropbox} this
27924          * @param {XMLHttpRequest} xhr
27925          */
27926         "exception" : true,
27927         /**
27928          * @event beforeloadcanvas
27929          * Fire before load the canvas
27930          * @param {Roo.bootstrap.UploadCropbox} this
27931          * @param {String} src
27932          */
27933         "beforeloadcanvas" : true,
27934         /**
27935          * @event trash
27936          * Fire when trash image
27937          * @param {Roo.bootstrap.UploadCropbox} this
27938          */
27939         "trash" : true,
27940         /**
27941          * @event download
27942          * Fire when download the image
27943          * @param {Roo.bootstrap.UploadCropbox} this
27944          */
27945         "download" : true,
27946         /**
27947          * @event footerbuttonclick
27948          * Fire when footerbuttonclick
27949          * @param {Roo.bootstrap.UploadCropbox} this
27950          * @param {String} type
27951          */
27952         "footerbuttonclick" : true,
27953         /**
27954          * @event resize
27955          * Fire when resize
27956          * @param {Roo.bootstrap.UploadCropbox} this
27957          */
27958         "resize" : true,
27959         /**
27960          * @event rotate
27961          * Fire when rotate the image
27962          * @param {Roo.bootstrap.UploadCropbox} this
27963          * @param {String} pos
27964          */
27965         "rotate" : true,
27966         /**
27967          * @event inspect
27968          * Fire when inspect the file
27969          * @param {Roo.bootstrap.UploadCropbox} this
27970          * @param {Object} file
27971          */
27972         "inspect" : true,
27973         /**
27974          * @event upload
27975          * Fire when xhr upload the file
27976          * @param {Roo.bootstrap.UploadCropbox} this
27977          * @param {Object} data
27978          */
27979         "upload" : true,
27980         /**
27981          * @event arrange
27982          * Fire when arrange the file data
27983          * @param {Roo.bootstrap.UploadCropbox} this
27984          * @param {Object} formData
27985          */
27986         "arrange" : true
27987     });
27988     
27989     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27990 };
27991
27992 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27993     
27994     emptyText : 'Click to upload image',
27995     rotateNotify : 'Image is too small to rotate',
27996     errorTimeout : 3000,
27997     scale : 0,
27998     baseScale : 1,
27999     rotate : 0,
28000     dragable : false,
28001     pinching : false,
28002     mouseX : 0,
28003     mouseY : 0,
28004     cropData : false,
28005     minWidth : 300,
28006     minHeight : 300,
28007     file : false,
28008     exif : {},
28009     baseRotate : 1,
28010     cropType : 'image/jpeg',
28011     buttons : false,
28012     canvasLoaded : false,
28013     isDocument : false,
28014     method : 'POST',
28015     paramName : 'imageUpload',
28016     loadMask : true,
28017     loadingText : 'Loading...',
28018     maskEl : false,
28019     
28020     getAutoCreate : function()
28021     {
28022         var cfg = {
28023             tag : 'div',
28024             cls : 'roo-upload-cropbox',
28025             cn : [
28026                 {
28027                     tag : 'input',
28028                     cls : 'roo-upload-cropbox-selector',
28029                     type : 'file'
28030                 },
28031                 {
28032                     tag : 'div',
28033                     cls : 'roo-upload-cropbox-body',
28034                     style : 'cursor:pointer',
28035                     cn : [
28036                         {
28037                             tag : 'div',
28038                             cls : 'roo-upload-cropbox-preview'
28039                         },
28040                         {
28041                             tag : 'div',
28042                             cls : 'roo-upload-cropbox-thumb'
28043                         },
28044                         {
28045                             tag : 'div',
28046                             cls : 'roo-upload-cropbox-empty-notify',
28047                             html : this.emptyText
28048                         },
28049                         {
28050                             tag : 'div',
28051                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
28052                             html : this.rotateNotify
28053                         }
28054                     ]
28055                 },
28056                 {
28057                     tag : 'div',
28058                     cls : 'roo-upload-cropbox-footer',
28059                     cn : {
28060                         tag : 'div',
28061                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
28062                         cn : []
28063                     }
28064                 }
28065             ]
28066         };
28067         
28068         return cfg;
28069     },
28070     
28071     onRender : function(ct, position)
28072     {
28073         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
28074         
28075         if (this.buttons.length) {
28076             
28077             Roo.each(this.buttons, function(bb) {
28078                 
28079                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
28080                 
28081                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
28082                 
28083             }, this);
28084         }
28085         
28086         if(this.loadMask){
28087             this.maskEl = this.el;
28088         }
28089     },
28090     
28091     initEvents : function()
28092     {
28093         this.urlAPI = (window.createObjectURL && window) || 
28094                                 (window.URL && URL.revokeObjectURL && URL) || 
28095                                 (window.webkitURL && webkitURL);
28096                         
28097         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
28098         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28099         
28100         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
28101         this.selectorEl.hide();
28102         
28103         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
28104         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28105         
28106         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
28107         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28108         this.thumbEl.hide();
28109         
28110         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
28111         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28112         
28113         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
28114         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28115         this.errorEl.hide();
28116         
28117         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
28118         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28119         this.footerEl.hide();
28120         
28121         this.setThumbBoxSize();
28122         
28123         this.bind();
28124         
28125         this.resize();
28126         
28127         this.fireEvent('initial', this);
28128     },
28129
28130     bind : function()
28131     {
28132         var _this = this;
28133         
28134         window.addEventListener("resize", function() { _this.resize(); } );
28135         
28136         this.bodyEl.on('click', this.beforeSelectFile, this);
28137         
28138         if(Roo.isTouch){
28139             this.bodyEl.on('touchstart', this.onTouchStart, this);
28140             this.bodyEl.on('touchmove', this.onTouchMove, this);
28141             this.bodyEl.on('touchend', this.onTouchEnd, this);
28142         }
28143         
28144         if(!Roo.isTouch){
28145             this.bodyEl.on('mousedown', this.onMouseDown, this);
28146             this.bodyEl.on('mousemove', this.onMouseMove, this);
28147             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
28148             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
28149             Roo.get(document).on('mouseup', this.onMouseUp, this);
28150         }
28151         
28152         this.selectorEl.on('change', this.onFileSelected, this);
28153     },
28154     
28155     reset : function()
28156     {    
28157         this.scale = 0;
28158         this.baseScale = 1;
28159         this.rotate = 0;
28160         this.baseRotate = 1;
28161         this.dragable = false;
28162         this.pinching = false;
28163         this.mouseX = 0;
28164         this.mouseY = 0;
28165         this.cropData = false;
28166         this.notifyEl.dom.innerHTML = this.emptyText;
28167         
28168         this.selectorEl.dom.value = '';
28169         
28170     },
28171     
28172     resize : function()
28173     {
28174         if(this.fireEvent('resize', this) != false){
28175             this.setThumbBoxPosition();
28176             this.setCanvasPosition();
28177         }
28178     },
28179     
28180     onFooterButtonClick : function(e, el, o, type)
28181     {
28182         switch (type) {
28183             case 'rotate-left' :
28184                 this.onRotateLeft(e);
28185                 break;
28186             case 'rotate-right' :
28187                 this.onRotateRight(e);
28188                 break;
28189             case 'picture' :
28190                 this.beforeSelectFile(e);
28191                 break;
28192             case 'trash' :
28193                 this.trash(e);
28194                 break;
28195             case 'crop' :
28196                 this.crop(e);
28197                 break;
28198             case 'download' :
28199                 this.download(e);
28200                 break;
28201             default :
28202                 break;
28203         }
28204         
28205         this.fireEvent('footerbuttonclick', this, type);
28206     },
28207     
28208     beforeSelectFile : function(e)
28209     {
28210         e.preventDefault();
28211         
28212         if(this.fireEvent('beforeselectfile', this) != false){
28213             this.selectorEl.dom.click();
28214         }
28215     },
28216     
28217     onFileSelected : function(e)
28218     {
28219         e.preventDefault();
28220         
28221         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28222             return;
28223         }
28224         
28225         var file = this.selectorEl.dom.files[0];
28226         
28227         if(this.fireEvent('inspect', this, file) != false){
28228             this.prepare(file);
28229         }
28230         
28231     },
28232     
28233     trash : function(e)
28234     {
28235         this.fireEvent('trash', this);
28236     },
28237     
28238     download : function(e)
28239     {
28240         this.fireEvent('download', this);
28241     },
28242     
28243     loadCanvas : function(src)
28244     {   
28245         if(this.fireEvent('beforeloadcanvas', this, src) != false){
28246             
28247             this.reset();
28248             
28249             this.imageEl = document.createElement('img');
28250             
28251             var _this = this;
28252             
28253             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
28254             
28255             this.imageEl.src = src;
28256         }
28257     },
28258     
28259     onLoadCanvas : function()
28260     {   
28261         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
28262         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
28263         
28264         this.bodyEl.un('click', this.beforeSelectFile, this);
28265         
28266         this.notifyEl.hide();
28267         this.thumbEl.show();
28268         this.footerEl.show();
28269         
28270         this.baseRotateLevel();
28271         
28272         if(this.isDocument){
28273             this.setThumbBoxSize();
28274         }
28275         
28276         this.setThumbBoxPosition();
28277         
28278         this.baseScaleLevel();
28279         
28280         this.draw();
28281         
28282         this.resize();
28283         
28284         this.canvasLoaded = true;
28285         
28286         if(this.loadMask){
28287             this.maskEl.unmask();
28288         }
28289         
28290     },
28291     
28292     setCanvasPosition : function()
28293     {   
28294         if(!this.canvasEl){
28295             return;
28296         }
28297         
28298         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
28299         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
28300         
28301         this.previewEl.setLeft(pw);
28302         this.previewEl.setTop(ph);
28303         
28304     },
28305     
28306     onMouseDown : function(e)
28307     {   
28308         e.stopEvent();
28309         
28310         this.dragable = true;
28311         this.pinching = false;
28312         
28313         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
28314             this.dragable = false;
28315             return;
28316         }
28317         
28318         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28319         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28320         
28321     },
28322     
28323     onMouseMove : function(e)
28324     {   
28325         e.stopEvent();
28326         
28327         if(!this.canvasLoaded){
28328             return;
28329         }
28330         
28331         if (!this.dragable){
28332             return;
28333         }
28334         
28335         var minX = Math.ceil(this.thumbEl.getLeft(true));
28336         var minY = Math.ceil(this.thumbEl.getTop(true));
28337         
28338         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
28339         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
28340         
28341         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28342         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28343         
28344         x = x - this.mouseX;
28345         y = y - this.mouseY;
28346         
28347         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
28348         var bgY = Math.ceil(y + this.previewEl.getTop(true));
28349         
28350         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
28351         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
28352         
28353         this.previewEl.setLeft(bgX);
28354         this.previewEl.setTop(bgY);
28355         
28356         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28357         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28358     },
28359     
28360     onMouseUp : function(e)
28361     {   
28362         e.stopEvent();
28363         
28364         this.dragable = false;
28365     },
28366     
28367     onMouseWheel : function(e)
28368     {   
28369         e.stopEvent();
28370         
28371         this.startScale = this.scale;
28372         
28373         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
28374         
28375         if(!this.zoomable()){
28376             this.scale = this.startScale;
28377             return;
28378         }
28379         
28380         this.draw();
28381         
28382         return;
28383     },
28384     
28385     zoomable : function()
28386     {
28387         var minScale = this.thumbEl.getWidth() / this.minWidth;
28388         
28389         if(this.minWidth < this.minHeight){
28390             minScale = this.thumbEl.getHeight() / this.minHeight;
28391         }
28392         
28393         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
28394         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
28395         
28396         if(
28397                 this.isDocument &&
28398                 (this.rotate == 0 || this.rotate == 180) && 
28399                 (
28400                     width > this.imageEl.OriginWidth || 
28401                     height > this.imageEl.OriginHeight ||
28402                     (width < this.minWidth && height < this.minHeight)
28403                 )
28404         ){
28405             return false;
28406         }
28407         
28408         if(
28409                 this.isDocument &&
28410                 (this.rotate == 90 || this.rotate == 270) && 
28411                 (
28412                     width > this.imageEl.OriginWidth || 
28413                     height > this.imageEl.OriginHeight ||
28414                     (width < this.minHeight && height < this.minWidth)
28415                 )
28416         ){
28417             return false;
28418         }
28419         
28420         if(
28421                 !this.isDocument &&
28422                 (this.rotate == 0 || this.rotate == 180) && 
28423                 (
28424                     width < this.minWidth || 
28425                     width > this.imageEl.OriginWidth || 
28426                     height < this.minHeight || 
28427                     height > this.imageEl.OriginHeight
28428                 )
28429         ){
28430             return false;
28431         }
28432         
28433         if(
28434                 !this.isDocument &&
28435                 (this.rotate == 90 || this.rotate == 270) && 
28436                 (
28437                     width < this.minHeight || 
28438                     width > this.imageEl.OriginWidth || 
28439                     height < this.minWidth || 
28440                     height > this.imageEl.OriginHeight
28441                 )
28442         ){
28443             return false;
28444         }
28445         
28446         return true;
28447         
28448     },
28449     
28450     onRotateLeft : function(e)
28451     {   
28452         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28453             
28454             var minScale = this.thumbEl.getWidth() / this.minWidth;
28455             
28456             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28457             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28458             
28459             this.startScale = this.scale;
28460             
28461             while (this.getScaleLevel() < minScale){
28462             
28463                 this.scale = this.scale + 1;
28464                 
28465                 if(!this.zoomable()){
28466                     break;
28467                 }
28468                 
28469                 if(
28470                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28471                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28472                 ){
28473                     continue;
28474                 }
28475                 
28476                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28477
28478                 this.draw();
28479                 
28480                 return;
28481             }
28482             
28483             this.scale = this.startScale;
28484             
28485             this.onRotateFail();
28486             
28487             return false;
28488         }
28489         
28490         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28491
28492         if(this.isDocument){
28493             this.setThumbBoxSize();
28494             this.setThumbBoxPosition();
28495             this.setCanvasPosition();
28496         }
28497         
28498         this.draw();
28499         
28500         this.fireEvent('rotate', this, 'left');
28501         
28502     },
28503     
28504     onRotateRight : function(e)
28505     {
28506         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28507             
28508             var minScale = this.thumbEl.getWidth() / this.minWidth;
28509         
28510             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28511             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28512             
28513             this.startScale = this.scale;
28514             
28515             while (this.getScaleLevel() < minScale){
28516             
28517                 this.scale = this.scale + 1;
28518                 
28519                 if(!this.zoomable()){
28520                     break;
28521                 }
28522                 
28523                 if(
28524                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28525                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28526                 ){
28527                     continue;
28528                 }
28529                 
28530                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28531
28532                 this.draw();
28533                 
28534                 return;
28535             }
28536             
28537             this.scale = this.startScale;
28538             
28539             this.onRotateFail();
28540             
28541             return false;
28542         }
28543         
28544         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28545
28546         if(this.isDocument){
28547             this.setThumbBoxSize();
28548             this.setThumbBoxPosition();
28549             this.setCanvasPosition();
28550         }
28551         
28552         this.draw();
28553         
28554         this.fireEvent('rotate', this, 'right');
28555     },
28556     
28557     onRotateFail : function()
28558     {
28559         this.errorEl.show(true);
28560         
28561         var _this = this;
28562         
28563         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28564     },
28565     
28566     draw : function()
28567     {
28568         this.previewEl.dom.innerHTML = '';
28569         
28570         var canvasEl = document.createElement("canvas");
28571         
28572         var contextEl = canvasEl.getContext("2d");
28573         
28574         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28575         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28576         var center = this.imageEl.OriginWidth / 2;
28577         
28578         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28579             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28580             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28581             center = this.imageEl.OriginHeight / 2;
28582         }
28583         
28584         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28585         
28586         contextEl.translate(center, center);
28587         contextEl.rotate(this.rotate * Math.PI / 180);
28588
28589         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28590         
28591         this.canvasEl = document.createElement("canvas");
28592         
28593         this.contextEl = this.canvasEl.getContext("2d");
28594         
28595         switch (this.rotate) {
28596             case 0 :
28597                 
28598                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28599                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28600                 
28601                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28602                 
28603                 break;
28604             case 90 : 
28605                 
28606                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28607                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28608                 
28609                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28610                     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);
28611                     break;
28612                 }
28613                 
28614                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28615                 
28616                 break;
28617             case 180 :
28618                 
28619                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28620                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28621                 
28622                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28623                     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);
28624                     break;
28625                 }
28626                 
28627                 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);
28628                 
28629                 break;
28630             case 270 :
28631                 
28632                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28633                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28634         
28635                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28636                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28637                     break;
28638                 }
28639                 
28640                 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);
28641                 
28642                 break;
28643             default : 
28644                 break;
28645         }
28646         
28647         this.previewEl.appendChild(this.canvasEl);
28648         
28649         this.setCanvasPosition();
28650     },
28651     
28652     crop : function()
28653     {
28654         if(!this.canvasLoaded){
28655             return;
28656         }
28657         
28658         var imageCanvas = document.createElement("canvas");
28659         
28660         var imageContext = imageCanvas.getContext("2d");
28661         
28662         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28663         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28664         
28665         var center = imageCanvas.width / 2;
28666         
28667         imageContext.translate(center, center);
28668         
28669         imageContext.rotate(this.rotate * Math.PI / 180);
28670         
28671         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28672         
28673         var canvas = document.createElement("canvas");
28674         
28675         var context = canvas.getContext("2d");
28676                 
28677         canvas.width = this.minWidth;
28678         canvas.height = this.minHeight;
28679
28680         switch (this.rotate) {
28681             case 0 :
28682                 
28683                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28684                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28685                 
28686                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28687                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28688                 
28689                 var targetWidth = this.minWidth - 2 * x;
28690                 var targetHeight = this.minHeight - 2 * y;
28691                 
28692                 var scale = 1;
28693                 
28694                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28695                     scale = targetWidth / width;
28696                 }
28697                 
28698                 if(x > 0 && y == 0){
28699                     scale = targetHeight / height;
28700                 }
28701                 
28702                 if(x > 0 && y > 0){
28703                     scale = targetWidth / width;
28704                     
28705                     if(width < height){
28706                         scale = targetHeight / height;
28707                     }
28708                 }
28709                 
28710                 context.scale(scale, scale);
28711                 
28712                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28713                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28714
28715                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28716                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28717
28718                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28719                 
28720                 break;
28721             case 90 : 
28722                 
28723                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28724                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28725                 
28726                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28727                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28728                 
28729                 var targetWidth = this.minWidth - 2 * x;
28730                 var targetHeight = this.minHeight - 2 * y;
28731                 
28732                 var scale = 1;
28733                 
28734                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28735                     scale = targetWidth / width;
28736                 }
28737                 
28738                 if(x > 0 && y == 0){
28739                     scale = targetHeight / height;
28740                 }
28741                 
28742                 if(x > 0 && y > 0){
28743                     scale = targetWidth / width;
28744                     
28745                     if(width < height){
28746                         scale = targetHeight / height;
28747                     }
28748                 }
28749                 
28750                 context.scale(scale, scale);
28751                 
28752                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28753                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28754
28755                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28756                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28757                 
28758                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28759                 
28760                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28761                 
28762                 break;
28763             case 180 :
28764                 
28765                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28766                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28767                 
28768                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28769                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28770                 
28771                 var targetWidth = this.minWidth - 2 * x;
28772                 var targetHeight = this.minHeight - 2 * y;
28773                 
28774                 var scale = 1;
28775                 
28776                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28777                     scale = targetWidth / width;
28778                 }
28779                 
28780                 if(x > 0 && y == 0){
28781                     scale = targetHeight / height;
28782                 }
28783                 
28784                 if(x > 0 && y > 0){
28785                     scale = targetWidth / width;
28786                     
28787                     if(width < height){
28788                         scale = targetHeight / height;
28789                     }
28790                 }
28791                 
28792                 context.scale(scale, scale);
28793                 
28794                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28795                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28796
28797                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28798                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28799
28800                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28801                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28802                 
28803                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28804                 
28805                 break;
28806             case 270 :
28807                 
28808                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28809                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28810                 
28811                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28812                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28813                 
28814                 var targetWidth = this.minWidth - 2 * x;
28815                 var targetHeight = this.minHeight - 2 * y;
28816                 
28817                 var scale = 1;
28818                 
28819                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28820                     scale = targetWidth / width;
28821                 }
28822                 
28823                 if(x > 0 && y == 0){
28824                     scale = targetHeight / height;
28825                 }
28826                 
28827                 if(x > 0 && y > 0){
28828                     scale = targetWidth / width;
28829                     
28830                     if(width < height){
28831                         scale = targetHeight / height;
28832                     }
28833                 }
28834                 
28835                 context.scale(scale, scale);
28836                 
28837                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28838                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28839
28840                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28841                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28842                 
28843                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28844                 
28845                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28846                 
28847                 break;
28848             default : 
28849                 break;
28850         }
28851         
28852         this.cropData = canvas.toDataURL(this.cropType);
28853         
28854         if(this.fireEvent('crop', this, this.cropData) !== false){
28855             this.process(this.file, this.cropData);
28856         }
28857         
28858         return;
28859         
28860     },
28861     
28862     setThumbBoxSize : function()
28863     {
28864         var width, height;
28865         
28866         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28867             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28868             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28869             
28870             this.minWidth = width;
28871             this.minHeight = height;
28872             
28873             if(this.rotate == 90 || this.rotate == 270){
28874                 this.minWidth = height;
28875                 this.minHeight = width;
28876             }
28877         }
28878         
28879         height = 300;
28880         width = Math.ceil(this.minWidth * height / this.minHeight);
28881         
28882         if(this.minWidth > this.minHeight){
28883             width = 300;
28884             height = Math.ceil(this.minHeight * width / this.minWidth);
28885         }
28886         
28887         this.thumbEl.setStyle({
28888             width : width + 'px',
28889             height : height + 'px'
28890         });
28891
28892         return;
28893             
28894     },
28895     
28896     setThumbBoxPosition : function()
28897     {
28898         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28899         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28900         
28901         this.thumbEl.setLeft(x);
28902         this.thumbEl.setTop(y);
28903         
28904     },
28905     
28906     baseRotateLevel : function()
28907     {
28908         this.baseRotate = 1;
28909         
28910         if(
28911                 typeof(this.exif) != 'undefined' &&
28912                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28913                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28914         ){
28915             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28916         }
28917         
28918         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28919         
28920     },
28921     
28922     baseScaleLevel : function()
28923     {
28924         var width, height;
28925         
28926         if(this.isDocument){
28927             
28928             if(this.baseRotate == 6 || this.baseRotate == 8){
28929             
28930                 height = this.thumbEl.getHeight();
28931                 this.baseScale = height / this.imageEl.OriginWidth;
28932
28933                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28934                     width = this.thumbEl.getWidth();
28935                     this.baseScale = width / this.imageEl.OriginHeight;
28936                 }
28937
28938                 return;
28939             }
28940
28941             height = this.thumbEl.getHeight();
28942             this.baseScale = height / this.imageEl.OriginHeight;
28943
28944             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28945                 width = this.thumbEl.getWidth();
28946                 this.baseScale = width / this.imageEl.OriginWidth;
28947             }
28948
28949             return;
28950         }
28951         
28952         if(this.baseRotate == 6 || this.baseRotate == 8){
28953             
28954             width = this.thumbEl.getHeight();
28955             this.baseScale = width / this.imageEl.OriginHeight;
28956             
28957             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28958                 height = this.thumbEl.getWidth();
28959                 this.baseScale = height / this.imageEl.OriginHeight;
28960             }
28961             
28962             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28963                 height = this.thumbEl.getWidth();
28964                 this.baseScale = height / this.imageEl.OriginHeight;
28965                 
28966                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28967                     width = this.thumbEl.getHeight();
28968                     this.baseScale = width / this.imageEl.OriginWidth;
28969                 }
28970             }
28971             
28972             return;
28973         }
28974         
28975         width = this.thumbEl.getWidth();
28976         this.baseScale = width / this.imageEl.OriginWidth;
28977         
28978         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28979             height = this.thumbEl.getHeight();
28980             this.baseScale = height / this.imageEl.OriginHeight;
28981         }
28982         
28983         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28984             
28985             height = this.thumbEl.getHeight();
28986             this.baseScale = height / this.imageEl.OriginHeight;
28987             
28988             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28989                 width = this.thumbEl.getWidth();
28990                 this.baseScale = width / this.imageEl.OriginWidth;
28991             }
28992             
28993         }
28994         
28995         return;
28996     },
28997     
28998     getScaleLevel : function()
28999     {
29000         return this.baseScale * Math.pow(1.1, this.scale);
29001     },
29002     
29003     onTouchStart : function(e)
29004     {
29005         if(!this.canvasLoaded){
29006             this.beforeSelectFile(e);
29007             return;
29008         }
29009         
29010         var touches = e.browserEvent.touches;
29011         
29012         if(!touches){
29013             return;
29014         }
29015         
29016         if(touches.length == 1){
29017             this.onMouseDown(e);
29018             return;
29019         }
29020         
29021         if(touches.length != 2){
29022             return;
29023         }
29024         
29025         var coords = [];
29026         
29027         for(var i = 0, finger; finger = touches[i]; i++){
29028             coords.push(finger.pageX, finger.pageY);
29029         }
29030         
29031         var x = Math.pow(coords[0] - coords[2], 2);
29032         var y = Math.pow(coords[1] - coords[3], 2);
29033         
29034         this.startDistance = Math.sqrt(x + y);
29035         
29036         this.startScale = this.scale;
29037         
29038         this.pinching = true;
29039         this.dragable = false;
29040         
29041     },
29042     
29043     onTouchMove : function(e)
29044     {
29045         if(!this.pinching && !this.dragable){
29046             return;
29047         }
29048         
29049         var touches = e.browserEvent.touches;
29050         
29051         if(!touches){
29052             return;
29053         }
29054         
29055         if(this.dragable){
29056             this.onMouseMove(e);
29057             return;
29058         }
29059         
29060         var coords = [];
29061         
29062         for(var i = 0, finger; finger = touches[i]; i++){
29063             coords.push(finger.pageX, finger.pageY);
29064         }
29065         
29066         var x = Math.pow(coords[0] - coords[2], 2);
29067         var y = Math.pow(coords[1] - coords[3], 2);
29068         
29069         this.endDistance = Math.sqrt(x + y);
29070         
29071         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
29072         
29073         if(!this.zoomable()){
29074             this.scale = this.startScale;
29075             return;
29076         }
29077         
29078         this.draw();
29079         
29080     },
29081     
29082     onTouchEnd : function(e)
29083     {
29084         this.pinching = false;
29085         this.dragable = false;
29086         
29087     },
29088     
29089     process : function(file, crop)
29090     {
29091         if(this.loadMask){
29092             this.maskEl.mask(this.loadingText);
29093         }
29094         
29095         this.xhr = new XMLHttpRequest();
29096         
29097         file.xhr = this.xhr;
29098
29099         this.xhr.open(this.method, this.url, true);
29100         
29101         var headers = {
29102             "Accept": "application/json",
29103             "Cache-Control": "no-cache",
29104             "X-Requested-With": "XMLHttpRequest"
29105         };
29106         
29107         for (var headerName in headers) {
29108             var headerValue = headers[headerName];
29109             if (headerValue) {
29110                 this.xhr.setRequestHeader(headerName, headerValue);
29111             }
29112         }
29113         
29114         var _this = this;
29115         
29116         this.xhr.onload = function()
29117         {
29118             _this.xhrOnLoad(_this.xhr);
29119         }
29120         
29121         this.xhr.onerror = function()
29122         {
29123             _this.xhrOnError(_this.xhr);
29124         }
29125         
29126         var formData = new FormData();
29127
29128         formData.append('returnHTML', 'NO');
29129         
29130         if(crop){
29131             formData.append('crop', crop);
29132         }
29133         
29134         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
29135             formData.append(this.paramName, file, file.name);
29136         }
29137         
29138         if(typeof(file.filename) != 'undefined'){
29139             formData.append('filename', file.filename);
29140         }
29141         
29142         if(typeof(file.mimetype) != 'undefined'){
29143             formData.append('mimetype', file.mimetype);
29144         }
29145         
29146         if(this.fireEvent('arrange', this, formData) != false){
29147             this.xhr.send(formData);
29148         };
29149     },
29150     
29151     xhrOnLoad : function(xhr)
29152     {
29153         if(this.loadMask){
29154             this.maskEl.unmask();
29155         }
29156         
29157         if (xhr.readyState !== 4) {
29158             this.fireEvent('exception', this, xhr);
29159             return;
29160         }
29161
29162         var response = Roo.decode(xhr.responseText);
29163         
29164         if(!response.success){
29165             this.fireEvent('exception', this, xhr);
29166             return;
29167         }
29168         
29169         var response = Roo.decode(xhr.responseText);
29170         
29171         this.fireEvent('upload', this, response);
29172         
29173     },
29174     
29175     xhrOnError : function()
29176     {
29177         if(this.loadMask){
29178             this.maskEl.unmask();
29179         }
29180         
29181         Roo.log('xhr on error');
29182         
29183         var response = Roo.decode(xhr.responseText);
29184           
29185         Roo.log(response);
29186         
29187     },
29188     
29189     prepare : function(file)
29190     {   
29191         if(this.loadMask){
29192             this.maskEl.mask(this.loadingText);
29193         }
29194         
29195         this.file = false;
29196         this.exif = {};
29197         
29198         if(typeof(file) === 'string'){
29199             this.loadCanvas(file);
29200             return;
29201         }
29202         
29203         if(!file || !this.urlAPI){
29204             return;
29205         }
29206         
29207         this.file = file;
29208         this.cropType = file.type;
29209         
29210         var _this = this;
29211         
29212         if(this.fireEvent('prepare', this, this.file) != false){
29213             
29214             var reader = new FileReader();
29215             
29216             reader.onload = function (e) {
29217                 if (e.target.error) {
29218                     Roo.log(e.target.error);
29219                     return;
29220                 }
29221                 
29222                 var buffer = e.target.result,
29223                     dataView = new DataView(buffer),
29224                     offset = 2,
29225                     maxOffset = dataView.byteLength - 4,
29226                     markerBytes,
29227                     markerLength;
29228                 
29229                 if (dataView.getUint16(0) === 0xffd8) {
29230                     while (offset < maxOffset) {
29231                         markerBytes = dataView.getUint16(offset);
29232                         
29233                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
29234                             markerLength = dataView.getUint16(offset + 2) + 2;
29235                             if (offset + markerLength > dataView.byteLength) {
29236                                 Roo.log('Invalid meta data: Invalid segment size.');
29237                                 break;
29238                             }
29239                             
29240                             if(markerBytes == 0xffe1){
29241                                 _this.parseExifData(
29242                                     dataView,
29243                                     offset,
29244                                     markerLength
29245                                 );
29246                             }
29247                             
29248                             offset += markerLength;
29249                             
29250                             continue;
29251                         }
29252                         
29253                         break;
29254                     }
29255                     
29256                 }
29257                 
29258                 var url = _this.urlAPI.createObjectURL(_this.file);
29259                 
29260                 _this.loadCanvas(url);
29261                 
29262                 return;
29263             }
29264             
29265             reader.readAsArrayBuffer(this.file);
29266             
29267         }
29268         
29269     },
29270     
29271     parseExifData : function(dataView, offset, length)
29272     {
29273         var tiffOffset = offset + 10,
29274             littleEndian,
29275             dirOffset;
29276     
29277         if (dataView.getUint32(offset + 4) !== 0x45786966) {
29278             // No Exif data, might be XMP data instead
29279             return;
29280         }
29281         
29282         // Check for the ASCII code for "Exif" (0x45786966):
29283         if (dataView.getUint32(offset + 4) !== 0x45786966) {
29284             // No Exif data, might be XMP data instead
29285             return;
29286         }
29287         if (tiffOffset + 8 > dataView.byteLength) {
29288             Roo.log('Invalid Exif data: Invalid segment size.');
29289             return;
29290         }
29291         // Check for the two null bytes:
29292         if (dataView.getUint16(offset + 8) !== 0x0000) {
29293             Roo.log('Invalid Exif data: Missing byte alignment offset.');
29294             return;
29295         }
29296         // Check the byte alignment:
29297         switch (dataView.getUint16(tiffOffset)) {
29298         case 0x4949:
29299             littleEndian = true;
29300             break;
29301         case 0x4D4D:
29302             littleEndian = false;
29303             break;
29304         default:
29305             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
29306             return;
29307         }
29308         // Check for the TIFF tag marker (0x002A):
29309         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
29310             Roo.log('Invalid Exif data: Missing TIFF marker.');
29311             return;
29312         }
29313         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
29314         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
29315         
29316         this.parseExifTags(
29317             dataView,
29318             tiffOffset,
29319             tiffOffset + dirOffset,
29320             littleEndian
29321         );
29322     },
29323     
29324     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
29325     {
29326         var tagsNumber,
29327             dirEndOffset,
29328             i;
29329         if (dirOffset + 6 > dataView.byteLength) {
29330             Roo.log('Invalid Exif data: Invalid directory offset.');
29331             return;
29332         }
29333         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
29334         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
29335         if (dirEndOffset + 4 > dataView.byteLength) {
29336             Roo.log('Invalid Exif data: Invalid directory size.');
29337             return;
29338         }
29339         for (i = 0; i < tagsNumber; i += 1) {
29340             this.parseExifTag(
29341                 dataView,
29342                 tiffOffset,
29343                 dirOffset + 2 + 12 * i, // tag offset
29344                 littleEndian
29345             );
29346         }
29347         // Return the offset to the next directory:
29348         return dataView.getUint32(dirEndOffset, littleEndian);
29349     },
29350     
29351     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
29352     {
29353         var tag = dataView.getUint16(offset, littleEndian);
29354         
29355         this.exif[tag] = this.getExifValue(
29356             dataView,
29357             tiffOffset,
29358             offset,
29359             dataView.getUint16(offset + 2, littleEndian), // tag type
29360             dataView.getUint32(offset + 4, littleEndian), // tag length
29361             littleEndian
29362         );
29363     },
29364     
29365     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
29366     {
29367         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
29368             tagSize,
29369             dataOffset,
29370             values,
29371             i,
29372             str,
29373             c;
29374     
29375         if (!tagType) {
29376             Roo.log('Invalid Exif data: Invalid tag type.');
29377             return;
29378         }
29379         
29380         tagSize = tagType.size * length;
29381         // Determine if the value is contained in the dataOffset bytes,
29382         // or if the value at the dataOffset is a pointer to the actual data:
29383         dataOffset = tagSize > 4 ?
29384                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
29385         if (dataOffset + tagSize > dataView.byteLength) {
29386             Roo.log('Invalid Exif data: Invalid data offset.');
29387             return;
29388         }
29389         if (length === 1) {
29390             return tagType.getValue(dataView, dataOffset, littleEndian);
29391         }
29392         values = [];
29393         for (i = 0; i < length; i += 1) {
29394             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
29395         }
29396         
29397         if (tagType.ascii) {
29398             str = '';
29399             // Concatenate the chars:
29400             for (i = 0; i < values.length; i += 1) {
29401                 c = values[i];
29402                 // Ignore the terminating NULL byte(s):
29403                 if (c === '\u0000') {
29404                     break;
29405                 }
29406                 str += c;
29407             }
29408             return str;
29409         }
29410         return values;
29411     }
29412     
29413 });
29414
29415 Roo.apply(Roo.bootstrap.UploadCropbox, {
29416     tags : {
29417         'Orientation': 0x0112
29418     },
29419     
29420     Orientation: {
29421             1: 0, //'top-left',
29422 //            2: 'top-right',
29423             3: 180, //'bottom-right',
29424 //            4: 'bottom-left',
29425 //            5: 'left-top',
29426             6: 90, //'right-top',
29427 //            7: 'right-bottom',
29428             8: 270 //'left-bottom'
29429     },
29430     
29431     exifTagTypes : {
29432         // byte, 8-bit unsigned int:
29433         1: {
29434             getValue: function (dataView, dataOffset) {
29435                 return dataView.getUint8(dataOffset);
29436             },
29437             size: 1
29438         },
29439         // ascii, 8-bit byte:
29440         2: {
29441             getValue: function (dataView, dataOffset) {
29442                 return String.fromCharCode(dataView.getUint8(dataOffset));
29443             },
29444             size: 1,
29445             ascii: true
29446         },
29447         // short, 16 bit int:
29448         3: {
29449             getValue: function (dataView, dataOffset, littleEndian) {
29450                 return dataView.getUint16(dataOffset, littleEndian);
29451             },
29452             size: 2
29453         },
29454         // long, 32 bit int:
29455         4: {
29456             getValue: function (dataView, dataOffset, littleEndian) {
29457                 return dataView.getUint32(dataOffset, littleEndian);
29458             },
29459             size: 4
29460         },
29461         // rational = two long values, first is numerator, second is denominator:
29462         5: {
29463             getValue: function (dataView, dataOffset, littleEndian) {
29464                 return dataView.getUint32(dataOffset, littleEndian) /
29465                     dataView.getUint32(dataOffset + 4, littleEndian);
29466             },
29467             size: 8
29468         },
29469         // slong, 32 bit signed int:
29470         9: {
29471             getValue: function (dataView, dataOffset, littleEndian) {
29472                 return dataView.getInt32(dataOffset, littleEndian);
29473             },
29474             size: 4
29475         },
29476         // srational, two slongs, first is numerator, second is denominator:
29477         10: {
29478             getValue: function (dataView, dataOffset, littleEndian) {
29479                 return dataView.getInt32(dataOffset, littleEndian) /
29480                     dataView.getInt32(dataOffset + 4, littleEndian);
29481             },
29482             size: 8
29483         }
29484     },
29485     
29486     footer : {
29487         STANDARD : [
29488             {
29489                 tag : 'div',
29490                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29491                 action : 'rotate-left',
29492                 cn : [
29493                     {
29494                         tag : 'button',
29495                         cls : 'btn btn-default',
29496                         html : '<i class="fa fa-undo"></i>'
29497                     }
29498                 ]
29499             },
29500             {
29501                 tag : 'div',
29502                 cls : 'btn-group roo-upload-cropbox-picture',
29503                 action : 'picture',
29504                 cn : [
29505                     {
29506                         tag : 'button',
29507                         cls : 'btn btn-default',
29508                         html : '<i class="fa fa-picture-o"></i>'
29509                     }
29510                 ]
29511             },
29512             {
29513                 tag : 'div',
29514                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29515                 action : 'rotate-right',
29516                 cn : [
29517                     {
29518                         tag : 'button',
29519                         cls : 'btn btn-default',
29520                         html : '<i class="fa fa-repeat"></i>'
29521                     }
29522                 ]
29523             }
29524         ],
29525         DOCUMENT : [
29526             {
29527                 tag : 'div',
29528                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29529                 action : 'rotate-left',
29530                 cn : [
29531                     {
29532                         tag : 'button',
29533                         cls : 'btn btn-default',
29534                         html : '<i class="fa fa-undo"></i>'
29535                     }
29536                 ]
29537             },
29538             {
29539                 tag : 'div',
29540                 cls : 'btn-group roo-upload-cropbox-download',
29541                 action : 'download',
29542                 cn : [
29543                     {
29544                         tag : 'button',
29545                         cls : 'btn btn-default',
29546                         html : '<i class="fa fa-download"></i>'
29547                     }
29548                 ]
29549             },
29550             {
29551                 tag : 'div',
29552                 cls : 'btn-group roo-upload-cropbox-crop',
29553                 action : 'crop',
29554                 cn : [
29555                     {
29556                         tag : 'button',
29557                         cls : 'btn btn-default',
29558                         html : '<i class="fa fa-crop"></i>'
29559                     }
29560                 ]
29561             },
29562             {
29563                 tag : 'div',
29564                 cls : 'btn-group roo-upload-cropbox-trash',
29565                 action : 'trash',
29566                 cn : [
29567                     {
29568                         tag : 'button',
29569                         cls : 'btn btn-default',
29570                         html : '<i class="fa fa-trash"></i>'
29571                     }
29572                 ]
29573             },
29574             {
29575                 tag : 'div',
29576                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29577                 action : 'rotate-right',
29578                 cn : [
29579                     {
29580                         tag : 'button',
29581                         cls : 'btn btn-default',
29582                         html : '<i class="fa fa-repeat"></i>'
29583                     }
29584                 ]
29585             }
29586         ],
29587         ROTATOR : [
29588             {
29589                 tag : 'div',
29590                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29591                 action : 'rotate-left',
29592                 cn : [
29593                     {
29594                         tag : 'button',
29595                         cls : 'btn btn-default',
29596                         html : '<i class="fa fa-undo"></i>'
29597                     }
29598                 ]
29599             },
29600             {
29601                 tag : 'div',
29602                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29603                 action : 'rotate-right',
29604                 cn : [
29605                     {
29606                         tag : 'button',
29607                         cls : 'btn btn-default',
29608                         html : '<i class="fa fa-repeat"></i>'
29609                     }
29610                 ]
29611             }
29612         ]
29613     }
29614 });
29615
29616 /*
29617 * Licence: LGPL
29618 */
29619
29620 /**
29621  * @class Roo.bootstrap.DocumentManager
29622  * @extends Roo.bootstrap.Component
29623  * Bootstrap DocumentManager class
29624  * @cfg {String} paramName default 'imageUpload'
29625  * @cfg {String} toolTipName default 'filename'
29626  * @cfg {String} method default POST
29627  * @cfg {String} url action url
29628  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29629  * @cfg {Boolean} multiple multiple upload default true
29630  * @cfg {Number} thumbSize default 300
29631  * @cfg {String} fieldLabel
29632  * @cfg {Number} labelWidth default 4
29633  * @cfg {String} labelAlign (left|top) default left
29634  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29635 * @cfg {Number} labellg set the width of label (1-12)
29636  * @cfg {Number} labelmd set the width of label (1-12)
29637  * @cfg {Number} labelsm set the width of label (1-12)
29638  * @cfg {Number} labelxs set the width of label (1-12)
29639  * 
29640  * @constructor
29641  * Create a new DocumentManager
29642  * @param {Object} config The config object
29643  */
29644
29645 Roo.bootstrap.DocumentManager = function(config){
29646     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29647     
29648     this.files = [];
29649     this.delegates = [];
29650     
29651     this.addEvents({
29652         /**
29653          * @event initial
29654          * Fire when initial the DocumentManager
29655          * @param {Roo.bootstrap.DocumentManager} this
29656          */
29657         "initial" : true,
29658         /**
29659          * @event inspect
29660          * inspect selected file
29661          * @param {Roo.bootstrap.DocumentManager} this
29662          * @param {File} file
29663          */
29664         "inspect" : true,
29665         /**
29666          * @event exception
29667          * Fire when xhr load exception
29668          * @param {Roo.bootstrap.DocumentManager} this
29669          * @param {XMLHttpRequest} xhr
29670          */
29671         "exception" : true,
29672         /**
29673          * @event afterupload
29674          * Fire when xhr load exception
29675          * @param {Roo.bootstrap.DocumentManager} this
29676          * @param {XMLHttpRequest} xhr
29677          */
29678         "afterupload" : true,
29679         /**
29680          * @event prepare
29681          * prepare the form data
29682          * @param {Roo.bootstrap.DocumentManager} this
29683          * @param {Object} formData
29684          */
29685         "prepare" : true,
29686         /**
29687          * @event remove
29688          * Fire when remove the file
29689          * @param {Roo.bootstrap.DocumentManager} this
29690          * @param {Object} file
29691          */
29692         "remove" : true,
29693         /**
29694          * @event refresh
29695          * Fire after refresh the file
29696          * @param {Roo.bootstrap.DocumentManager} this
29697          */
29698         "refresh" : true,
29699         /**
29700          * @event click
29701          * Fire after click the image
29702          * @param {Roo.bootstrap.DocumentManager} this
29703          * @param {Object} file
29704          */
29705         "click" : true,
29706         /**
29707          * @event edit
29708          * Fire when upload a image and editable set to true
29709          * @param {Roo.bootstrap.DocumentManager} this
29710          * @param {Object} file
29711          */
29712         "edit" : true,
29713         /**
29714          * @event beforeselectfile
29715          * Fire before select file
29716          * @param {Roo.bootstrap.DocumentManager} this
29717          */
29718         "beforeselectfile" : true,
29719         /**
29720          * @event process
29721          * Fire before process file
29722          * @param {Roo.bootstrap.DocumentManager} this
29723          * @param {Object} file
29724          */
29725         "process" : true,
29726         /**
29727          * @event previewrendered
29728          * Fire when preview rendered
29729          * @param {Roo.bootstrap.DocumentManager} this
29730          * @param {Object} file
29731          */
29732         "previewrendered" : true,
29733         /**
29734          */
29735         "previewResize" : true
29736         
29737     });
29738 };
29739
29740 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29741     
29742     boxes : 0,
29743     inputName : '',
29744     thumbSize : 300,
29745     multiple : true,
29746     files : false,
29747     method : 'POST',
29748     url : '',
29749     paramName : 'imageUpload',
29750     toolTipName : 'filename',
29751     fieldLabel : '',
29752     labelWidth : 4,
29753     labelAlign : 'left',
29754     editable : true,
29755     delegates : false,
29756     xhr : false, 
29757     
29758     labellg : 0,
29759     labelmd : 0,
29760     labelsm : 0,
29761     labelxs : 0,
29762     
29763     getAutoCreate : function()
29764     {   
29765         var managerWidget = {
29766             tag : 'div',
29767             cls : 'roo-document-manager',
29768             cn : [
29769                 {
29770                     tag : 'input',
29771                     cls : 'roo-document-manager-selector',
29772                     type : 'file'
29773                 },
29774                 {
29775                     tag : 'div',
29776                     cls : 'roo-document-manager-uploader',
29777                     cn : [
29778                         {
29779                             tag : 'div',
29780                             cls : 'roo-document-manager-upload-btn',
29781                             html : '<i class="fa fa-plus"></i>'
29782                         }
29783                     ]
29784                     
29785                 }
29786             ]
29787         };
29788         
29789         var content = [
29790             {
29791                 tag : 'div',
29792                 cls : 'column col-md-12',
29793                 cn : managerWidget
29794             }
29795         ];
29796         
29797         if(this.fieldLabel.length){
29798             
29799             content = [
29800                 {
29801                     tag : 'div',
29802                     cls : 'column col-md-12',
29803                     html : this.fieldLabel
29804                 },
29805                 {
29806                     tag : 'div',
29807                     cls : 'column col-md-12',
29808                     cn : managerWidget
29809                 }
29810             ];
29811
29812             if(this.labelAlign == 'left'){
29813                 content = [
29814                     {
29815                         tag : 'div',
29816                         cls : 'column',
29817                         html : this.fieldLabel
29818                     },
29819                     {
29820                         tag : 'div',
29821                         cls : 'column',
29822                         cn : managerWidget
29823                     }
29824                 ];
29825                 
29826                 if(this.labelWidth > 12){
29827                     content[0].style = "width: " + this.labelWidth + 'px';
29828                 }
29829
29830                 if(this.labelWidth < 13 && this.labelmd == 0){
29831                     this.labelmd = this.labelWidth;
29832                 }
29833
29834                 if(this.labellg > 0){
29835                     content[0].cls += ' col-lg-' + this.labellg;
29836                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29837                 }
29838
29839                 if(this.labelmd > 0){
29840                     content[0].cls += ' col-md-' + this.labelmd;
29841                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29842                 }
29843
29844                 if(this.labelsm > 0){
29845                     content[0].cls += ' col-sm-' + this.labelsm;
29846                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29847                 }
29848
29849                 if(this.labelxs > 0){
29850                     content[0].cls += ' col-xs-' + this.labelxs;
29851                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29852                 }
29853                 
29854             }
29855         }
29856         
29857         var cfg = {
29858             tag : 'div',
29859             cls : 'row clearfix',
29860             cn : content
29861         };
29862         
29863         return cfg;
29864         
29865     },
29866     
29867     initEvents : function()
29868     {
29869         this.managerEl = this.el.select('.roo-document-manager', true).first();
29870         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29871         
29872         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29873         this.selectorEl.hide();
29874         
29875         if(this.multiple){
29876             this.selectorEl.attr('multiple', 'multiple');
29877         }
29878         
29879         this.selectorEl.on('change', this.onFileSelected, this);
29880         
29881         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29882         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29883         
29884         this.uploader.on('click', this.onUploaderClick, this);
29885         
29886         this.renderProgressDialog();
29887         
29888         var _this = this;
29889         
29890         window.addEventListener("resize", function() { _this.refresh(); } );
29891         
29892         this.fireEvent('initial', this);
29893     },
29894     
29895     renderProgressDialog : function()
29896     {
29897         var _this = this;
29898         
29899         this.progressDialog = new Roo.bootstrap.Modal({
29900             cls : 'roo-document-manager-progress-dialog',
29901             allow_close : false,
29902             animate : false,
29903             title : '',
29904             buttons : [
29905                 {
29906                     name  :'cancel',
29907                     weight : 'danger',
29908                     html : 'Cancel'
29909                 }
29910             ], 
29911             listeners : { 
29912                 btnclick : function() {
29913                     _this.uploadCancel();
29914                     this.hide();
29915                 }
29916             }
29917         });
29918          
29919         this.progressDialog.render(Roo.get(document.body));
29920          
29921         this.progress = new Roo.bootstrap.Progress({
29922             cls : 'roo-document-manager-progress',
29923             active : true,
29924             striped : true
29925         });
29926         
29927         this.progress.render(this.progressDialog.getChildContainer());
29928         
29929         this.progressBar = new Roo.bootstrap.ProgressBar({
29930             cls : 'roo-document-manager-progress-bar',
29931             aria_valuenow : 0,
29932             aria_valuemin : 0,
29933             aria_valuemax : 12,
29934             panel : 'success'
29935         });
29936         
29937         this.progressBar.render(this.progress.getChildContainer());
29938     },
29939     
29940     onUploaderClick : function(e)
29941     {
29942         e.preventDefault();
29943      
29944         if(this.fireEvent('beforeselectfile', this) != false){
29945             this.selectorEl.dom.click();
29946         }
29947         
29948     },
29949     
29950     onFileSelected : function(e)
29951     {
29952         e.preventDefault();
29953         
29954         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29955             return;
29956         }
29957         
29958         Roo.each(this.selectorEl.dom.files, function(file){
29959             if(this.fireEvent('inspect', this, file) != false){
29960                 this.files.push(file);
29961             }
29962         }, this);
29963         
29964         this.queue();
29965         
29966     },
29967     
29968     queue : function()
29969     {
29970         this.selectorEl.dom.value = '';
29971         
29972         if(!this.files || !this.files.length){
29973             return;
29974         }
29975         
29976         if(this.boxes > 0 && this.files.length > this.boxes){
29977             this.files = this.files.slice(0, this.boxes);
29978         }
29979         
29980         this.uploader.show();
29981         
29982         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29983             this.uploader.hide();
29984         }
29985         
29986         var _this = this;
29987         
29988         var files = [];
29989         
29990         var docs = [];
29991         
29992         Roo.each(this.files, function(file){
29993             
29994             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29995                 var f = this.renderPreview(file);
29996                 files.push(f);
29997                 return;
29998             }
29999             
30000             if(file.type.indexOf('image') != -1){
30001                 this.delegates.push(
30002                     (function(){
30003                         _this.process(file);
30004                     }).createDelegate(this)
30005                 );
30006         
30007                 return;
30008             }
30009             
30010             docs.push(
30011                 (function(){
30012                     _this.process(file);
30013                 }).createDelegate(this)
30014             );
30015             
30016         }, this);
30017         
30018         this.files = files;
30019         
30020         this.delegates = this.delegates.concat(docs);
30021         
30022         if(!this.delegates.length){
30023             this.refresh();
30024             return;
30025         }
30026         
30027         this.progressBar.aria_valuemax = this.delegates.length;
30028         
30029         this.arrange();
30030         
30031         return;
30032     },
30033     
30034     arrange : function()
30035     {
30036         if(!this.delegates.length){
30037             this.progressDialog.hide();
30038             this.refresh();
30039             return;
30040         }
30041         
30042         var delegate = this.delegates.shift();
30043         
30044         this.progressDialog.show();
30045         
30046         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
30047         
30048         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
30049         
30050         delegate();
30051     },
30052     
30053     refresh : function()
30054     {
30055         this.uploader.show();
30056         
30057         if(this.boxes > 0 && this.files.length > this.boxes - 1){
30058             this.uploader.hide();
30059         }
30060         
30061         Roo.isTouch ? this.closable(false) : this.closable(true);
30062         
30063         this.fireEvent('refresh', this);
30064     },
30065     
30066     onRemove : function(e, el, o)
30067     {
30068         e.preventDefault();
30069         
30070         this.fireEvent('remove', this, o);
30071         
30072     },
30073     
30074     remove : function(o)
30075     {
30076         var files = [];
30077         
30078         Roo.each(this.files, function(file){
30079             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
30080                 files.push(file);
30081                 return;
30082             }
30083
30084             o.target.remove();
30085
30086         }, this);
30087         
30088         this.files = files;
30089         
30090         this.refresh();
30091     },
30092     
30093     clear : function()
30094     {
30095         Roo.each(this.files, function(file){
30096             if(!file.target){
30097                 return;
30098             }
30099             
30100             file.target.remove();
30101
30102         }, this);
30103         
30104         this.files = [];
30105         
30106         this.refresh();
30107     },
30108     
30109     onClick : function(e, el, o)
30110     {
30111         e.preventDefault();
30112         
30113         this.fireEvent('click', this, o);
30114         
30115     },
30116     
30117     closable : function(closable)
30118     {
30119         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
30120             
30121             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30122             
30123             if(closable){
30124                 el.show();
30125                 return;
30126             }
30127             
30128             el.hide();
30129             
30130         }, this);
30131     },
30132     
30133     xhrOnLoad : function(xhr)
30134     {
30135         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30136             el.remove();
30137         }, this);
30138         
30139         if (xhr.readyState !== 4) {
30140             this.arrange();
30141             this.fireEvent('exception', this, xhr);
30142             return;
30143         }
30144
30145         var response = Roo.decode(xhr.responseText);
30146         
30147         if(!response.success){
30148             this.arrange();
30149             this.fireEvent('exception', this, xhr);
30150             return;
30151         }
30152         
30153         var file = this.renderPreview(response.data);
30154         
30155         this.files.push(file);
30156         
30157         this.arrange();
30158         
30159         this.fireEvent('afterupload', this, xhr);
30160         
30161     },
30162     
30163     xhrOnError : function(xhr)
30164     {
30165         Roo.log('xhr on error');
30166         
30167         var response = Roo.decode(xhr.responseText);
30168           
30169         Roo.log(response);
30170         
30171         this.arrange();
30172     },
30173     
30174     process : function(file)
30175     {
30176         if(this.fireEvent('process', this, file) !== false){
30177             if(this.editable && file.type.indexOf('image') != -1){
30178                 this.fireEvent('edit', this, file);
30179                 return;
30180             }
30181
30182             this.uploadStart(file, false);
30183
30184             return;
30185         }
30186         
30187     },
30188     
30189     uploadStart : function(file, crop)
30190     {
30191         this.xhr = new XMLHttpRequest();
30192         
30193         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30194             this.arrange();
30195             return;
30196         }
30197         
30198         file.xhr = this.xhr;
30199             
30200         this.managerEl.createChild({
30201             tag : 'div',
30202             cls : 'roo-document-manager-loading',
30203             cn : [
30204                 {
30205                     tag : 'div',
30206                     tooltip : file.name,
30207                     cls : 'roo-document-manager-thumb',
30208                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30209                 }
30210             ]
30211
30212         });
30213
30214         this.xhr.open(this.method, this.url, true);
30215         
30216         var headers = {
30217             "Accept": "application/json",
30218             "Cache-Control": "no-cache",
30219             "X-Requested-With": "XMLHttpRequest"
30220         };
30221         
30222         for (var headerName in headers) {
30223             var headerValue = headers[headerName];
30224             if (headerValue) {
30225                 this.xhr.setRequestHeader(headerName, headerValue);
30226             }
30227         }
30228         
30229         var _this = this;
30230         
30231         this.xhr.onload = function()
30232         {
30233             _this.xhrOnLoad(_this.xhr);
30234         }
30235         
30236         this.xhr.onerror = function()
30237         {
30238             _this.xhrOnError(_this.xhr);
30239         }
30240         
30241         var formData = new FormData();
30242
30243         formData.append('returnHTML', 'NO');
30244         
30245         if(crop){
30246             formData.append('crop', crop);
30247         }
30248         
30249         formData.append(this.paramName, file, file.name);
30250         
30251         var options = {
30252             file : file, 
30253             manually : false
30254         };
30255         
30256         if(this.fireEvent('prepare', this, formData, options) != false){
30257             
30258             if(options.manually){
30259                 return;
30260             }
30261             
30262             this.xhr.send(formData);
30263             return;
30264         };
30265         
30266         this.uploadCancel();
30267     },
30268     
30269     uploadCancel : function()
30270     {
30271         if (this.xhr) {
30272             this.xhr.abort();
30273         }
30274         
30275         this.delegates = [];
30276         
30277         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30278             el.remove();
30279         }, this);
30280         
30281         this.arrange();
30282     },
30283     
30284     renderPreview : function(file)
30285     {
30286         if(typeof(file.target) != 'undefined' && file.target){
30287             return file;
30288         }
30289         
30290         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
30291         
30292         var previewEl = this.managerEl.createChild({
30293             tag : 'div',
30294             cls : 'roo-document-manager-preview',
30295             cn : [
30296                 {
30297                     tag : 'div',
30298                     tooltip : file[this.toolTipName],
30299                     cls : 'roo-document-manager-thumb',
30300                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
30301                 },
30302                 {
30303                     tag : 'button',
30304                     cls : 'close',
30305                     html : '<i class="fa fa-times-circle"></i>'
30306                 }
30307             ]
30308         });
30309
30310         var close = previewEl.select('button.close', true).first();
30311
30312         close.on('click', this.onRemove, this, file);
30313
30314         file.target = previewEl;
30315
30316         var image = previewEl.select('img', true).first();
30317         
30318         var _this = this;
30319         
30320         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
30321         
30322         image.on('click', this.onClick, this, file);
30323         
30324         this.fireEvent('previewrendered', this, file);
30325         
30326         return file;
30327         
30328     },
30329     
30330     onPreviewLoad : function(file, image)
30331     {
30332         if(typeof(file.target) == 'undefined' || !file.target){
30333             return;
30334         }
30335         
30336         var width = image.dom.naturalWidth || image.dom.width;
30337         var height = image.dom.naturalHeight || image.dom.height;
30338         
30339         if(!this.previewResize) {
30340             return;
30341         }
30342         
30343         if(width > height){
30344             file.target.addClass('wide');
30345             return;
30346         }
30347         
30348         file.target.addClass('tall');
30349         return;
30350         
30351     },
30352     
30353     uploadFromSource : function(file, crop)
30354     {
30355         this.xhr = new XMLHttpRequest();
30356         
30357         this.managerEl.createChild({
30358             tag : 'div',
30359             cls : 'roo-document-manager-loading',
30360             cn : [
30361                 {
30362                     tag : 'div',
30363                     tooltip : file.name,
30364                     cls : 'roo-document-manager-thumb',
30365                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30366                 }
30367             ]
30368
30369         });
30370
30371         this.xhr.open(this.method, this.url, true);
30372         
30373         var headers = {
30374             "Accept": "application/json",
30375             "Cache-Control": "no-cache",
30376             "X-Requested-With": "XMLHttpRequest"
30377         };
30378         
30379         for (var headerName in headers) {
30380             var headerValue = headers[headerName];
30381             if (headerValue) {
30382                 this.xhr.setRequestHeader(headerName, headerValue);
30383             }
30384         }
30385         
30386         var _this = this;
30387         
30388         this.xhr.onload = function()
30389         {
30390             _this.xhrOnLoad(_this.xhr);
30391         }
30392         
30393         this.xhr.onerror = function()
30394         {
30395             _this.xhrOnError(_this.xhr);
30396         }
30397         
30398         var formData = new FormData();
30399
30400         formData.append('returnHTML', 'NO');
30401         
30402         formData.append('crop', crop);
30403         
30404         if(typeof(file.filename) != 'undefined'){
30405             formData.append('filename', file.filename);
30406         }
30407         
30408         if(typeof(file.mimetype) != 'undefined'){
30409             formData.append('mimetype', file.mimetype);
30410         }
30411         
30412         Roo.log(formData);
30413         
30414         if(this.fireEvent('prepare', this, formData) != false){
30415             this.xhr.send(formData);
30416         };
30417     }
30418 });
30419
30420 /*
30421 * Licence: LGPL
30422 */
30423
30424 /**
30425  * @class Roo.bootstrap.DocumentViewer
30426  * @extends Roo.bootstrap.Component
30427  * Bootstrap DocumentViewer class
30428  * @cfg {Boolean} showDownload (true|false) show download button (default true)
30429  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30430  * 
30431  * @constructor
30432  * Create a new DocumentViewer
30433  * @param {Object} config The config object
30434  */
30435
30436 Roo.bootstrap.DocumentViewer = function(config){
30437     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30438     
30439     this.addEvents({
30440         /**
30441          * @event initial
30442          * Fire after initEvent
30443          * @param {Roo.bootstrap.DocumentViewer} this
30444          */
30445         "initial" : true,
30446         /**
30447          * @event click
30448          * Fire after click
30449          * @param {Roo.bootstrap.DocumentViewer} this
30450          */
30451         "click" : true,
30452         /**
30453          * @event download
30454          * Fire after download button
30455          * @param {Roo.bootstrap.DocumentViewer} this
30456          */
30457         "download" : true,
30458         /**
30459          * @event trash
30460          * Fire after trash button
30461          * @param {Roo.bootstrap.DocumentViewer} this
30462          */
30463         "trash" : true
30464         
30465     });
30466 };
30467
30468 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
30469     
30470     showDownload : true,
30471     
30472     showTrash : true,
30473     
30474     getAutoCreate : function()
30475     {
30476         var cfg = {
30477             tag : 'div',
30478             cls : 'roo-document-viewer',
30479             cn : [
30480                 {
30481                     tag : 'div',
30482                     cls : 'roo-document-viewer-body',
30483                     cn : [
30484                         {
30485                             tag : 'div',
30486                             cls : 'roo-document-viewer-thumb',
30487                             cn : [
30488                                 {
30489                                     tag : 'img',
30490                                     cls : 'roo-document-viewer-image'
30491                                 }
30492                             ]
30493                         }
30494                     ]
30495                 },
30496                 {
30497                     tag : 'div',
30498                     cls : 'roo-document-viewer-footer',
30499                     cn : {
30500                         tag : 'div',
30501                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
30502                         cn : [
30503                             {
30504                                 tag : 'div',
30505                                 cls : 'btn-group roo-document-viewer-download',
30506                                 cn : [
30507                                     {
30508                                         tag : 'button',
30509                                         cls : 'btn btn-default',
30510                                         html : '<i class="fa fa-download"></i>'
30511                                     }
30512                                 ]
30513                             },
30514                             {
30515                                 tag : 'div',
30516                                 cls : 'btn-group roo-document-viewer-trash',
30517                                 cn : [
30518                                     {
30519                                         tag : 'button',
30520                                         cls : 'btn btn-default',
30521                                         html : '<i class="fa fa-trash"></i>'
30522                                     }
30523                                 ]
30524                             }
30525                         ]
30526                     }
30527                 }
30528             ]
30529         };
30530         
30531         return cfg;
30532     },
30533     
30534     initEvents : function()
30535     {
30536         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30537         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30538         
30539         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30540         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30541         
30542         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30543         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30544         
30545         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30546         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30547         
30548         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30549         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30550         
30551         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30552         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30553         
30554         this.bodyEl.on('click', this.onClick, this);
30555         this.downloadBtn.on('click', this.onDownload, this);
30556         this.trashBtn.on('click', this.onTrash, this);
30557         
30558         this.downloadBtn.hide();
30559         this.trashBtn.hide();
30560         
30561         if(this.showDownload){
30562             this.downloadBtn.show();
30563         }
30564         
30565         if(this.showTrash){
30566             this.trashBtn.show();
30567         }
30568         
30569         if(!this.showDownload && !this.showTrash) {
30570             this.footerEl.hide();
30571         }
30572         
30573     },
30574     
30575     initial : function()
30576     {
30577         this.fireEvent('initial', this);
30578         
30579     },
30580     
30581     onClick : function(e)
30582     {
30583         e.preventDefault();
30584         
30585         this.fireEvent('click', this);
30586     },
30587     
30588     onDownload : function(e)
30589     {
30590         e.preventDefault();
30591         
30592         this.fireEvent('download', this);
30593     },
30594     
30595     onTrash : function(e)
30596     {
30597         e.preventDefault();
30598         
30599         this.fireEvent('trash', this);
30600     }
30601     
30602 });
30603 /*
30604  * - LGPL
30605  *
30606  * nav progress bar
30607  * 
30608  */
30609
30610 /**
30611  * @class Roo.bootstrap.NavProgressBar
30612  * @extends Roo.bootstrap.Component
30613  * Bootstrap NavProgressBar class
30614  * 
30615  * @constructor
30616  * Create a new nav progress bar
30617  * @param {Object} config The config object
30618  */
30619
30620 Roo.bootstrap.NavProgressBar = function(config){
30621     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30622
30623     this.bullets = this.bullets || [];
30624    
30625 //    Roo.bootstrap.NavProgressBar.register(this);
30626      this.addEvents({
30627         /**
30628              * @event changed
30629              * Fires when the active item changes
30630              * @param {Roo.bootstrap.NavProgressBar} this
30631              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30632              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30633          */
30634         'changed': true
30635      });
30636     
30637 };
30638
30639 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30640     
30641     bullets : [],
30642     barItems : [],
30643     
30644     getAutoCreate : function()
30645     {
30646         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30647         
30648         cfg = {
30649             tag : 'div',
30650             cls : 'roo-navigation-bar-group',
30651             cn : [
30652                 {
30653                     tag : 'div',
30654                     cls : 'roo-navigation-top-bar'
30655                 },
30656                 {
30657                     tag : 'div',
30658                     cls : 'roo-navigation-bullets-bar',
30659                     cn : [
30660                         {
30661                             tag : 'ul',
30662                             cls : 'roo-navigation-bar'
30663                         }
30664                     ]
30665                 },
30666                 
30667                 {
30668                     tag : 'div',
30669                     cls : 'roo-navigation-bottom-bar'
30670                 }
30671             ]
30672             
30673         };
30674         
30675         return cfg;
30676         
30677     },
30678     
30679     initEvents: function() 
30680     {
30681         
30682     },
30683     
30684     onRender : function(ct, position) 
30685     {
30686         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30687         
30688         if(this.bullets.length){
30689             Roo.each(this.bullets, function(b){
30690                this.addItem(b);
30691             }, this);
30692         }
30693         
30694         this.format();
30695         
30696     },
30697     
30698     addItem : function(cfg)
30699     {
30700         var item = new Roo.bootstrap.NavProgressItem(cfg);
30701         
30702         item.parentId = this.id;
30703         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30704         
30705         if(cfg.html){
30706             var top = new Roo.bootstrap.Element({
30707                 tag : 'div',
30708                 cls : 'roo-navigation-bar-text'
30709             });
30710             
30711             var bottom = new Roo.bootstrap.Element({
30712                 tag : 'div',
30713                 cls : 'roo-navigation-bar-text'
30714             });
30715             
30716             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30717             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30718             
30719             var topText = new Roo.bootstrap.Element({
30720                 tag : 'span',
30721                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30722             });
30723             
30724             var bottomText = new Roo.bootstrap.Element({
30725                 tag : 'span',
30726                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30727             });
30728             
30729             topText.onRender(top.el, null);
30730             bottomText.onRender(bottom.el, null);
30731             
30732             item.topEl = top;
30733             item.bottomEl = bottom;
30734         }
30735         
30736         this.barItems.push(item);
30737         
30738         return item;
30739     },
30740     
30741     getActive : function()
30742     {
30743         var active = false;
30744         
30745         Roo.each(this.barItems, function(v){
30746             
30747             if (!v.isActive()) {
30748                 return;
30749             }
30750             
30751             active = v;
30752             return false;
30753             
30754         });
30755         
30756         return active;
30757     },
30758     
30759     setActiveItem : function(item)
30760     {
30761         var prev = false;
30762         
30763         Roo.each(this.barItems, function(v){
30764             if (v.rid == item.rid) {
30765                 return ;
30766             }
30767             
30768             if (v.isActive()) {
30769                 v.setActive(false);
30770                 prev = v;
30771             }
30772         });
30773
30774         item.setActive(true);
30775         
30776         this.fireEvent('changed', this, item, prev);
30777     },
30778     
30779     getBarItem: function(rid)
30780     {
30781         var ret = false;
30782         
30783         Roo.each(this.barItems, function(e) {
30784             if (e.rid != rid) {
30785                 return;
30786             }
30787             
30788             ret =  e;
30789             return false;
30790         });
30791         
30792         return ret;
30793     },
30794     
30795     indexOfItem : function(item)
30796     {
30797         var index = false;
30798         
30799         Roo.each(this.barItems, function(v, i){
30800             
30801             if (v.rid != item.rid) {
30802                 return;
30803             }
30804             
30805             index = i;
30806             return false
30807         });
30808         
30809         return index;
30810     },
30811     
30812     setActiveNext : function()
30813     {
30814         var i = this.indexOfItem(this.getActive());
30815         
30816         if (i > this.barItems.length) {
30817             return;
30818         }
30819         
30820         this.setActiveItem(this.barItems[i+1]);
30821     },
30822     
30823     setActivePrev : function()
30824     {
30825         var i = this.indexOfItem(this.getActive());
30826         
30827         if (i  < 1) {
30828             return;
30829         }
30830         
30831         this.setActiveItem(this.barItems[i-1]);
30832     },
30833     
30834     format : function()
30835     {
30836         if(!this.barItems.length){
30837             return;
30838         }
30839      
30840         var width = 100 / this.barItems.length;
30841         
30842         Roo.each(this.barItems, function(i){
30843             i.el.setStyle('width', width + '%');
30844             i.topEl.el.setStyle('width', width + '%');
30845             i.bottomEl.el.setStyle('width', width + '%');
30846         }, this);
30847         
30848     }
30849     
30850 });
30851 /*
30852  * - LGPL
30853  *
30854  * Nav Progress Item
30855  * 
30856  */
30857
30858 /**
30859  * @class Roo.bootstrap.NavProgressItem
30860  * @extends Roo.bootstrap.Component
30861  * Bootstrap NavProgressItem class
30862  * @cfg {String} rid the reference id
30863  * @cfg {Boolean} active (true|false) Is item active default false
30864  * @cfg {Boolean} disabled (true|false) Is item active default false
30865  * @cfg {String} html
30866  * @cfg {String} position (top|bottom) text position default bottom
30867  * @cfg {String} icon show icon instead of number
30868  * 
30869  * @constructor
30870  * Create a new NavProgressItem
30871  * @param {Object} config The config object
30872  */
30873 Roo.bootstrap.NavProgressItem = function(config){
30874     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30875     this.addEvents({
30876         // raw events
30877         /**
30878          * @event click
30879          * The raw click event for the entire grid.
30880          * @param {Roo.bootstrap.NavProgressItem} this
30881          * @param {Roo.EventObject} e
30882          */
30883         "click" : true
30884     });
30885    
30886 };
30887
30888 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30889     
30890     rid : '',
30891     active : false,
30892     disabled : false,
30893     html : '',
30894     position : 'bottom',
30895     icon : false,
30896     
30897     getAutoCreate : function()
30898     {
30899         var iconCls = 'roo-navigation-bar-item-icon';
30900         
30901         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30902         
30903         var cfg = {
30904             tag: 'li',
30905             cls: 'roo-navigation-bar-item',
30906             cn : [
30907                 {
30908                     tag : 'i',
30909                     cls : iconCls
30910                 }
30911             ]
30912         };
30913         
30914         if(this.active){
30915             cfg.cls += ' active';
30916         }
30917         if(this.disabled){
30918             cfg.cls += ' disabled';
30919         }
30920         
30921         return cfg;
30922     },
30923     
30924     disable : function()
30925     {
30926         this.setDisabled(true);
30927     },
30928     
30929     enable : function()
30930     {
30931         this.setDisabled(false);
30932     },
30933     
30934     initEvents: function() 
30935     {
30936         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30937         
30938         this.iconEl.on('click', this.onClick, this);
30939     },
30940     
30941     onClick : function(e)
30942     {
30943         e.preventDefault();
30944         
30945         if(this.disabled){
30946             return;
30947         }
30948         
30949         if(this.fireEvent('click', this, e) === false){
30950             return;
30951         };
30952         
30953         this.parent().setActiveItem(this);
30954     },
30955     
30956     isActive: function () 
30957     {
30958         return this.active;
30959     },
30960     
30961     setActive : function(state)
30962     {
30963         if(this.active == state){
30964             return;
30965         }
30966         
30967         this.active = state;
30968         
30969         if (state) {
30970             this.el.addClass('active');
30971             return;
30972         }
30973         
30974         this.el.removeClass('active');
30975         
30976         return;
30977     },
30978     
30979     setDisabled : function(state)
30980     {
30981         if(this.disabled == state){
30982             return;
30983         }
30984         
30985         this.disabled = state;
30986         
30987         if (state) {
30988             this.el.addClass('disabled');
30989             return;
30990         }
30991         
30992         this.el.removeClass('disabled');
30993     },
30994     
30995     tooltipEl : function()
30996     {
30997         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30998     }
30999 });
31000  
31001
31002  /*
31003  * - LGPL
31004  *
31005  * FieldLabel
31006  * 
31007  */
31008
31009 /**
31010  * @class Roo.bootstrap.FieldLabel
31011  * @extends Roo.bootstrap.Component
31012  * Bootstrap FieldLabel class
31013  * @cfg {String} html contents of the element
31014  * @cfg {String} tag tag of the element default label
31015  * @cfg {String} cls class of the element
31016  * @cfg {String} target label target 
31017  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
31018  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
31019  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
31020  * @cfg {String} iconTooltip default "This field is required"
31021  * @cfg {String} indicatorpos (left|right) default left
31022  * 
31023  * @constructor
31024  * Create a new FieldLabel
31025  * @param {Object} config The config object
31026  */
31027
31028 Roo.bootstrap.FieldLabel = function(config){
31029     Roo.bootstrap.Element.superclass.constructor.call(this, config);
31030     
31031     this.addEvents({
31032             /**
31033              * @event invalid
31034              * Fires after the field has been marked as invalid.
31035              * @param {Roo.form.FieldLabel} this
31036              * @param {String} msg The validation message
31037              */
31038             invalid : true,
31039             /**
31040              * @event valid
31041              * Fires after the field has been validated with no errors.
31042              * @param {Roo.form.FieldLabel} this
31043              */
31044             valid : true
31045         });
31046 };
31047
31048 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
31049     
31050     tag: 'label',
31051     cls: '',
31052     html: '',
31053     target: '',
31054     allowBlank : true,
31055     invalidClass : 'has-warning',
31056     validClass : 'has-success',
31057     iconTooltip : 'This field is required',
31058     indicatorpos : 'left',
31059     
31060     getAutoCreate : function(){
31061         
31062         var cls = "";
31063         if (!this.allowBlank) {
31064             cls  = "visible";
31065         }
31066         
31067         var cfg = {
31068             tag : this.tag,
31069             cls : 'roo-bootstrap-field-label ' + this.cls,
31070             for : this.target,
31071             cn : [
31072                 {
31073                     tag : 'i',
31074                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
31075                     tooltip : this.iconTooltip
31076                 },
31077                 {
31078                     tag : 'span',
31079                     html : this.html
31080                 }
31081             ] 
31082         };
31083         
31084         if(this.indicatorpos == 'right'){
31085             var cfg = {
31086                 tag : this.tag,
31087                 cls : 'roo-bootstrap-field-label ' + this.cls,
31088                 for : this.target,
31089                 cn : [
31090                     {
31091                         tag : 'span',
31092                         html : this.html
31093                     },
31094                     {
31095                         tag : 'i',
31096                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
31097                         tooltip : this.iconTooltip
31098                     }
31099                 ] 
31100             };
31101         }
31102         
31103         return cfg;
31104     },
31105     
31106     initEvents: function() 
31107     {
31108         Roo.bootstrap.Element.superclass.initEvents.call(this);
31109         
31110         this.indicator = this.indicatorEl();
31111         
31112         if(this.indicator){
31113             this.indicator.removeClass('visible');
31114             this.indicator.addClass('invisible');
31115         }
31116         
31117         Roo.bootstrap.FieldLabel.register(this);
31118     },
31119     
31120     indicatorEl : function()
31121     {
31122         var indicator = this.el.select('i.roo-required-indicator',true).first();
31123         
31124         if(!indicator){
31125             return false;
31126         }
31127         
31128         return indicator;
31129         
31130     },
31131     
31132     /**
31133      * Mark this field as valid
31134      */
31135     markValid : function()
31136     {
31137         if(this.indicator){
31138             this.indicator.removeClass('visible');
31139             this.indicator.addClass('invisible');
31140         }
31141         if (Roo.bootstrap.version == 3) {
31142             this.el.removeClass(this.invalidClass);
31143             this.el.addClass(this.validClass);
31144         } else {
31145             this.el.removeClass('is-invalid');
31146             this.el.addClass('is-valid');
31147         }
31148         
31149         
31150         this.fireEvent('valid', this);
31151     },
31152     
31153     /**
31154      * Mark this field as invalid
31155      * @param {String} msg The validation message
31156      */
31157     markInvalid : function(msg)
31158     {
31159         if(this.indicator){
31160             this.indicator.removeClass('invisible');
31161             this.indicator.addClass('visible');
31162         }
31163           if (Roo.bootstrap.version == 3) {
31164             this.el.removeClass(this.validClass);
31165             this.el.addClass(this.invalidClass);
31166         } else {
31167             this.el.removeClass('is-valid');
31168             this.el.addClass('is-invalid');
31169         }
31170         
31171         
31172         this.fireEvent('invalid', this, msg);
31173     }
31174     
31175    
31176 });
31177
31178 Roo.apply(Roo.bootstrap.FieldLabel, {
31179     
31180     groups: {},
31181     
31182      /**
31183     * register a FieldLabel Group
31184     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
31185     */
31186     register : function(label)
31187     {
31188         if(this.groups.hasOwnProperty(label.target)){
31189             return;
31190         }
31191      
31192         this.groups[label.target] = label;
31193         
31194     },
31195     /**
31196     * fetch a FieldLabel Group based on the target
31197     * @param {string} target
31198     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
31199     */
31200     get: function(target) {
31201         if (typeof(this.groups[target]) == 'undefined') {
31202             return false;
31203         }
31204         
31205         return this.groups[target] ;
31206     }
31207 });
31208
31209  
31210
31211  /*
31212  * - LGPL
31213  *
31214  * page DateSplitField.
31215  * 
31216  */
31217
31218
31219 /**
31220  * @class Roo.bootstrap.DateSplitField
31221  * @extends Roo.bootstrap.Component
31222  * Bootstrap DateSplitField class
31223  * @cfg {string} fieldLabel - the label associated
31224  * @cfg {Number} labelWidth set the width of label (0-12)
31225  * @cfg {String} labelAlign (top|left)
31226  * @cfg {Boolean} dayAllowBlank (true|false) default false
31227  * @cfg {Boolean} monthAllowBlank (true|false) default false
31228  * @cfg {Boolean} yearAllowBlank (true|false) default false
31229  * @cfg {string} dayPlaceholder 
31230  * @cfg {string} monthPlaceholder
31231  * @cfg {string} yearPlaceholder
31232  * @cfg {string} dayFormat default 'd'
31233  * @cfg {string} monthFormat default 'm'
31234  * @cfg {string} yearFormat default 'Y'
31235  * @cfg {Number} labellg set the width of label (1-12)
31236  * @cfg {Number} labelmd set the width of label (1-12)
31237  * @cfg {Number} labelsm set the width of label (1-12)
31238  * @cfg {Number} labelxs set the width of label (1-12)
31239
31240  *     
31241  * @constructor
31242  * Create a new DateSplitField
31243  * @param {Object} config The config object
31244  */
31245
31246 Roo.bootstrap.DateSplitField = function(config){
31247     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
31248     
31249     this.addEvents({
31250         // raw events
31251          /**
31252          * @event years
31253          * getting the data of years
31254          * @param {Roo.bootstrap.DateSplitField} this
31255          * @param {Object} years
31256          */
31257         "years" : true,
31258         /**
31259          * @event days
31260          * getting the data of days
31261          * @param {Roo.bootstrap.DateSplitField} this
31262          * @param {Object} days
31263          */
31264         "days" : true,
31265         /**
31266          * @event invalid
31267          * Fires after the field has been marked as invalid.
31268          * @param {Roo.form.Field} this
31269          * @param {String} msg The validation message
31270          */
31271         invalid : true,
31272        /**
31273          * @event valid
31274          * Fires after the field has been validated with no errors.
31275          * @param {Roo.form.Field} this
31276          */
31277         valid : true
31278     });
31279 };
31280
31281 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
31282     
31283     fieldLabel : '',
31284     labelAlign : 'top',
31285     labelWidth : 3,
31286     dayAllowBlank : false,
31287     monthAllowBlank : false,
31288     yearAllowBlank : false,
31289     dayPlaceholder : '',
31290     monthPlaceholder : '',
31291     yearPlaceholder : '',
31292     dayFormat : 'd',
31293     monthFormat : 'm',
31294     yearFormat : 'Y',
31295     isFormField : true,
31296     labellg : 0,
31297     labelmd : 0,
31298     labelsm : 0,
31299     labelxs : 0,
31300     
31301     getAutoCreate : function()
31302     {
31303         var cfg = {
31304             tag : 'div',
31305             cls : 'row roo-date-split-field-group',
31306             cn : [
31307                 {
31308                     tag : 'input',
31309                     type : 'hidden',
31310                     cls : 'form-hidden-field roo-date-split-field-group-value',
31311                     name : this.name
31312                 }
31313             ]
31314         };
31315         
31316         var labelCls = 'col-md-12';
31317         var contentCls = 'col-md-4';
31318         
31319         if(this.fieldLabel){
31320             
31321             var label = {
31322                 tag : 'div',
31323                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
31324                 cn : [
31325                     {
31326                         tag : 'label',
31327                         html : this.fieldLabel
31328                     }
31329                 ]
31330             };
31331             
31332             if(this.labelAlign == 'left'){
31333             
31334                 if(this.labelWidth > 12){
31335                     label.style = "width: " + this.labelWidth + 'px';
31336                 }
31337
31338                 if(this.labelWidth < 13 && this.labelmd == 0){
31339                     this.labelmd = this.labelWidth;
31340                 }
31341
31342                 if(this.labellg > 0){
31343                     labelCls = ' col-lg-' + this.labellg;
31344                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
31345                 }
31346
31347                 if(this.labelmd > 0){
31348                     labelCls = ' col-md-' + this.labelmd;
31349                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
31350                 }
31351
31352                 if(this.labelsm > 0){
31353                     labelCls = ' col-sm-' + this.labelsm;
31354                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
31355                 }
31356
31357                 if(this.labelxs > 0){
31358                     labelCls = ' col-xs-' + this.labelxs;
31359                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
31360                 }
31361             }
31362             
31363             label.cls += ' ' + labelCls;
31364             
31365             cfg.cn.push(label);
31366         }
31367         
31368         Roo.each(['day', 'month', 'year'], function(t){
31369             cfg.cn.push({
31370                 tag : 'div',
31371                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
31372             });
31373         }, this);
31374         
31375         return cfg;
31376     },
31377     
31378     inputEl: function ()
31379     {
31380         return this.el.select('.roo-date-split-field-group-value', true).first();
31381     },
31382     
31383     onRender : function(ct, position) 
31384     {
31385         var _this = this;
31386         
31387         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31388         
31389         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
31390         
31391         this.dayField = new Roo.bootstrap.ComboBox({
31392             allowBlank : this.dayAllowBlank,
31393             alwaysQuery : true,
31394             displayField : 'value',
31395             editable : false,
31396             fieldLabel : '',
31397             forceSelection : true,
31398             mode : 'local',
31399             placeholder : this.dayPlaceholder,
31400             selectOnFocus : true,
31401             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31402             triggerAction : 'all',
31403             typeAhead : true,
31404             valueField : 'value',
31405             store : new Roo.data.SimpleStore({
31406                 data : (function() {    
31407                     var days = [];
31408                     _this.fireEvent('days', _this, days);
31409                     return days;
31410                 })(),
31411                 fields : [ 'value' ]
31412             }),
31413             listeners : {
31414                 select : function (_self, record, index)
31415                 {
31416                     _this.setValue(_this.getValue());
31417                 }
31418             }
31419         });
31420
31421         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
31422         
31423         this.monthField = new Roo.bootstrap.MonthField({
31424             after : '<i class=\"fa fa-calendar\"></i>',
31425             allowBlank : this.monthAllowBlank,
31426             placeholder : this.monthPlaceholder,
31427             readOnly : true,
31428             listeners : {
31429                 render : function (_self)
31430                 {
31431                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
31432                         e.preventDefault();
31433                         _self.focus();
31434                     });
31435                 },
31436                 select : function (_self, oldvalue, newvalue)
31437                 {
31438                     _this.setValue(_this.getValue());
31439                 }
31440             }
31441         });
31442         
31443         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31444         
31445         this.yearField = new Roo.bootstrap.ComboBox({
31446             allowBlank : this.yearAllowBlank,
31447             alwaysQuery : true,
31448             displayField : 'value',
31449             editable : false,
31450             fieldLabel : '',
31451             forceSelection : true,
31452             mode : 'local',
31453             placeholder : this.yearPlaceholder,
31454             selectOnFocus : true,
31455             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31456             triggerAction : 'all',
31457             typeAhead : true,
31458             valueField : 'value',
31459             store : new Roo.data.SimpleStore({
31460                 data : (function() {
31461                     var years = [];
31462                     _this.fireEvent('years', _this, years);
31463                     return years;
31464                 })(),
31465                 fields : [ 'value' ]
31466             }),
31467             listeners : {
31468                 select : function (_self, record, index)
31469                 {
31470                     _this.setValue(_this.getValue());
31471                 }
31472             }
31473         });
31474
31475         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
31476     },
31477     
31478     setValue : function(v, format)
31479     {
31480         this.inputEl.dom.value = v;
31481         
31482         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31483         
31484         var d = Date.parseDate(v, f);
31485         
31486         if(!d){
31487             this.validate();
31488             return;
31489         }
31490         
31491         this.setDay(d.format(this.dayFormat));
31492         this.setMonth(d.format(this.monthFormat));
31493         this.setYear(d.format(this.yearFormat));
31494         
31495         this.validate();
31496         
31497         return;
31498     },
31499     
31500     setDay : function(v)
31501     {
31502         this.dayField.setValue(v);
31503         this.inputEl.dom.value = this.getValue();
31504         this.validate();
31505         return;
31506     },
31507     
31508     setMonth : function(v)
31509     {
31510         this.monthField.setValue(v, true);
31511         this.inputEl.dom.value = this.getValue();
31512         this.validate();
31513         return;
31514     },
31515     
31516     setYear : function(v)
31517     {
31518         this.yearField.setValue(v);
31519         this.inputEl.dom.value = this.getValue();
31520         this.validate();
31521         return;
31522     },
31523     
31524     getDay : function()
31525     {
31526         return this.dayField.getValue();
31527     },
31528     
31529     getMonth : function()
31530     {
31531         return this.monthField.getValue();
31532     },
31533     
31534     getYear : function()
31535     {
31536         return this.yearField.getValue();
31537     },
31538     
31539     getValue : function()
31540     {
31541         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31542         
31543         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31544         
31545         return date;
31546     },
31547     
31548     reset : function()
31549     {
31550         this.setDay('');
31551         this.setMonth('');
31552         this.setYear('');
31553         this.inputEl.dom.value = '';
31554         this.validate();
31555         return;
31556     },
31557     
31558     validate : function()
31559     {
31560         var d = this.dayField.validate();
31561         var m = this.monthField.validate();
31562         var y = this.yearField.validate();
31563         
31564         var valid = true;
31565         
31566         if(
31567                 (!this.dayAllowBlank && !d) ||
31568                 (!this.monthAllowBlank && !m) ||
31569                 (!this.yearAllowBlank && !y)
31570         ){
31571             valid = false;
31572         }
31573         
31574         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31575             return valid;
31576         }
31577         
31578         if(valid){
31579             this.markValid();
31580             return valid;
31581         }
31582         
31583         this.markInvalid();
31584         
31585         return valid;
31586     },
31587     
31588     markValid : function()
31589     {
31590         
31591         var label = this.el.select('label', true).first();
31592         var icon = this.el.select('i.fa-star', true).first();
31593
31594         if(label && icon){
31595             icon.remove();
31596         }
31597         
31598         this.fireEvent('valid', this);
31599     },
31600     
31601      /**
31602      * Mark this field as invalid
31603      * @param {String} msg The validation message
31604      */
31605     markInvalid : function(msg)
31606     {
31607         
31608         var label = this.el.select('label', true).first();
31609         var icon = this.el.select('i.fa-star', true).first();
31610
31611         if(label && !icon){
31612             this.el.select('.roo-date-split-field-label', true).createChild({
31613                 tag : 'i',
31614                 cls : 'text-danger fa fa-lg fa-star',
31615                 tooltip : 'This field is required',
31616                 style : 'margin-right:5px;'
31617             }, label, true);
31618         }
31619         
31620         this.fireEvent('invalid', this, msg);
31621     },
31622     
31623     clearInvalid : function()
31624     {
31625         var label = this.el.select('label', true).first();
31626         var icon = this.el.select('i.fa-star', true).first();
31627
31628         if(label && icon){
31629             icon.remove();
31630         }
31631         
31632         this.fireEvent('valid', this);
31633     },
31634     
31635     getName: function()
31636     {
31637         return this.name;
31638     }
31639     
31640 });
31641
31642  /**
31643  *
31644  * This is based on 
31645  * http://masonry.desandro.com
31646  *
31647  * The idea is to render all the bricks based on vertical width...
31648  *
31649  * The original code extends 'outlayer' - we might need to use that....
31650  * 
31651  */
31652
31653
31654 /**
31655  * @class Roo.bootstrap.LayoutMasonry
31656  * @extends Roo.bootstrap.Component
31657  * Bootstrap Layout Masonry class
31658  * 
31659  * @constructor
31660  * Create a new Element
31661  * @param {Object} config The config object
31662  */
31663
31664 Roo.bootstrap.LayoutMasonry = function(config){
31665     
31666     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31667     
31668     this.bricks = [];
31669     
31670     Roo.bootstrap.LayoutMasonry.register(this);
31671     
31672     this.addEvents({
31673         // raw events
31674         /**
31675          * @event layout
31676          * Fire after layout the items
31677          * @param {Roo.bootstrap.LayoutMasonry} this
31678          * @param {Roo.EventObject} e
31679          */
31680         "layout" : true
31681     });
31682     
31683 };
31684
31685 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31686     
31687     /**
31688      * @cfg {Boolean} isLayoutInstant = no animation?
31689      */   
31690     isLayoutInstant : false, // needed?
31691    
31692     /**
31693      * @cfg {Number} boxWidth  width of the columns
31694      */   
31695     boxWidth : 450,
31696     
31697       /**
31698      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31699      */   
31700     boxHeight : 0,
31701     
31702     /**
31703      * @cfg {Number} padWidth padding below box..
31704      */   
31705     padWidth : 10, 
31706     
31707     /**
31708      * @cfg {Number} gutter gutter width..
31709      */   
31710     gutter : 10,
31711     
31712      /**
31713      * @cfg {Number} maxCols maximum number of columns
31714      */   
31715     
31716     maxCols: 0,
31717     
31718     /**
31719      * @cfg {Boolean} isAutoInitial defalut true
31720      */   
31721     isAutoInitial : true, 
31722     
31723     containerWidth: 0,
31724     
31725     /**
31726      * @cfg {Boolean} isHorizontal defalut false
31727      */   
31728     isHorizontal : false, 
31729
31730     currentSize : null,
31731     
31732     tag: 'div',
31733     
31734     cls: '',
31735     
31736     bricks: null, //CompositeElement
31737     
31738     cols : 1,
31739     
31740     _isLayoutInited : false,
31741     
31742 //    isAlternative : false, // only use for vertical layout...
31743     
31744     /**
31745      * @cfg {Number} alternativePadWidth padding below box..
31746      */   
31747     alternativePadWidth : 50,
31748     
31749     selectedBrick : [],
31750     
31751     getAutoCreate : function(){
31752         
31753         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31754         
31755         var cfg = {
31756             tag: this.tag,
31757             cls: 'blog-masonary-wrapper ' + this.cls,
31758             cn : {
31759                 cls : 'mas-boxes masonary'
31760             }
31761         };
31762         
31763         return cfg;
31764     },
31765     
31766     getChildContainer: function( )
31767     {
31768         if (this.boxesEl) {
31769             return this.boxesEl;
31770         }
31771         
31772         this.boxesEl = this.el.select('.mas-boxes').first();
31773         
31774         return this.boxesEl;
31775     },
31776     
31777     
31778     initEvents : function()
31779     {
31780         var _this = this;
31781         
31782         if(this.isAutoInitial){
31783             Roo.log('hook children rendered');
31784             this.on('childrenrendered', function() {
31785                 Roo.log('children rendered');
31786                 _this.initial();
31787             } ,this);
31788         }
31789     },
31790     
31791     initial : function()
31792     {
31793         this.selectedBrick = [];
31794         
31795         this.currentSize = this.el.getBox(true);
31796         
31797         Roo.EventManager.onWindowResize(this.resize, this); 
31798
31799         if(!this.isAutoInitial){
31800             this.layout();
31801             return;
31802         }
31803         
31804         this.layout();
31805         
31806         return;
31807         //this.layout.defer(500,this);
31808         
31809     },
31810     
31811     resize : function()
31812     {
31813         var cs = this.el.getBox(true);
31814         
31815         if (
31816                 this.currentSize.width == cs.width && 
31817                 this.currentSize.x == cs.x && 
31818                 this.currentSize.height == cs.height && 
31819                 this.currentSize.y == cs.y 
31820         ) {
31821             Roo.log("no change in with or X or Y");
31822             return;
31823         }
31824         
31825         this.currentSize = cs;
31826         
31827         this.layout();
31828         
31829     },
31830     
31831     layout : function()
31832     {   
31833         this._resetLayout();
31834         
31835         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31836         
31837         this.layoutItems( isInstant );
31838       
31839         this._isLayoutInited = true;
31840         
31841         this.fireEvent('layout', this);
31842         
31843     },
31844     
31845     _resetLayout : function()
31846     {
31847         if(this.isHorizontal){
31848             this.horizontalMeasureColumns();
31849             return;
31850         }
31851         
31852         this.verticalMeasureColumns();
31853         
31854     },
31855     
31856     verticalMeasureColumns : function()
31857     {
31858         this.getContainerWidth();
31859         
31860 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31861 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31862 //            return;
31863 //        }
31864         
31865         var boxWidth = this.boxWidth + this.padWidth;
31866         
31867         if(this.containerWidth < this.boxWidth){
31868             boxWidth = this.containerWidth
31869         }
31870         
31871         var containerWidth = this.containerWidth;
31872         
31873         var cols = Math.floor(containerWidth / boxWidth);
31874         
31875         this.cols = Math.max( cols, 1 );
31876         
31877         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31878         
31879         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31880         
31881         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31882         
31883         this.colWidth = boxWidth + avail - this.padWidth;
31884         
31885         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31886         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31887     },
31888     
31889     horizontalMeasureColumns : function()
31890     {
31891         this.getContainerWidth();
31892         
31893         var boxWidth = this.boxWidth;
31894         
31895         if(this.containerWidth < boxWidth){
31896             boxWidth = this.containerWidth;
31897         }
31898         
31899         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31900         
31901         this.el.setHeight(boxWidth);
31902         
31903     },
31904     
31905     getContainerWidth : function()
31906     {
31907         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31908     },
31909     
31910     layoutItems : function( isInstant )
31911     {
31912         Roo.log(this.bricks);
31913         
31914         var items = Roo.apply([], this.bricks);
31915         
31916         if(this.isHorizontal){
31917             this._horizontalLayoutItems( items , isInstant );
31918             return;
31919         }
31920         
31921 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31922 //            this._verticalAlternativeLayoutItems( items , isInstant );
31923 //            return;
31924 //        }
31925         
31926         this._verticalLayoutItems( items , isInstant );
31927         
31928     },
31929     
31930     _verticalLayoutItems : function ( items , isInstant)
31931     {
31932         if ( !items || !items.length ) {
31933             return;
31934         }
31935         
31936         var standard = [
31937             ['xs', 'xs', 'xs', 'tall'],
31938             ['xs', 'xs', 'tall'],
31939             ['xs', 'xs', 'sm'],
31940             ['xs', 'xs', 'xs'],
31941             ['xs', 'tall'],
31942             ['xs', 'sm'],
31943             ['xs', 'xs'],
31944             ['xs'],
31945             
31946             ['sm', 'xs', 'xs'],
31947             ['sm', 'xs'],
31948             ['sm'],
31949             
31950             ['tall', 'xs', 'xs', 'xs'],
31951             ['tall', 'xs', 'xs'],
31952             ['tall', 'xs'],
31953             ['tall']
31954             
31955         ];
31956         
31957         var queue = [];
31958         
31959         var boxes = [];
31960         
31961         var box = [];
31962         
31963         Roo.each(items, function(item, k){
31964             
31965             switch (item.size) {
31966                 // these layouts take up a full box,
31967                 case 'md' :
31968                 case 'md-left' :
31969                 case 'md-right' :
31970                 case 'wide' :
31971                     
31972                     if(box.length){
31973                         boxes.push(box);
31974                         box = [];
31975                     }
31976                     
31977                     boxes.push([item]);
31978                     
31979                     break;
31980                     
31981                 case 'xs' :
31982                 case 'sm' :
31983                 case 'tall' :
31984                     
31985                     box.push(item);
31986                     
31987                     break;
31988                 default :
31989                     break;
31990                     
31991             }
31992             
31993         }, this);
31994         
31995         if(box.length){
31996             boxes.push(box);
31997             box = [];
31998         }
31999         
32000         var filterPattern = function(box, length)
32001         {
32002             if(!box.length){
32003                 return;
32004             }
32005             
32006             var match = false;
32007             
32008             var pattern = box.slice(0, length);
32009             
32010             var format = [];
32011             
32012             Roo.each(pattern, function(i){
32013                 format.push(i.size);
32014             }, this);
32015             
32016             Roo.each(standard, function(s){
32017                 
32018                 if(String(s) != String(format)){
32019                     return;
32020                 }
32021                 
32022                 match = true;
32023                 return false;
32024                 
32025             }, this);
32026             
32027             if(!match && length == 1){
32028                 return;
32029             }
32030             
32031             if(!match){
32032                 filterPattern(box, length - 1);
32033                 return;
32034             }
32035                 
32036             queue.push(pattern);
32037
32038             box = box.slice(length, box.length);
32039
32040             filterPattern(box, 4);
32041
32042             return;
32043             
32044         }
32045         
32046         Roo.each(boxes, function(box, k){
32047             
32048             if(!box.length){
32049                 return;
32050             }
32051             
32052             if(box.length == 1){
32053                 queue.push(box);
32054                 return;
32055             }
32056             
32057             filterPattern(box, 4);
32058             
32059         }, this);
32060         
32061         this._processVerticalLayoutQueue( queue, isInstant );
32062         
32063     },
32064     
32065 //    _verticalAlternativeLayoutItems : function( items , isInstant )
32066 //    {
32067 //        if ( !items || !items.length ) {
32068 //            return;
32069 //        }
32070 //
32071 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
32072 //        
32073 //    },
32074     
32075     _horizontalLayoutItems : function ( items , isInstant)
32076     {
32077         if ( !items || !items.length || items.length < 3) {
32078             return;
32079         }
32080         
32081         items.reverse();
32082         
32083         var eItems = items.slice(0, 3);
32084         
32085         items = items.slice(3, items.length);
32086         
32087         var standard = [
32088             ['xs', 'xs', 'xs', 'wide'],
32089             ['xs', 'xs', 'wide'],
32090             ['xs', 'xs', 'sm'],
32091             ['xs', 'xs', 'xs'],
32092             ['xs', 'wide'],
32093             ['xs', 'sm'],
32094             ['xs', 'xs'],
32095             ['xs'],
32096             
32097             ['sm', 'xs', 'xs'],
32098             ['sm', 'xs'],
32099             ['sm'],
32100             
32101             ['wide', 'xs', 'xs', 'xs'],
32102             ['wide', 'xs', 'xs'],
32103             ['wide', 'xs'],
32104             ['wide'],
32105             
32106             ['wide-thin']
32107         ];
32108         
32109         var queue = [];
32110         
32111         var boxes = [];
32112         
32113         var box = [];
32114         
32115         Roo.each(items, function(item, k){
32116             
32117             switch (item.size) {
32118                 case 'md' :
32119                 case 'md-left' :
32120                 case 'md-right' :
32121                 case 'tall' :
32122                     
32123                     if(box.length){
32124                         boxes.push(box);
32125                         box = [];
32126                     }
32127                     
32128                     boxes.push([item]);
32129                     
32130                     break;
32131                     
32132                 case 'xs' :
32133                 case 'sm' :
32134                 case 'wide' :
32135                 case 'wide-thin' :
32136                     
32137                     box.push(item);
32138                     
32139                     break;
32140                 default :
32141                     break;
32142                     
32143             }
32144             
32145         }, this);
32146         
32147         if(box.length){
32148             boxes.push(box);
32149             box = [];
32150         }
32151         
32152         var filterPattern = function(box, length)
32153         {
32154             if(!box.length){
32155                 return;
32156             }
32157             
32158             var match = false;
32159             
32160             var pattern = box.slice(0, length);
32161             
32162             var format = [];
32163             
32164             Roo.each(pattern, function(i){
32165                 format.push(i.size);
32166             }, this);
32167             
32168             Roo.each(standard, function(s){
32169                 
32170                 if(String(s) != String(format)){
32171                     return;
32172                 }
32173                 
32174                 match = true;
32175                 return false;
32176                 
32177             }, this);
32178             
32179             if(!match && length == 1){
32180                 return;
32181             }
32182             
32183             if(!match){
32184                 filterPattern(box, length - 1);
32185                 return;
32186             }
32187                 
32188             queue.push(pattern);
32189
32190             box = box.slice(length, box.length);
32191
32192             filterPattern(box, 4);
32193
32194             return;
32195             
32196         }
32197         
32198         Roo.each(boxes, function(box, k){
32199             
32200             if(!box.length){
32201                 return;
32202             }
32203             
32204             if(box.length == 1){
32205                 queue.push(box);
32206                 return;
32207             }
32208             
32209             filterPattern(box, 4);
32210             
32211         }, this);
32212         
32213         
32214         var prune = [];
32215         
32216         var pos = this.el.getBox(true);
32217         
32218         var minX = pos.x;
32219         
32220         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32221         
32222         var hit_end = false;
32223         
32224         Roo.each(queue, function(box){
32225             
32226             if(hit_end){
32227                 
32228                 Roo.each(box, function(b){
32229                 
32230                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
32231                     b.el.hide();
32232
32233                 }, this);
32234
32235                 return;
32236             }
32237             
32238             var mx = 0;
32239             
32240             Roo.each(box, function(b){
32241                 
32242                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32243                 b.el.show();
32244
32245                 mx = Math.max(mx, b.x);
32246                 
32247             }, this);
32248             
32249             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
32250             
32251             if(maxX < minX){
32252                 
32253                 Roo.each(box, function(b){
32254                 
32255                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
32256                     b.el.hide();
32257                     
32258                 }, this);
32259                 
32260                 hit_end = true;
32261                 
32262                 return;
32263             }
32264             
32265             prune.push(box);
32266             
32267         }, this);
32268         
32269         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
32270     },
32271     
32272     /** Sets position of item in DOM
32273     * @param {Element} item
32274     * @param {Number} x - horizontal position
32275     * @param {Number} y - vertical position
32276     * @param {Boolean} isInstant - disables transitions
32277     */
32278     _processVerticalLayoutQueue : function( queue, isInstant )
32279     {
32280         var pos = this.el.getBox(true);
32281         var x = pos.x;
32282         var y = pos.y;
32283         var maxY = [];
32284         
32285         for (var i = 0; i < this.cols; i++){
32286             maxY[i] = pos.y;
32287         }
32288         
32289         Roo.each(queue, function(box, k){
32290             
32291             var col = k % this.cols;
32292             
32293             Roo.each(box, function(b,kk){
32294                 
32295                 b.el.position('absolute');
32296                 
32297                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32298                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32299                 
32300                 if(b.size == 'md-left' || b.size == 'md-right'){
32301                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32302                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32303                 }
32304                 
32305                 b.el.setWidth(width);
32306                 b.el.setHeight(height);
32307                 // iframe?
32308                 b.el.select('iframe',true).setSize(width,height);
32309                 
32310             }, this);
32311             
32312             for (var i = 0; i < this.cols; i++){
32313                 
32314                 if(maxY[i] < maxY[col]){
32315                     col = i;
32316                     continue;
32317                 }
32318                 
32319                 col = Math.min(col, i);
32320                 
32321             }
32322             
32323             x = pos.x + col * (this.colWidth + this.padWidth);
32324             
32325             y = maxY[col];
32326             
32327             var positions = [];
32328             
32329             switch (box.length){
32330                 case 1 :
32331                     positions = this.getVerticalOneBoxColPositions(x, y, box);
32332                     break;
32333                 case 2 :
32334                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
32335                     break;
32336                 case 3 :
32337                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
32338                     break;
32339                 case 4 :
32340                     positions = this.getVerticalFourBoxColPositions(x, y, box);
32341                     break;
32342                 default :
32343                     break;
32344             }
32345             
32346             Roo.each(box, function(b,kk){
32347                 
32348                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32349                 
32350                 var sz = b.el.getSize();
32351                 
32352                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
32353                 
32354             }, this);
32355             
32356         }, this);
32357         
32358         var mY = 0;
32359         
32360         for (var i = 0; i < this.cols; i++){
32361             mY = Math.max(mY, maxY[i]);
32362         }
32363         
32364         this.el.setHeight(mY - pos.y);
32365         
32366     },
32367     
32368 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
32369 //    {
32370 //        var pos = this.el.getBox(true);
32371 //        var x = pos.x;
32372 //        var y = pos.y;
32373 //        var maxX = pos.right;
32374 //        
32375 //        var maxHeight = 0;
32376 //        
32377 //        Roo.each(items, function(item, k){
32378 //            
32379 //            var c = k % 2;
32380 //            
32381 //            item.el.position('absolute');
32382 //                
32383 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
32384 //
32385 //            item.el.setWidth(width);
32386 //
32387 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
32388 //
32389 //            item.el.setHeight(height);
32390 //            
32391 //            if(c == 0){
32392 //                item.el.setXY([x, y], isInstant ? false : true);
32393 //            } else {
32394 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
32395 //            }
32396 //            
32397 //            y = y + height + this.alternativePadWidth;
32398 //            
32399 //            maxHeight = maxHeight + height + this.alternativePadWidth;
32400 //            
32401 //        }, this);
32402 //        
32403 //        this.el.setHeight(maxHeight);
32404 //        
32405 //    },
32406     
32407     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
32408     {
32409         var pos = this.el.getBox(true);
32410         
32411         var minX = pos.x;
32412         var minY = pos.y;
32413         
32414         var maxX = pos.right;
32415         
32416         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
32417         
32418         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32419         
32420         Roo.each(queue, function(box, k){
32421             
32422             Roo.each(box, function(b, kk){
32423                 
32424                 b.el.position('absolute');
32425                 
32426                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32427                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32428                 
32429                 if(b.size == 'md-left' || b.size == 'md-right'){
32430                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32431                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32432                 }
32433                 
32434                 b.el.setWidth(width);
32435                 b.el.setHeight(height);
32436                 
32437             }, this);
32438             
32439             if(!box.length){
32440                 return;
32441             }
32442             
32443             var positions = [];
32444             
32445             switch (box.length){
32446                 case 1 :
32447                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32448                     break;
32449                 case 2 :
32450                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32451                     break;
32452                 case 3 :
32453                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
32454                     break;
32455                 case 4 :
32456                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
32457                     break;
32458                 default :
32459                     break;
32460             }
32461             
32462             Roo.each(box, function(b,kk){
32463                 
32464                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32465                 
32466                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
32467                 
32468             }, this);
32469             
32470         }, this);
32471         
32472     },
32473     
32474     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
32475     {
32476         Roo.each(eItems, function(b,k){
32477             
32478             b.size = (k == 0) ? 'sm' : 'xs';
32479             b.x = (k == 0) ? 2 : 1;
32480             b.y = (k == 0) ? 2 : 1;
32481             
32482             b.el.position('absolute');
32483             
32484             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32485                 
32486             b.el.setWidth(width);
32487             
32488             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32489             
32490             b.el.setHeight(height);
32491             
32492         }, this);
32493
32494         var positions = [];
32495         
32496         positions.push({
32497             x : maxX - this.unitWidth * 2 - this.gutter,
32498             y : minY
32499         });
32500         
32501         positions.push({
32502             x : maxX - this.unitWidth,
32503             y : minY + (this.unitWidth + this.gutter) * 2
32504         });
32505         
32506         positions.push({
32507             x : maxX - this.unitWidth * 3 - this.gutter * 2,
32508             y : minY
32509         });
32510         
32511         Roo.each(eItems, function(b,k){
32512             
32513             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32514
32515         }, this);
32516         
32517     },
32518     
32519     getVerticalOneBoxColPositions : function(x, y, box)
32520     {
32521         var pos = [];
32522         
32523         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32524         
32525         if(box[0].size == 'md-left'){
32526             rand = 0;
32527         }
32528         
32529         if(box[0].size == 'md-right'){
32530             rand = 1;
32531         }
32532         
32533         pos.push({
32534             x : x + (this.unitWidth + this.gutter) * rand,
32535             y : y
32536         });
32537         
32538         return pos;
32539     },
32540     
32541     getVerticalTwoBoxColPositions : function(x, y, box)
32542     {
32543         var pos = [];
32544         
32545         if(box[0].size == 'xs'){
32546             
32547             pos.push({
32548                 x : x,
32549                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32550             });
32551
32552             pos.push({
32553                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32554                 y : y
32555             });
32556             
32557             return pos;
32558             
32559         }
32560         
32561         pos.push({
32562             x : x,
32563             y : y
32564         });
32565
32566         pos.push({
32567             x : x + (this.unitWidth + this.gutter) * 2,
32568             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32569         });
32570         
32571         return pos;
32572         
32573     },
32574     
32575     getVerticalThreeBoxColPositions : function(x, y, box)
32576     {
32577         var pos = [];
32578         
32579         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32580             
32581             pos.push({
32582                 x : x,
32583                 y : y
32584             });
32585
32586             pos.push({
32587                 x : x + (this.unitWidth + this.gutter) * 1,
32588                 y : y
32589             });
32590             
32591             pos.push({
32592                 x : x + (this.unitWidth + this.gutter) * 2,
32593                 y : y
32594             });
32595             
32596             return pos;
32597             
32598         }
32599         
32600         if(box[0].size == 'xs' && box[1].size == 'xs'){
32601             
32602             pos.push({
32603                 x : x,
32604                 y : y
32605             });
32606
32607             pos.push({
32608                 x : x,
32609                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32610             });
32611             
32612             pos.push({
32613                 x : x + (this.unitWidth + this.gutter) * 1,
32614                 y : y
32615             });
32616             
32617             return pos;
32618             
32619         }
32620         
32621         pos.push({
32622             x : x,
32623             y : y
32624         });
32625
32626         pos.push({
32627             x : x + (this.unitWidth + this.gutter) * 2,
32628             y : y
32629         });
32630
32631         pos.push({
32632             x : x + (this.unitWidth + this.gutter) * 2,
32633             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32634         });
32635             
32636         return pos;
32637         
32638     },
32639     
32640     getVerticalFourBoxColPositions : function(x, y, box)
32641     {
32642         var pos = [];
32643         
32644         if(box[0].size == 'xs'){
32645             
32646             pos.push({
32647                 x : x,
32648                 y : y
32649             });
32650
32651             pos.push({
32652                 x : x,
32653                 y : y + (this.unitHeight + this.gutter) * 1
32654             });
32655             
32656             pos.push({
32657                 x : x,
32658                 y : y + (this.unitHeight + this.gutter) * 2
32659             });
32660             
32661             pos.push({
32662                 x : x + (this.unitWidth + this.gutter) * 1,
32663                 y : y
32664             });
32665             
32666             return pos;
32667             
32668         }
32669         
32670         pos.push({
32671             x : x,
32672             y : y
32673         });
32674
32675         pos.push({
32676             x : x + (this.unitWidth + this.gutter) * 2,
32677             y : y
32678         });
32679
32680         pos.push({
32681             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32682             y : y + (this.unitHeight + this.gutter) * 1
32683         });
32684
32685         pos.push({
32686             x : x + (this.unitWidth + this.gutter) * 2,
32687             y : y + (this.unitWidth + this.gutter) * 2
32688         });
32689
32690         return pos;
32691         
32692     },
32693     
32694     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32695     {
32696         var pos = [];
32697         
32698         if(box[0].size == 'md-left'){
32699             pos.push({
32700                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32701                 y : minY
32702             });
32703             
32704             return pos;
32705         }
32706         
32707         if(box[0].size == 'md-right'){
32708             pos.push({
32709                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32710                 y : minY + (this.unitWidth + this.gutter) * 1
32711             });
32712             
32713             return pos;
32714         }
32715         
32716         var rand = Math.floor(Math.random() * (4 - box[0].y));
32717         
32718         pos.push({
32719             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32720             y : minY + (this.unitWidth + this.gutter) * rand
32721         });
32722         
32723         return pos;
32724         
32725     },
32726     
32727     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32728     {
32729         var pos = [];
32730         
32731         if(box[0].size == 'xs'){
32732             
32733             pos.push({
32734                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32735                 y : minY
32736             });
32737
32738             pos.push({
32739                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32740                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32741             });
32742             
32743             return pos;
32744             
32745         }
32746         
32747         pos.push({
32748             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32749             y : minY
32750         });
32751
32752         pos.push({
32753             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32754             y : minY + (this.unitWidth + this.gutter) * 2
32755         });
32756         
32757         return pos;
32758         
32759     },
32760     
32761     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32762     {
32763         var pos = [];
32764         
32765         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32766             
32767             pos.push({
32768                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32769                 y : minY
32770             });
32771
32772             pos.push({
32773                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32774                 y : minY + (this.unitWidth + this.gutter) * 1
32775             });
32776             
32777             pos.push({
32778                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32779                 y : minY + (this.unitWidth + this.gutter) * 2
32780             });
32781             
32782             return pos;
32783             
32784         }
32785         
32786         if(box[0].size == 'xs' && box[1].size == 'xs'){
32787             
32788             pos.push({
32789                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32790                 y : minY
32791             });
32792
32793             pos.push({
32794                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32795                 y : minY
32796             });
32797             
32798             pos.push({
32799                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32800                 y : minY + (this.unitWidth + this.gutter) * 1
32801             });
32802             
32803             return pos;
32804             
32805         }
32806         
32807         pos.push({
32808             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32809             y : minY
32810         });
32811
32812         pos.push({
32813             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32814             y : minY + (this.unitWidth + this.gutter) * 2
32815         });
32816
32817         pos.push({
32818             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32819             y : minY + (this.unitWidth + this.gutter) * 2
32820         });
32821             
32822         return pos;
32823         
32824     },
32825     
32826     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32827     {
32828         var pos = [];
32829         
32830         if(box[0].size == 'xs'){
32831             
32832             pos.push({
32833                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32834                 y : minY
32835             });
32836
32837             pos.push({
32838                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32839                 y : minY
32840             });
32841             
32842             pos.push({
32843                 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),
32844                 y : minY
32845             });
32846             
32847             pos.push({
32848                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32849                 y : minY + (this.unitWidth + this.gutter) * 1
32850             });
32851             
32852             return pos;
32853             
32854         }
32855         
32856         pos.push({
32857             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32858             y : minY
32859         });
32860         
32861         pos.push({
32862             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32863             y : minY + (this.unitWidth + this.gutter) * 2
32864         });
32865         
32866         pos.push({
32867             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32868             y : minY + (this.unitWidth + this.gutter) * 2
32869         });
32870         
32871         pos.push({
32872             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),
32873             y : minY + (this.unitWidth + this.gutter) * 2
32874         });
32875
32876         return pos;
32877         
32878     },
32879     
32880     /**
32881     * remove a Masonry Brick
32882     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32883     */
32884     removeBrick : function(brick_id)
32885     {
32886         if (!brick_id) {
32887             return;
32888         }
32889         
32890         for (var i = 0; i<this.bricks.length; i++) {
32891             if (this.bricks[i].id == brick_id) {
32892                 this.bricks.splice(i,1);
32893                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32894                 this.initial();
32895             }
32896         }
32897     },
32898     
32899     /**
32900     * adds a Masonry Brick
32901     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32902     */
32903     addBrick : function(cfg)
32904     {
32905         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32906         //this.register(cn);
32907         cn.parentId = this.id;
32908         cn.render(this.el);
32909         return cn;
32910     },
32911     
32912     /**
32913     * register a Masonry Brick
32914     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32915     */
32916     
32917     register : function(brick)
32918     {
32919         this.bricks.push(brick);
32920         brick.masonryId = this.id;
32921     },
32922     
32923     /**
32924     * clear all the Masonry Brick
32925     */
32926     clearAll : function()
32927     {
32928         this.bricks = [];
32929         //this.getChildContainer().dom.innerHTML = "";
32930         this.el.dom.innerHTML = '';
32931     },
32932     
32933     getSelected : function()
32934     {
32935         if (!this.selectedBrick) {
32936             return false;
32937         }
32938         
32939         return this.selectedBrick;
32940     }
32941 });
32942
32943 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32944     
32945     groups: {},
32946      /**
32947     * register a Masonry Layout
32948     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32949     */
32950     
32951     register : function(layout)
32952     {
32953         this.groups[layout.id] = layout;
32954     },
32955     /**
32956     * fetch a  Masonry Layout based on the masonry layout ID
32957     * @param {string} the masonry layout to add
32958     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32959     */
32960     
32961     get: function(layout_id) {
32962         if (typeof(this.groups[layout_id]) == 'undefined') {
32963             return false;
32964         }
32965         return this.groups[layout_id] ;
32966     }
32967     
32968     
32969     
32970 });
32971
32972  
32973
32974  /**
32975  *
32976  * This is based on 
32977  * http://masonry.desandro.com
32978  *
32979  * The idea is to render all the bricks based on vertical width...
32980  *
32981  * The original code extends 'outlayer' - we might need to use that....
32982  * 
32983  */
32984
32985
32986 /**
32987  * @class Roo.bootstrap.LayoutMasonryAuto
32988  * @extends Roo.bootstrap.Component
32989  * Bootstrap Layout Masonry class
32990  * 
32991  * @constructor
32992  * Create a new Element
32993  * @param {Object} config The config object
32994  */
32995
32996 Roo.bootstrap.LayoutMasonryAuto = function(config){
32997     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32998 };
32999
33000 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
33001     
33002       /**
33003      * @cfg {Boolean} isFitWidth  - resize the width..
33004      */   
33005     isFitWidth : false,  // options..
33006     /**
33007      * @cfg {Boolean} isOriginLeft = left align?
33008      */   
33009     isOriginLeft : true,
33010     /**
33011      * @cfg {Boolean} isOriginTop = top align?
33012      */   
33013     isOriginTop : false,
33014     /**
33015      * @cfg {Boolean} isLayoutInstant = no animation?
33016      */   
33017     isLayoutInstant : false, // needed?
33018     /**
33019      * @cfg {Boolean} isResizingContainer = not sure if this is used..
33020      */   
33021     isResizingContainer : true,
33022     /**
33023      * @cfg {Number} columnWidth  width of the columns 
33024      */   
33025     
33026     columnWidth : 0,
33027     
33028     /**
33029      * @cfg {Number} maxCols maximum number of columns
33030      */   
33031     
33032     maxCols: 0,
33033     /**
33034      * @cfg {Number} padHeight padding below box..
33035      */   
33036     
33037     padHeight : 10, 
33038     
33039     /**
33040      * @cfg {Boolean} isAutoInitial defalut true
33041      */   
33042     
33043     isAutoInitial : true, 
33044     
33045     // private?
33046     gutter : 0,
33047     
33048     containerWidth: 0,
33049     initialColumnWidth : 0,
33050     currentSize : null,
33051     
33052     colYs : null, // array.
33053     maxY : 0,
33054     padWidth: 10,
33055     
33056     
33057     tag: 'div',
33058     cls: '',
33059     bricks: null, //CompositeElement
33060     cols : 0, // array?
33061     // element : null, // wrapped now this.el
33062     _isLayoutInited : null, 
33063     
33064     
33065     getAutoCreate : function(){
33066         
33067         var cfg = {
33068             tag: this.tag,
33069             cls: 'blog-masonary-wrapper ' + this.cls,
33070             cn : {
33071                 cls : 'mas-boxes masonary'
33072             }
33073         };
33074         
33075         return cfg;
33076     },
33077     
33078     getChildContainer: function( )
33079     {
33080         if (this.boxesEl) {
33081             return this.boxesEl;
33082         }
33083         
33084         this.boxesEl = this.el.select('.mas-boxes').first();
33085         
33086         return this.boxesEl;
33087     },
33088     
33089     
33090     initEvents : function()
33091     {
33092         var _this = this;
33093         
33094         if(this.isAutoInitial){
33095             Roo.log('hook children rendered');
33096             this.on('childrenrendered', function() {
33097                 Roo.log('children rendered');
33098                 _this.initial();
33099             } ,this);
33100         }
33101         
33102     },
33103     
33104     initial : function()
33105     {
33106         this.reloadItems();
33107
33108         this.currentSize = this.el.getBox(true);
33109
33110         /// was window resize... - let's see if this works..
33111         Roo.EventManager.onWindowResize(this.resize, this); 
33112
33113         if(!this.isAutoInitial){
33114             this.layout();
33115             return;
33116         }
33117         
33118         this.layout.defer(500,this);
33119     },
33120     
33121     reloadItems: function()
33122     {
33123         this.bricks = this.el.select('.masonry-brick', true);
33124         
33125         this.bricks.each(function(b) {
33126             //Roo.log(b.getSize());
33127             if (!b.attr('originalwidth')) {
33128                 b.attr('originalwidth',  b.getSize().width);
33129             }
33130             
33131         });
33132         
33133         Roo.log(this.bricks.elements.length);
33134     },
33135     
33136     resize : function()
33137     {
33138         Roo.log('resize');
33139         var cs = this.el.getBox(true);
33140         
33141         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
33142             Roo.log("no change in with or X");
33143             return;
33144         }
33145         this.currentSize = cs;
33146         this.layout();
33147     },
33148     
33149     layout : function()
33150     {
33151          Roo.log('layout');
33152         this._resetLayout();
33153         //this._manageStamps();
33154       
33155         // don't animate first layout
33156         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33157         this.layoutItems( isInstant );
33158       
33159         // flag for initalized
33160         this._isLayoutInited = true;
33161     },
33162     
33163     layoutItems : function( isInstant )
33164     {
33165         //var items = this._getItemsForLayout( this.items );
33166         // original code supports filtering layout items.. we just ignore it..
33167         
33168         this._layoutItems( this.bricks , isInstant );
33169       
33170         this._postLayout();
33171     },
33172     _layoutItems : function ( items , isInstant)
33173     {
33174        //this.fireEvent( 'layout', this, items );
33175     
33176
33177         if ( !items || !items.elements.length ) {
33178           // no items, emit event with empty array
33179             return;
33180         }
33181
33182         var queue = [];
33183         items.each(function(item) {
33184             Roo.log("layout item");
33185             Roo.log(item);
33186             // get x/y object from method
33187             var position = this._getItemLayoutPosition( item );
33188             // enqueue
33189             position.item = item;
33190             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
33191             queue.push( position );
33192         }, this);
33193       
33194         this._processLayoutQueue( queue );
33195     },
33196     /** Sets position of item in DOM
33197     * @param {Element} item
33198     * @param {Number} x - horizontal position
33199     * @param {Number} y - vertical position
33200     * @param {Boolean} isInstant - disables transitions
33201     */
33202     _processLayoutQueue : function( queue )
33203     {
33204         for ( var i=0, len = queue.length; i < len; i++ ) {
33205             var obj = queue[i];
33206             obj.item.position('absolute');
33207             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
33208         }
33209     },
33210       
33211     
33212     /**
33213     * Any logic you want to do after each layout,
33214     * i.e. size the container
33215     */
33216     _postLayout : function()
33217     {
33218         this.resizeContainer();
33219     },
33220     
33221     resizeContainer : function()
33222     {
33223         if ( !this.isResizingContainer ) {
33224             return;
33225         }
33226         var size = this._getContainerSize();
33227         if ( size ) {
33228             this.el.setSize(size.width,size.height);
33229             this.boxesEl.setSize(size.width,size.height);
33230         }
33231     },
33232     
33233     
33234     
33235     _resetLayout : function()
33236     {
33237         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
33238         this.colWidth = this.el.getWidth();
33239         //this.gutter = this.el.getWidth(); 
33240         
33241         this.measureColumns();
33242
33243         // reset column Y
33244         var i = this.cols;
33245         this.colYs = [];
33246         while (i--) {
33247             this.colYs.push( 0 );
33248         }
33249     
33250         this.maxY = 0;
33251     },
33252
33253     measureColumns : function()
33254     {
33255         this.getContainerWidth();
33256       // if columnWidth is 0, default to outerWidth of first item
33257         if ( !this.columnWidth ) {
33258             var firstItem = this.bricks.first();
33259             Roo.log(firstItem);
33260             this.columnWidth  = this.containerWidth;
33261             if (firstItem && firstItem.attr('originalwidth') ) {
33262                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
33263             }
33264             // columnWidth fall back to item of first element
33265             Roo.log("set column width?");
33266                         this.initialColumnWidth = this.columnWidth  ;
33267
33268             // if first elem has no width, default to size of container
33269             
33270         }
33271         
33272         
33273         if (this.initialColumnWidth) {
33274             this.columnWidth = this.initialColumnWidth;
33275         }
33276         
33277         
33278             
33279         // column width is fixed at the top - however if container width get's smaller we should
33280         // reduce it...
33281         
33282         // this bit calcs how man columns..
33283             
33284         var columnWidth = this.columnWidth += this.gutter;
33285       
33286         // calculate columns
33287         var containerWidth = this.containerWidth + this.gutter;
33288         
33289         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
33290         // fix rounding errors, typically with gutters
33291         var excess = columnWidth - containerWidth % columnWidth;
33292         
33293         
33294         // if overshoot is less than a pixel, round up, otherwise floor it
33295         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
33296         cols = Math[ mathMethod ]( cols );
33297         this.cols = Math.max( cols, 1 );
33298         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33299         
33300          // padding positioning..
33301         var totalColWidth = this.cols * this.columnWidth;
33302         var padavail = this.containerWidth - totalColWidth;
33303         // so for 2 columns - we need 3 'pads'
33304         
33305         var padNeeded = (1+this.cols) * this.padWidth;
33306         
33307         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
33308         
33309         this.columnWidth += padExtra
33310         //this.padWidth = Math.floor(padavail /  ( this.cols));
33311         
33312         // adjust colum width so that padding is fixed??
33313         
33314         // we have 3 columns ... total = width * 3
33315         // we have X left over... that should be used by 
33316         
33317         //if (this.expandC) {
33318             
33319         //}
33320         
33321         
33322         
33323     },
33324     
33325     getContainerWidth : function()
33326     {
33327        /* // container is parent if fit width
33328         var container = this.isFitWidth ? this.element.parentNode : this.element;
33329         // check that this.size and size are there
33330         // IE8 triggers resize on body size change, so they might not be
33331         
33332         var size = getSize( container );  //FIXME
33333         this.containerWidth = size && size.innerWidth; //FIXME
33334         */
33335          
33336         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33337         
33338     },
33339     
33340     _getItemLayoutPosition : function( item )  // what is item?
33341     {
33342         // we resize the item to our columnWidth..
33343       
33344         item.setWidth(this.columnWidth);
33345         item.autoBoxAdjust  = false;
33346         
33347         var sz = item.getSize();
33348  
33349         // how many columns does this brick span
33350         var remainder = this.containerWidth % this.columnWidth;
33351         
33352         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
33353         // round if off by 1 pixel, otherwise use ceil
33354         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
33355         colSpan = Math.min( colSpan, this.cols );
33356         
33357         // normally this should be '1' as we dont' currently allow multi width columns..
33358         
33359         var colGroup = this._getColGroup( colSpan );
33360         // get the minimum Y value from the columns
33361         var minimumY = Math.min.apply( Math, colGroup );
33362         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
33363         
33364         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
33365          
33366         // position the brick
33367         var position = {
33368             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
33369             y: this.currentSize.y + minimumY + this.padHeight
33370         };
33371         
33372         Roo.log(position);
33373         // apply setHeight to necessary columns
33374         var setHeight = minimumY + sz.height + this.padHeight;
33375         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
33376         
33377         var setSpan = this.cols + 1 - colGroup.length;
33378         for ( var i = 0; i < setSpan; i++ ) {
33379           this.colYs[ shortColIndex + i ] = setHeight ;
33380         }
33381       
33382         return position;
33383     },
33384     
33385     /**
33386      * @param {Number} colSpan - number of columns the element spans
33387      * @returns {Array} colGroup
33388      */
33389     _getColGroup : function( colSpan )
33390     {
33391         if ( colSpan < 2 ) {
33392           // if brick spans only one column, use all the column Ys
33393           return this.colYs;
33394         }
33395       
33396         var colGroup = [];
33397         // how many different places could this brick fit horizontally
33398         var groupCount = this.cols + 1 - colSpan;
33399         // for each group potential horizontal position
33400         for ( var i = 0; i < groupCount; i++ ) {
33401           // make an array of colY values for that one group
33402           var groupColYs = this.colYs.slice( i, i + colSpan );
33403           // and get the max value of the array
33404           colGroup[i] = Math.max.apply( Math, groupColYs );
33405         }
33406         return colGroup;
33407     },
33408     /*
33409     _manageStamp : function( stamp )
33410     {
33411         var stampSize =  stamp.getSize();
33412         var offset = stamp.getBox();
33413         // get the columns that this stamp affects
33414         var firstX = this.isOriginLeft ? offset.x : offset.right;
33415         var lastX = firstX + stampSize.width;
33416         var firstCol = Math.floor( firstX / this.columnWidth );
33417         firstCol = Math.max( 0, firstCol );
33418         
33419         var lastCol = Math.floor( lastX / this.columnWidth );
33420         // lastCol should not go over if multiple of columnWidth #425
33421         lastCol -= lastX % this.columnWidth ? 0 : 1;
33422         lastCol = Math.min( this.cols - 1, lastCol );
33423         
33424         // set colYs to bottom of the stamp
33425         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
33426             stampSize.height;
33427             
33428         for ( var i = firstCol; i <= lastCol; i++ ) {
33429           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33430         }
33431     },
33432     */
33433     
33434     _getContainerSize : function()
33435     {
33436         this.maxY = Math.max.apply( Math, this.colYs );
33437         var size = {
33438             height: this.maxY
33439         };
33440       
33441         if ( this.isFitWidth ) {
33442             size.width = this._getContainerFitWidth();
33443         }
33444       
33445         return size;
33446     },
33447     
33448     _getContainerFitWidth : function()
33449     {
33450         var unusedCols = 0;
33451         // count unused columns
33452         var i = this.cols;
33453         while ( --i ) {
33454           if ( this.colYs[i] !== 0 ) {
33455             break;
33456           }
33457           unusedCols++;
33458         }
33459         // fit container to columns that have been used
33460         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
33461     },
33462     
33463     needsResizeLayout : function()
33464     {
33465         var previousWidth = this.containerWidth;
33466         this.getContainerWidth();
33467         return previousWidth !== this.containerWidth;
33468     }
33469  
33470 });
33471
33472  
33473
33474  /*
33475  * - LGPL
33476  *
33477  * element
33478  * 
33479  */
33480
33481 /**
33482  * @class Roo.bootstrap.MasonryBrick
33483  * @extends Roo.bootstrap.Component
33484  * Bootstrap MasonryBrick class
33485  * 
33486  * @constructor
33487  * Create a new MasonryBrick
33488  * @param {Object} config The config object
33489  */
33490
33491 Roo.bootstrap.MasonryBrick = function(config){
33492     
33493     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
33494     
33495     Roo.bootstrap.MasonryBrick.register(this);
33496     
33497     this.addEvents({
33498         // raw events
33499         /**
33500          * @event click
33501          * When a MasonryBrick is clcik
33502          * @param {Roo.bootstrap.MasonryBrick} this
33503          * @param {Roo.EventObject} e
33504          */
33505         "click" : true
33506     });
33507 };
33508
33509 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
33510     
33511     /**
33512      * @cfg {String} title
33513      */   
33514     title : '',
33515     /**
33516      * @cfg {String} html
33517      */   
33518     html : '',
33519     /**
33520      * @cfg {String} bgimage
33521      */   
33522     bgimage : '',
33523     /**
33524      * @cfg {String} videourl
33525      */   
33526     videourl : '',
33527     /**
33528      * @cfg {String} cls
33529      */   
33530     cls : '',
33531     /**
33532      * @cfg {String} href
33533      */   
33534     href : '',
33535     /**
33536      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33537      */   
33538     size : 'xs',
33539     
33540     /**
33541      * @cfg {String} placetitle (center|bottom)
33542      */   
33543     placetitle : '',
33544     
33545     /**
33546      * @cfg {Boolean} isFitContainer defalut true
33547      */   
33548     isFitContainer : true, 
33549     
33550     /**
33551      * @cfg {Boolean} preventDefault defalut false
33552      */   
33553     preventDefault : false, 
33554     
33555     /**
33556      * @cfg {Boolean} inverse defalut false
33557      */   
33558     maskInverse : false, 
33559     
33560     getAutoCreate : function()
33561     {
33562         if(!this.isFitContainer){
33563             return this.getSplitAutoCreate();
33564         }
33565         
33566         var cls = 'masonry-brick masonry-brick-full';
33567         
33568         if(this.href.length){
33569             cls += ' masonry-brick-link';
33570         }
33571         
33572         if(this.bgimage.length){
33573             cls += ' masonry-brick-image';
33574         }
33575         
33576         if(this.maskInverse){
33577             cls += ' mask-inverse';
33578         }
33579         
33580         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33581             cls += ' enable-mask';
33582         }
33583         
33584         if(this.size){
33585             cls += ' masonry-' + this.size + '-brick';
33586         }
33587         
33588         if(this.placetitle.length){
33589             
33590             switch (this.placetitle) {
33591                 case 'center' :
33592                     cls += ' masonry-center-title';
33593                     break;
33594                 case 'bottom' :
33595                     cls += ' masonry-bottom-title';
33596                     break;
33597                 default:
33598                     break;
33599             }
33600             
33601         } else {
33602             if(!this.html.length && !this.bgimage.length){
33603                 cls += ' masonry-center-title';
33604             }
33605
33606             if(!this.html.length && this.bgimage.length){
33607                 cls += ' masonry-bottom-title';
33608             }
33609         }
33610         
33611         if(this.cls){
33612             cls += ' ' + this.cls;
33613         }
33614         
33615         var cfg = {
33616             tag: (this.href.length) ? 'a' : 'div',
33617             cls: cls,
33618             cn: [
33619                 {
33620                     tag: 'div',
33621                     cls: 'masonry-brick-mask'
33622                 },
33623                 {
33624                     tag: 'div',
33625                     cls: 'masonry-brick-paragraph',
33626                     cn: []
33627                 }
33628             ]
33629         };
33630         
33631         if(this.href.length){
33632             cfg.href = this.href;
33633         }
33634         
33635         var cn = cfg.cn[1].cn;
33636         
33637         if(this.title.length){
33638             cn.push({
33639                 tag: 'h4',
33640                 cls: 'masonry-brick-title',
33641                 html: this.title
33642             });
33643         }
33644         
33645         if(this.html.length){
33646             cn.push({
33647                 tag: 'p',
33648                 cls: 'masonry-brick-text',
33649                 html: this.html
33650             });
33651         }
33652         
33653         if (!this.title.length && !this.html.length) {
33654             cfg.cn[1].cls += ' hide';
33655         }
33656         
33657         if(this.bgimage.length){
33658             cfg.cn.push({
33659                 tag: 'img',
33660                 cls: 'masonry-brick-image-view',
33661                 src: this.bgimage
33662             });
33663         }
33664         
33665         if(this.videourl.length){
33666             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33667             // youtube support only?
33668             cfg.cn.push({
33669                 tag: 'iframe',
33670                 cls: 'masonry-brick-image-view',
33671                 src: vurl,
33672                 frameborder : 0,
33673                 allowfullscreen : true
33674             });
33675         }
33676         
33677         return cfg;
33678         
33679     },
33680     
33681     getSplitAutoCreate : function()
33682     {
33683         var cls = 'masonry-brick masonry-brick-split';
33684         
33685         if(this.href.length){
33686             cls += ' masonry-brick-link';
33687         }
33688         
33689         if(this.bgimage.length){
33690             cls += ' masonry-brick-image';
33691         }
33692         
33693         if(this.size){
33694             cls += ' masonry-' + this.size + '-brick';
33695         }
33696         
33697         switch (this.placetitle) {
33698             case 'center' :
33699                 cls += ' masonry-center-title';
33700                 break;
33701             case 'bottom' :
33702                 cls += ' masonry-bottom-title';
33703                 break;
33704             default:
33705                 if(!this.bgimage.length){
33706                     cls += ' masonry-center-title';
33707                 }
33708
33709                 if(this.bgimage.length){
33710                     cls += ' masonry-bottom-title';
33711                 }
33712                 break;
33713         }
33714         
33715         if(this.cls){
33716             cls += ' ' + this.cls;
33717         }
33718         
33719         var cfg = {
33720             tag: (this.href.length) ? 'a' : 'div',
33721             cls: cls,
33722             cn: [
33723                 {
33724                     tag: 'div',
33725                     cls: 'masonry-brick-split-head',
33726                     cn: [
33727                         {
33728                             tag: 'div',
33729                             cls: 'masonry-brick-paragraph',
33730                             cn: []
33731                         }
33732                     ]
33733                 },
33734                 {
33735                     tag: 'div',
33736                     cls: 'masonry-brick-split-body',
33737                     cn: []
33738                 }
33739             ]
33740         };
33741         
33742         if(this.href.length){
33743             cfg.href = this.href;
33744         }
33745         
33746         if(this.title.length){
33747             cfg.cn[0].cn[0].cn.push({
33748                 tag: 'h4',
33749                 cls: 'masonry-brick-title',
33750                 html: this.title
33751             });
33752         }
33753         
33754         if(this.html.length){
33755             cfg.cn[1].cn.push({
33756                 tag: 'p',
33757                 cls: 'masonry-brick-text',
33758                 html: this.html
33759             });
33760         }
33761
33762         if(this.bgimage.length){
33763             cfg.cn[0].cn.push({
33764                 tag: 'img',
33765                 cls: 'masonry-brick-image-view',
33766                 src: this.bgimage
33767             });
33768         }
33769         
33770         if(this.videourl.length){
33771             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33772             // youtube support only?
33773             cfg.cn[0].cn.cn.push({
33774                 tag: 'iframe',
33775                 cls: 'masonry-brick-image-view',
33776                 src: vurl,
33777                 frameborder : 0,
33778                 allowfullscreen : true
33779             });
33780         }
33781         
33782         return cfg;
33783     },
33784     
33785     initEvents: function() 
33786     {
33787         switch (this.size) {
33788             case 'xs' :
33789                 this.x = 1;
33790                 this.y = 1;
33791                 break;
33792             case 'sm' :
33793                 this.x = 2;
33794                 this.y = 2;
33795                 break;
33796             case 'md' :
33797             case 'md-left' :
33798             case 'md-right' :
33799                 this.x = 3;
33800                 this.y = 3;
33801                 break;
33802             case 'tall' :
33803                 this.x = 2;
33804                 this.y = 3;
33805                 break;
33806             case 'wide' :
33807                 this.x = 3;
33808                 this.y = 2;
33809                 break;
33810             case 'wide-thin' :
33811                 this.x = 3;
33812                 this.y = 1;
33813                 break;
33814                         
33815             default :
33816                 break;
33817         }
33818         
33819         if(Roo.isTouch){
33820             this.el.on('touchstart', this.onTouchStart, this);
33821             this.el.on('touchmove', this.onTouchMove, this);
33822             this.el.on('touchend', this.onTouchEnd, this);
33823             this.el.on('contextmenu', this.onContextMenu, this);
33824         } else {
33825             this.el.on('mouseenter'  ,this.enter, this);
33826             this.el.on('mouseleave', this.leave, this);
33827             this.el.on('click', this.onClick, this);
33828         }
33829         
33830         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33831             this.parent().bricks.push(this);   
33832         }
33833         
33834     },
33835     
33836     onClick: function(e, el)
33837     {
33838         var time = this.endTimer - this.startTimer;
33839         // Roo.log(e.preventDefault());
33840         if(Roo.isTouch){
33841             if(time > 1000){
33842                 e.preventDefault();
33843                 return;
33844             }
33845         }
33846         
33847         if(!this.preventDefault){
33848             return;
33849         }
33850         
33851         e.preventDefault();
33852         
33853         if (this.activeClass != '') {
33854             this.selectBrick();
33855         }
33856         
33857         this.fireEvent('click', this, e);
33858     },
33859     
33860     enter: function(e, el)
33861     {
33862         e.preventDefault();
33863         
33864         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33865             return;
33866         }
33867         
33868         if(this.bgimage.length && this.html.length){
33869             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33870         }
33871     },
33872     
33873     leave: function(e, el)
33874     {
33875         e.preventDefault();
33876         
33877         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33878             return;
33879         }
33880         
33881         if(this.bgimage.length && this.html.length){
33882             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33883         }
33884     },
33885     
33886     onTouchStart: function(e, el)
33887     {
33888 //        e.preventDefault();
33889         
33890         this.touchmoved = false;
33891         
33892         if(!this.isFitContainer){
33893             return;
33894         }
33895         
33896         if(!this.bgimage.length || !this.html.length){
33897             return;
33898         }
33899         
33900         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33901         
33902         this.timer = new Date().getTime();
33903         
33904     },
33905     
33906     onTouchMove: function(e, el)
33907     {
33908         this.touchmoved = true;
33909     },
33910     
33911     onContextMenu : function(e,el)
33912     {
33913         e.preventDefault();
33914         e.stopPropagation();
33915         return false;
33916     },
33917     
33918     onTouchEnd: function(e, el)
33919     {
33920 //        e.preventDefault();
33921         
33922         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33923         
33924             this.leave(e,el);
33925             
33926             return;
33927         }
33928         
33929         if(!this.bgimage.length || !this.html.length){
33930             
33931             if(this.href.length){
33932                 window.location.href = this.href;
33933             }
33934             
33935             return;
33936         }
33937         
33938         if(!this.isFitContainer){
33939             return;
33940         }
33941         
33942         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33943         
33944         window.location.href = this.href;
33945     },
33946     
33947     //selection on single brick only
33948     selectBrick : function() {
33949         
33950         if (!this.parentId) {
33951             return;
33952         }
33953         
33954         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33955         var index = m.selectedBrick.indexOf(this.id);
33956         
33957         if ( index > -1) {
33958             m.selectedBrick.splice(index,1);
33959             this.el.removeClass(this.activeClass);
33960             return;
33961         }
33962         
33963         for(var i = 0; i < m.selectedBrick.length; i++) {
33964             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33965             b.el.removeClass(b.activeClass);
33966         }
33967         
33968         m.selectedBrick = [];
33969         
33970         m.selectedBrick.push(this.id);
33971         this.el.addClass(this.activeClass);
33972         return;
33973     },
33974     
33975     isSelected : function(){
33976         return this.el.hasClass(this.activeClass);
33977         
33978     }
33979 });
33980
33981 Roo.apply(Roo.bootstrap.MasonryBrick, {
33982     
33983     //groups: {},
33984     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33985      /**
33986     * register a Masonry Brick
33987     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33988     */
33989     
33990     register : function(brick)
33991     {
33992         //this.groups[brick.id] = brick;
33993         this.groups.add(brick.id, brick);
33994     },
33995     /**
33996     * fetch a  masonry brick based on the masonry brick ID
33997     * @param {string} the masonry brick to add
33998     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33999     */
34000     
34001     get: function(brick_id) 
34002     {
34003         // if (typeof(this.groups[brick_id]) == 'undefined') {
34004         //     return false;
34005         // }
34006         // return this.groups[brick_id] ;
34007         
34008         if(this.groups.key(brick_id)) {
34009             return this.groups.key(brick_id);
34010         }
34011         
34012         return false;
34013     }
34014     
34015     
34016     
34017 });
34018
34019  /*
34020  * - LGPL
34021  *
34022  * element
34023  * 
34024  */
34025
34026 /**
34027  * @class Roo.bootstrap.Brick
34028  * @extends Roo.bootstrap.Component
34029  * Bootstrap Brick class
34030  * 
34031  * @constructor
34032  * Create a new Brick
34033  * @param {Object} config The config object
34034  */
34035
34036 Roo.bootstrap.Brick = function(config){
34037     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
34038     
34039     this.addEvents({
34040         // raw events
34041         /**
34042          * @event click
34043          * When a Brick is click
34044          * @param {Roo.bootstrap.Brick} this
34045          * @param {Roo.EventObject} e
34046          */
34047         "click" : true
34048     });
34049 };
34050
34051 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
34052     
34053     /**
34054      * @cfg {String} title
34055      */   
34056     title : '',
34057     /**
34058      * @cfg {String} html
34059      */   
34060     html : '',
34061     /**
34062      * @cfg {String} bgimage
34063      */   
34064     bgimage : '',
34065     /**
34066      * @cfg {String} cls
34067      */   
34068     cls : '',
34069     /**
34070      * @cfg {String} href
34071      */   
34072     href : '',
34073     /**
34074      * @cfg {String} video
34075      */   
34076     video : '',
34077     /**
34078      * @cfg {Boolean} square
34079      */   
34080     square : true,
34081     
34082     getAutoCreate : function()
34083     {
34084         var cls = 'roo-brick';
34085         
34086         if(this.href.length){
34087             cls += ' roo-brick-link';
34088         }
34089         
34090         if(this.bgimage.length){
34091             cls += ' roo-brick-image';
34092         }
34093         
34094         if(!this.html.length && !this.bgimage.length){
34095             cls += ' roo-brick-center-title';
34096         }
34097         
34098         if(!this.html.length && this.bgimage.length){
34099             cls += ' roo-brick-bottom-title';
34100         }
34101         
34102         if(this.cls){
34103             cls += ' ' + this.cls;
34104         }
34105         
34106         var cfg = {
34107             tag: (this.href.length) ? 'a' : 'div',
34108             cls: cls,
34109             cn: [
34110                 {
34111                     tag: 'div',
34112                     cls: 'roo-brick-paragraph',
34113                     cn: []
34114                 }
34115             ]
34116         };
34117         
34118         if(this.href.length){
34119             cfg.href = this.href;
34120         }
34121         
34122         var cn = cfg.cn[0].cn;
34123         
34124         if(this.title.length){
34125             cn.push({
34126                 tag: 'h4',
34127                 cls: 'roo-brick-title',
34128                 html: this.title
34129             });
34130         }
34131         
34132         if(this.html.length){
34133             cn.push({
34134                 tag: 'p',
34135                 cls: 'roo-brick-text',
34136                 html: this.html
34137             });
34138         } else {
34139             cn.cls += ' hide';
34140         }
34141         
34142         if(this.bgimage.length){
34143             cfg.cn.push({
34144                 tag: 'img',
34145                 cls: 'roo-brick-image-view',
34146                 src: this.bgimage
34147             });
34148         }
34149         
34150         return cfg;
34151     },
34152     
34153     initEvents: function() 
34154     {
34155         if(this.title.length || this.html.length){
34156             this.el.on('mouseenter'  ,this.enter, this);
34157             this.el.on('mouseleave', this.leave, this);
34158         }
34159         
34160         Roo.EventManager.onWindowResize(this.resize, this); 
34161         
34162         if(this.bgimage.length){
34163             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
34164             this.imageEl.on('load', this.onImageLoad, this);
34165             return;
34166         }
34167         
34168         this.resize();
34169     },
34170     
34171     onImageLoad : function()
34172     {
34173         this.resize();
34174     },
34175     
34176     resize : function()
34177     {
34178         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
34179         
34180         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
34181         
34182         if(this.bgimage.length){
34183             var image = this.el.select('.roo-brick-image-view', true).first();
34184             
34185             image.setWidth(paragraph.getWidth());
34186             
34187             if(this.square){
34188                 image.setHeight(paragraph.getWidth());
34189             }
34190             
34191             this.el.setHeight(image.getHeight());
34192             paragraph.setHeight(image.getHeight());
34193             
34194         }
34195         
34196     },
34197     
34198     enter: function(e, el)
34199     {
34200         e.preventDefault();
34201         
34202         if(this.bgimage.length){
34203             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
34204             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
34205         }
34206     },
34207     
34208     leave: function(e, el)
34209     {
34210         e.preventDefault();
34211         
34212         if(this.bgimage.length){
34213             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
34214             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
34215         }
34216     }
34217     
34218 });
34219
34220  
34221
34222  /*
34223  * - LGPL
34224  *
34225  * Number field 
34226  */
34227
34228 /**
34229  * @class Roo.bootstrap.NumberField
34230  * @extends Roo.bootstrap.Input
34231  * Bootstrap NumberField class
34232  * 
34233  * 
34234  * 
34235  * 
34236  * @constructor
34237  * Create a new NumberField
34238  * @param {Object} config The config object
34239  */
34240
34241 Roo.bootstrap.NumberField = function(config){
34242     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
34243 };
34244
34245 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
34246     
34247     /**
34248      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
34249      */
34250     allowDecimals : true,
34251     /**
34252      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
34253      */
34254     decimalSeparator : ".",
34255     /**
34256      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
34257      */
34258     decimalPrecision : 2,
34259     /**
34260      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
34261      */
34262     allowNegative : true,
34263     
34264     /**
34265      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
34266      */
34267     allowZero: true,
34268     /**
34269      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
34270      */
34271     minValue : Number.NEGATIVE_INFINITY,
34272     /**
34273      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
34274      */
34275     maxValue : Number.MAX_VALUE,
34276     /**
34277      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
34278      */
34279     minText : "The minimum value for this field is {0}",
34280     /**
34281      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
34282      */
34283     maxText : "The maximum value for this field is {0}",
34284     /**
34285      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
34286      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
34287      */
34288     nanText : "{0} is not a valid number",
34289     /**
34290      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
34291      */
34292     thousandsDelimiter : false,
34293     /**
34294      * @cfg {String} valueAlign alignment of value
34295      */
34296     valueAlign : "left",
34297
34298     getAutoCreate : function()
34299     {
34300         var hiddenInput = {
34301             tag: 'input',
34302             type: 'hidden',
34303             id: Roo.id(),
34304             cls: 'hidden-number-input'
34305         };
34306         
34307         if (this.name) {
34308             hiddenInput.name = this.name;
34309         }
34310         
34311         this.name = '';
34312         
34313         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
34314         
34315         this.name = hiddenInput.name;
34316         
34317         if(cfg.cn.length > 0) {
34318             cfg.cn.push(hiddenInput);
34319         }
34320         
34321         return cfg;
34322     },
34323
34324     // private
34325     initEvents : function()
34326     {   
34327         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
34328         
34329         var allowed = "0123456789";
34330         
34331         if(this.allowDecimals){
34332             allowed += this.decimalSeparator;
34333         }
34334         
34335         if(this.allowNegative){
34336             allowed += "-";
34337         }
34338         
34339         if(this.thousandsDelimiter) {
34340             allowed += ",";
34341         }
34342         
34343         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
34344         
34345         var keyPress = function(e){
34346             
34347             var k = e.getKey();
34348             
34349             var c = e.getCharCode();
34350             
34351             if(
34352                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
34353                     allowed.indexOf(String.fromCharCode(c)) === -1
34354             ){
34355                 e.stopEvent();
34356                 return;
34357             }
34358             
34359             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
34360                 return;
34361             }
34362             
34363             if(allowed.indexOf(String.fromCharCode(c)) === -1){
34364                 e.stopEvent();
34365             }
34366         };
34367         
34368         this.el.on("keypress", keyPress, this);
34369     },
34370     
34371     validateValue : function(value)
34372     {
34373         
34374         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
34375             return false;
34376         }
34377         
34378         var num = this.parseValue(value);
34379         
34380         if(isNaN(num)){
34381             this.markInvalid(String.format(this.nanText, value));
34382             return false;
34383         }
34384         
34385         if(num < this.minValue){
34386             this.markInvalid(String.format(this.minText, this.minValue));
34387             return false;
34388         }
34389         
34390         if(num > this.maxValue){
34391             this.markInvalid(String.format(this.maxText, this.maxValue));
34392             return false;
34393         }
34394         
34395         return true;
34396     },
34397
34398     getValue : function()
34399     {
34400         var v = this.hiddenEl().getValue();
34401         
34402         return this.fixPrecision(this.parseValue(v));
34403     },
34404
34405     parseValue : function(value)
34406     {
34407         if(this.thousandsDelimiter) {
34408             value += "";
34409             r = new RegExp(",", "g");
34410             value = value.replace(r, "");
34411         }
34412         
34413         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
34414         return isNaN(value) ? '' : value;
34415     },
34416
34417     fixPrecision : function(value)
34418     {
34419         if(this.thousandsDelimiter) {
34420             value += "";
34421             r = new RegExp(",", "g");
34422             value = value.replace(r, "");
34423         }
34424         
34425         var nan = isNaN(value);
34426         
34427         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34428             return nan ? '' : value;
34429         }
34430         return parseFloat(value).toFixed(this.decimalPrecision);
34431     },
34432
34433     setValue : function(v)
34434     {
34435         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34436         
34437         this.value = v;
34438         
34439         if(this.rendered){
34440             
34441             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34442             
34443             this.inputEl().dom.value = (v == '') ? '' :
34444                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34445             
34446             if(!this.allowZero && v === '0') {
34447                 this.hiddenEl().dom.value = '';
34448                 this.inputEl().dom.value = '';
34449             }
34450             
34451             this.validate();
34452         }
34453     },
34454
34455     decimalPrecisionFcn : function(v)
34456     {
34457         return Math.floor(v);
34458     },
34459
34460     beforeBlur : function()
34461     {
34462         var v = this.parseValue(this.getRawValue());
34463         
34464         if(v || v === 0 || v === ''){
34465             this.setValue(v);
34466         }
34467     },
34468     
34469     hiddenEl : function()
34470     {
34471         return this.el.select('input.hidden-number-input',true).first();
34472     }
34473     
34474 });
34475
34476  
34477
34478 /*
34479 * Licence: LGPL
34480 */
34481
34482 /**
34483  * @class Roo.bootstrap.DocumentSlider
34484  * @extends Roo.bootstrap.Component
34485  * Bootstrap DocumentSlider class
34486  * 
34487  * @constructor
34488  * Create a new DocumentViewer
34489  * @param {Object} config The config object
34490  */
34491
34492 Roo.bootstrap.DocumentSlider = function(config){
34493     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
34494     
34495     this.files = [];
34496     
34497     this.addEvents({
34498         /**
34499          * @event initial
34500          * Fire after initEvent
34501          * @param {Roo.bootstrap.DocumentSlider} this
34502          */
34503         "initial" : true,
34504         /**
34505          * @event update
34506          * Fire after update
34507          * @param {Roo.bootstrap.DocumentSlider} this
34508          */
34509         "update" : true,
34510         /**
34511          * @event click
34512          * Fire after click
34513          * @param {Roo.bootstrap.DocumentSlider} this
34514          */
34515         "click" : true
34516     });
34517 };
34518
34519 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
34520     
34521     files : false,
34522     
34523     indicator : 0,
34524     
34525     getAutoCreate : function()
34526     {
34527         var cfg = {
34528             tag : 'div',
34529             cls : 'roo-document-slider',
34530             cn : [
34531                 {
34532                     tag : 'div',
34533                     cls : 'roo-document-slider-header',
34534                     cn : [
34535                         {
34536                             tag : 'div',
34537                             cls : 'roo-document-slider-header-title'
34538                         }
34539                     ]
34540                 },
34541                 {
34542                     tag : 'div',
34543                     cls : 'roo-document-slider-body',
34544                     cn : [
34545                         {
34546                             tag : 'div',
34547                             cls : 'roo-document-slider-prev',
34548                             cn : [
34549                                 {
34550                                     tag : 'i',
34551                                     cls : 'fa fa-chevron-left'
34552                                 }
34553                             ]
34554                         },
34555                         {
34556                             tag : 'div',
34557                             cls : 'roo-document-slider-thumb',
34558                             cn : [
34559                                 {
34560                                     tag : 'img',
34561                                     cls : 'roo-document-slider-image'
34562                                 }
34563                             ]
34564                         },
34565                         {
34566                             tag : 'div',
34567                             cls : 'roo-document-slider-next',
34568                             cn : [
34569                                 {
34570                                     tag : 'i',
34571                                     cls : 'fa fa-chevron-right'
34572                                 }
34573                             ]
34574                         }
34575                     ]
34576                 }
34577             ]
34578         };
34579         
34580         return cfg;
34581     },
34582     
34583     initEvents : function()
34584     {
34585         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34586         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34587         
34588         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34589         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34590         
34591         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34592         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34593         
34594         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34595         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34596         
34597         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34598         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34599         
34600         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34601         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34602         
34603         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34604         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34605         
34606         this.thumbEl.on('click', this.onClick, this);
34607         
34608         this.prevIndicator.on('click', this.prev, this);
34609         
34610         this.nextIndicator.on('click', this.next, this);
34611         
34612     },
34613     
34614     initial : function()
34615     {
34616         if(this.files.length){
34617             this.indicator = 1;
34618             this.update()
34619         }
34620         
34621         this.fireEvent('initial', this);
34622     },
34623     
34624     update : function()
34625     {
34626         this.imageEl.attr('src', this.files[this.indicator - 1]);
34627         
34628         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34629         
34630         this.prevIndicator.show();
34631         
34632         if(this.indicator == 1){
34633             this.prevIndicator.hide();
34634         }
34635         
34636         this.nextIndicator.show();
34637         
34638         if(this.indicator == this.files.length){
34639             this.nextIndicator.hide();
34640         }
34641         
34642         this.thumbEl.scrollTo('top');
34643         
34644         this.fireEvent('update', this);
34645     },
34646     
34647     onClick : function(e)
34648     {
34649         e.preventDefault();
34650         
34651         this.fireEvent('click', this);
34652     },
34653     
34654     prev : function(e)
34655     {
34656         e.preventDefault();
34657         
34658         this.indicator = Math.max(1, this.indicator - 1);
34659         
34660         this.update();
34661     },
34662     
34663     next : function(e)
34664     {
34665         e.preventDefault();
34666         
34667         this.indicator = Math.min(this.files.length, this.indicator + 1);
34668         
34669         this.update();
34670     }
34671 });
34672 /*
34673  * - LGPL
34674  *
34675  * RadioSet
34676  *
34677  *
34678  */
34679
34680 /**
34681  * @class Roo.bootstrap.RadioSet
34682  * @extends Roo.bootstrap.Input
34683  * Bootstrap RadioSet class
34684  * @cfg {String} indicatorpos (left|right) default left
34685  * @cfg {Boolean} inline (true|false) inline the element (default true)
34686  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34687  * @constructor
34688  * Create a new RadioSet
34689  * @param {Object} config The config object
34690  */
34691
34692 Roo.bootstrap.RadioSet = function(config){
34693     
34694     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34695     
34696     this.radioes = [];
34697     
34698     Roo.bootstrap.RadioSet.register(this);
34699     
34700     this.addEvents({
34701         /**
34702         * @event check
34703         * Fires when the element is checked or unchecked.
34704         * @param {Roo.bootstrap.RadioSet} this This radio
34705         * @param {Roo.bootstrap.Radio} item The checked item
34706         */
34707        check : true,
34708        /**
34709         * @event click
34710         * Fires when the element is click.
34711         * @param {Roo.bootstrap.RadioSet} this This radio set
34712         * @param {Roo.bootstrap.Radio} item The checked item
34713         * @param {Roo.EventObject} e The event object
34714         */
34715        click : true
34716     });
34717     
34718 };
34719
34720 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34721
34722     radioes : false,
34723     
34724     inline : true,
34725     
34726     weight : '',
34727     
34728     indicatorpos : 'left',
34729     
34730     getAutoCreate : function()
34731     {
34732         var label = {
34733             tag : 'label',
34734             cls : 'roo-radio-set-label',
34735             cn : [
34736                 {
34737                     tag : 'span',
34738                     html : this.fieldLabel
34739                 }
34740             ]
34741         };
34742         if (Roo.bootstrap.version == 3) {
34743             
34744             
34745             if(this.indicatorpos == 'left'){
34746                 label.cn.unshift({
34747                     tag : 'i',
34748                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34749                     tooltip : 'This field is required'
34750                 });
34751             } else {
34752                 label.cn.push({
34753                     tag : 'i',
34754                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34755                     tooltip : 'This field is required'
34756                 });
34757             }
34758         }
34759         var items = {
34760             tag : 'div',
34761             cls : 'roo-radio-set-items'
34762         };
34763         
34764         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34765         
34766         if (align === 'left' && this.fieldLabel.length) {
34767             
34768             items = {
34769                 cls : "roo-radio-set-right", 
34770                 cn: [
34771                     items
34772                 ]
34773             };
34774             
34775             if(this.labelWidth > 12){
34776                 label.style = "width: " + this.labelWidth + 'px';
34777             }
34778             
34779             if(this.labelWidth < 13 && this.labelmd == 0){
34780                 this.labelmd = this.labelWidth;
34781             }
34782             
34783             if(this.labellg > 0){
34784                 label.cls += ' col-lg-' + this.labellg;
34785                 items.cls += ' col-lg-' + (12 - this.labellg);
34786             }
34787             
34788             if(this.labelmd > 0){
34789                 label.cls += ' col-md-' + this.labelmd;
34790                 items.cls += ' col-md-' + (12 - this.labelmd);
34791             }
34792             
34793             if(this.labelsm > 0){
34794                 label.cls += ' col-sm-' + this.labelsm;
34795                 items.cls += ' col-sm-' + (12 - this.labelsm);
34796             }
34797             
34798             if(this.labelxs > 0){
34799                 label.cls += ' col-xs-' + this.labelxs;
34800                 items.cls += ' col-xs-' + (12 - this.labelxs);
34801             }
34802         }
34803         
34804         var cfg = {
34805             tag : 'div',
34806             cls : 'roo-radio-set',
34807             cn : [
34808                 {
34809                     tag : 'input',
34810                     cls : 'roo-radio-set-input',
34811                     type : 'hidden',
34812                     name : this.name,
34813                     value : this.value ? this.value :  ''
34814                 },
34815                 label,
34816                 items
34817             ]
34818         };
34819         
34820         if(this.weight.length){
34821             cfg.cls += ' roo-radio-' + this.weight;
34822         }
34823         
34824         if(this.inline) {
34825             cfg.cls += ' roo-radio-set-inline';
34826         }
34827         
34828         var settings=this;
34829         ['xs','sm','md','lg'].map(function(size){
34830             if (settings[size]) {
34831                 cfg.cls += ' col-' + size + '-' + settings[size];
34832             }
34833         });
34834         
34835         return cfg;
34836         
34837     },
34838
34839     initEvents : function()
34840     {
34841         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34842         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34843         
34844         if(!this.fieldLabel.length){
34845             this.labelEl.hide();
34846         }
34847         
34848         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34849         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34850         
34851         this.indicator = this.indicatorEl();
34852         
34853         if(this.indicator){
34854             this.indicator.addClass('invisible');
34855         }
34856         
34857         this.originalValue = this.getValue();
34858         
34859     },
34860     
34861     inputEl: function ()
34862     {
34863         return this.el.select('.roo-radio-set-input', true).first();
34864     },
34865     
34866     getChildContainer : function()
34867     {
34868         return this.itemsEl;
34869     },
34870     
34871     register : function(item)
34872     {
34873         this.radioes.push(item);
34874         
34875     },
34876     
34877     validate : function()
34878     {   
34879         if(this.getVisibilityEl().hasClass('hidden')){
34880             return true;
34881         }
34882         
34883         var valid = false;
34884         
34885         Roo.each(this.radioes, function(i){
34886             if(!i.checked){
34887                 return;
34888             }
34889             
34890             valid = true;
34891             return false;
34892         });
34893         
34894         if(this.allowBlank) {
34895             return true;
34896         }
34897         
34898         if(this.disabled || valid){
34899             this.markValid();
34900             return true;
34901         }
34902         
34903         this.markInvalid();
34904         return false;
34905         
34906     },
34907     
34908     markValid : function()
34909     {
34910         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34911             this.indicatorEl().removeClass('visible');
34912             this.indicatorEl().addClass('invisible');
34913         }
34914         
34915         
34916         if (Roo.bootstrap.version == 3) {
34917             this.el.removeClass([this.invalidClass, this.validClass]);
34918             this.el.addClass(this.validClass);
34919         } else {
34920             this.el.removeClass(['is-invalid','is-valid']);
34921             this.el.addClass(['is-valid']);
34922         }
34923         this.fireEvent('valid', this);
34924     },
34925     
34926     markInvalid : function(msg)
34927     {
34928         if(this.allowBlank || this.disabled){
34929             return;
34930         }
34931         
34932         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34933             this.indicatorEl().removeClass('invisible');
34934             this.indicatorEl().addClass('visible');
34935         }
34936         if (Roo.bootstrap.version == 3) {
34937             this.el.removeClass([this.invalidClass, this.validClass]);
34938             this.el.addClass(this.invalidClass);
34939         } else {
34940             this.el.removeClass(['is-invalid','is-valid']);
34941             this.el.addClass(['is-invalid']);
34942         }
34943         
34944         this.fireEvent('invalid', this, msg);
34945         
34946     },
34947     
34948     setValue : function(v, suppressEvent)
34949     {   
34950         if(this.value === v){
34951             return;
34952         }
34953         
34954         this.value = v;
34955         
34956         if(this.rendered){
34957             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34958         }
34959         
34960         Roo.each(this.radioes, function(i){
34961             i.checked = false;
34962             i.el.removeClass('checked');
34963         });
34964         
34965         Roo.each(this.radioes, function(i){
34966             
34967             if(i.value === v || i.value.toString() === v.toString()){
34968                 i.checked = true;
34969                 i.el.addClass('checked');
34970                 
34971                 if(suppressEvent !== true){
34972                     this.fireEvent('check', this, i);
34973                 }
34974                 
34975                 return false;
34976             }
34977             
34978         }, this);
34979         
34980         this.validate();
34981     },
34982     
34983     clearInvalid : function(){
34984         
34985         if(!this.el || this.preventMark){
34986             return;
34987         }
34988         
34989         this.el.removeClass([this.invalidClass]);
34990         
34991         this.fireEvent('valid', this);
34992     }
34993     
34994 });
34995
34996 Roo.apply(Roo.bootstrap.RadioSet, {
34997     
34998     groups: {},
34999     
35000     register : function(set)
35001     {
35002         this.groups[set.name] = set;
35003     },
35004     
35005     get: function(name) 
35006     {
35007         if (typeof(this.groups[name]) == 'undefined') {
35008             return false;
35009         }
35010         
35011         return this.groups[name] ;
35012     }
35013     
35014 });
35015 /*
35016  * Based on:
35017  * Ext JS Library 1.1.1
35018  * Copyright(c) 2006-2007, Ext JS, LLC.
35019  *
35020  * Originally Released Under LGPL - original licence link has changed is not relivant.
35021  *
35022  * Fork - LGPL
35023  * <script type="text/javascript">
35024  */
35025
35026
35027 /**
35028  * @class Roo.bootstrap.SplitBar
35029  * @extends Roo.util.Observable
35030  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
35031  * <br><br>
35032  * Usage:
35033  * <pre><code>
35034 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
35035                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
35036 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
35037 split.minSize = 100;
35038 split.maxSize = 600;
35039 split.animate = true;
35040 split.on('moved', splitterMoved);
35041 </code></pre>
35042  * @constructor
35043  * Create a new SplitBar
35044  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
35045  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
35046  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
35047  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
35048                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
35049                         position of the SplitBar).
35050  */
35051 Roo.bootstrap.SplitBar = function(cfg){
35052     
35053     /** @private */
35054     
35055     //{
35056     //  dragElement : elm
35057     //  resizingElement: el,
35058         // optional..
35059     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
35060     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
35061         // existingProxy ???
35062     //}
35063     
35064     this.el = Roo.get(cfg.dragElement, true);
35065     this.el.dom.unselectable = "on";
35066     /** @private */
35067     this.resizingEl = Roo.get(cfg.resizingElement, true);
35068
35069     /**
35070      * @private
35071      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
35072      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
35073      * @type Number
35074      */
35075     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
35076     
35077     /**
35078      * The minimum size of the resizing element. (Defaults to 0)
35079      * @type Number
35080      */
35081     this.minSize = 0;
35082     
35083     /**
35084      * The maximum size of the resizing element. (Defaults to 2000)
35085      * @type Number
35086      */
35087     this.maxSize = 2000;
35088     
35089     /**
35090      * Whether to animate the transition to the new size
35091      * @type Boolean
35092      */
35093     this.animate = false;
35094     
35095     /**
35096      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
35097      * @type Boolean
35098      */
35099     this.useShim = false;
35100     
35101     /** @private */
35102     this.shim = null;
35103     
35104     if(!cfg.existingProxy){
35105         /** @private */
35106         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
35107     }else{
35108         this.proxy = Roo.get(cfg.existingProxy).dom;
35109     }
35110     /** @private */
35111     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
35112     
35113     /** @private */
35114     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
35115     
35116     /** @private */
35117     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
35118     
35119     /** @private */
35120     this.dragSpecs = {};
35121     
35122     /**
35123      * @private The adapter to use to positon and resize elements
35124      */
35125     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35126     this.adapter.init(this);
35127     
35128     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35129         /** @private */
35130         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
35131         this.el.addClass("roo-splitbar-h");
35132     }else{
35133         /** @private */
35134         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
35135         this.el.addClass("roo-splitbar-v");
35136     }
35137     
35138     this.addEvents({
35139         /**
35140          * @event resize
35141          * Fires when the splitter is moved (alias for {@link #event-moved})
35142          * @param {Roo.bootstrap.SplitBar} this
35143          * @param {Number} newSize the new width or height
35144          */
35145         "resize" : true,
35146         /**
35147          * @event moved
35148          * Fires when the splitter is moved
35149          * @param {Roo.bootstrap.SplitBar} this
35150          * @param {Number} newSize the new width or height
35151          */
35152         "moved" : true,
35153         /**
35154          * @event beforeresize
35155          * Fires before the splitter is dragged
35156          * @param {Roo.bootstrap.SplitBar} this
35157          */
35158         "beforeresize" : true,
35159
35160         "beforeapply" : true
35161     });
35162
35163     Roo.util.Observable.call(this);
35164 };
35165
35166 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
35167     onStartProxyDrag : function(x, y){
35168         this.fireEvent("beforeresize", this);
35169         if(!this.overlay){
35170             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
35171             o.unselectable();
35172             o.enableDisplayMode("block");
35173             // all splitbars share the same overlay
35174             Roo.bootstrap.SplitBar.prototype.overlay = o;
35175         }
35176         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
35177         this.overlay.show();
35178         Roo.get(this.proxy).setDisplayed("block");
35179         var size = this.adapter.getElementSize(this);
35180         this.activeMinSize = this.getMinimumSize();;
35181         this.activeMaxSize = this.getMaximumSize();;
35182         var c1 = size - this.activeMinSize;
35183         var c2 = Math.max(this.activeMaxSize - size, 0);
35184         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35185             this.dd.resetConstraints();
35186             this.dd.setXConstraint(
35187                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
35188                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
35189             );
35190             this.dd.setYConstraint(0, 0);
35191         }else{
35192             this.dd.resetConstraints();
35193             this.dd.setXConstraint(0, 0);
35194             this.dd.setYConstraint(
35195                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
35196                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
35197             );
35198          }
35199         this.dragSpecs.startSize = size;
35200         this.dragSpecs.startPoint = [x, y];
35201         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
35202     },
35203     
35204     /** 
35205      * @private Called after the drag operation by the DDProxy
35206      */
35207     onEndProxyDrag : function(e){
35208         Roo.get(this.proxy).setDisplayed(false);
35209         var endPoint = Roo.lib.Event.getXY(e);
35210         if(this.overlay){
35211             this.overlay.hide();
35212         }
35213         var newSize;
35214         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35215             newSize = this.dragSpecs.startSize + 
35216                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
35217                     endPoint[0] - this.dragSpecs.startPoint[0] :
35218                     this.dragSpecs.startPoint[0] - endPoint[0]
35219                 );
35220         }else{
35221             newSize = this.dragSpecs.startSize + 
35222                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
35223                     endPoint[1] - this.dragSpecs.startPoint[1] :
35224                     this.dragSpecs.startPoint[1] - endPoint[1]
35225                 );
35226         }
35227         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
35228         if(newSize != this.dragSpecs.startSize){
35229             if(this.fireEvent('beforeapply', this, newSize) !== false){
35230                 this.adapter.setElementSize(this, newSize);
35231                 this.fireEvent("moved", this, newSize);
35232                 this.fireEvent("resize", this, newSize);
35233             }
35234         }
35235     },
35236     
35237     /**
35238      * Get the adapter this SplitBar uses
35239      * @return The adapter object
35240      */
35241     getAdapter : function(){
35242         return this.adapter;
35243     },
35244     
35245     /**
35246      * Set the adapter this SplitBar uses
35247      * @param {Object} adapter A SplitBar adapter object
35248      */
35249     setAdapter : function(adapter){
35250         this.adapter = adapter;
35251         this.adapter.init(this);
35252     },
35253     
35254     /**
35255      * Gets the minimum size for the resizing element
35256      * @return {Number} The minimum size
35257      */
35258     getMinimumSize : function(){
35259         return this.minSize;
35260     },
35261     
35262     /**
35263      * Sets the minimum size for the resizing element
35264      * @param {Number} minSize The minimum size
35265      */
35266     setMinimumSize : function(minSize){
35267         this.minSize = minSize;
35268     },
35269     
35270     /**
35271      * Gets the maximum size for the resizing element
35272      * @return {Number} The maximum size
35273      */
35274     getMaximumSize : function(){
35275         return this.maxSize;
35276     },
35277     
35278     /**
35279      * Sets the maximum size for the resizing element
35280      * @param {Number} maxSize The maximum size
35281      */
35282     setMaximumSize : function(maxSize){
35283         this.maxSize = maxSize;
35284     },
35285     
35286     /**
35287      * Sets the initialize size for the resizing element
35288      * @param {Number} size The initial size
35289      */
35290     setCurrentSize : function(size){
35291         var oldAnimate = this.animate;
35292         this.animate = false;
35293         this.adapter.setElementSize(this, size);
35294         this.animate = oldAnimate;
35295     },
35296     
35297     /**
35298      * Destroy this splitbar. 
35299      * @param {Boolean} removeEl True to remove the element
35300      */
35301     destroy : function(removeEl){
35302         if(this.shim){
35303             this.shim.remove();
35304         }
35305         this.dd.unreg();
35306         this.proxy.parentNode.removeChild(this.proxy);
35307         if(removeEl){
35308             this.el.remove();
35309         }
35310     }
35311 });
35312
35313 /**
35314  * @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.
35315  */
35316 Roo.bootstrap.SplitBar.createProxy = function(dir){
35317     var proxy = new Roo.Element(document.createElement("div"));
35318     proxy.unselectable();
35319     var cls = 'roo-splitbar-proxy';
35320     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
35321     document.body.appendChild(proxy.dom);
35322     return proxy.dom;
35323 };
35324
35325 /** 
35326  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
35327  * Default Adapter. It assumes the splitter and resizing element are not positioned
35328  * elements and only gets/sets the width of the element. Generally used for table based layouts.
35329  */
35330 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
35331 };
35332
35333 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
35334     // do nothing for now
35335     init : function(s){
35336     
35337     },
35338     /**
35339      * Called before drag operations to get the current size of the resizing element. 
35340      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35341      */
35342      getElementSize : function(s){
35343         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35344             return s.resizingEl.getWidth();
35345         }else{
35346             return s.resizingEl.getHeight();
35347         }
35348     },
35349     
35350     /**
35351      * Called after drag operations to set the size of the resizing element.
35352      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35353      * @param {Number} newSize The new size to set
35354      * @param {Function} onComplete A function to be invoked when resizing is complete
35355      */
35356     setElementSize : function(s, newSize, onComplete){
35357         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35358             if(!s.animate){
35359                 s.resizingEl.setWidth(newSize);
35360                 if(onComplete){
35361                     onComplete(s, newSize);
35362                 }
35363             }else{
35364                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
35365             }
35366         }else{
35367             
35368             if(!s.animate){
35369                 s.resizingEl.setHeight(newSize);
35370                 if(onComplete){
35371                     onComplete(s, newSize);
35372                 }
35373             }else{
35374                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
35375             }
35376         }
35377     }
35378 };
35379
35380 /** 
35381  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
35382  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
35383  * Adapter that  moves the splitter element to align with the resized sizing element. 
35384  * Used with an absolute positioned SplitBar.
35385  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
35386  * document.body, make sure you assign an id to the body element.
35387  */
35388 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
35389     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35390     this.container = Roo.get(container);
35391 };
35392
35393 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
35394     init : function(s){
35395         this.basic.init(s);
35396     },
35397     
35398     getElementSize : function(s){
35399         return this.basic.getElementSize(s);
35400     },
35401     
35402     setElementSize : function(s, newSize, onComplete){
35403         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
35404     },
35405     
35406     moveSplitter : function(s){
35407         var yes = Roo.bootstrap.SplitBar;
35408         switch(s.placement){
35409             case yes.LEFT:
35410                 s.el.setX(s.resizingEl.getRight());
35411                 break;
35412             case yes.RIGHT:
35413                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
35414                 break;
35415             case yes.TOP:
35416                 s.el.setY(s.resizingEl.getBottom());
35417                 break;
35418             case yes.BOTTOM:
35419                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
35420                 break;
35421         }
35422     }
35423 };
35424
35425 /**
35426  * Orientation constant - Create a vertical SplitBar
35427  * @static
35428  * @type Number
35429  */
35430 Roo.bootstrap.SplitBar.VERTICAL = 1;
35431
35432 /**
35433  * Orientation constant - Create a horizontal SplitBar
35434  * @static
35435  * @type Number
35436  */
35437 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35438
35439 /**
35440  * Placement constant - The resizing element is to the left of the splitter element
35441  * @static
35442  * @type Number
35443  */
35444 Roo.bootstrap.SplitBar.LEFT = 1;
35445
35446 /**
35447  * Placement constant - The resizing element is to the right of the splitter element
35448  * @static
35449  * @type Number
35450  */
35451 Roo.bootstrap.SplitBar.RIGHT = 2;
35452
35453 /**
35454  * Placement constant - The resizing element is positioned above the splitter element
35455  * @static
35456  * @type Number
35457  */
35458 Roo.bootstrap.SplitBar.TOP = 3;
35459
35460 /**
35461  * Placement constant - The resizing element is positioned under splitter element
35462  * @static
35463  * @type Number
35464  */
35465 Roo.bootstrap.SplitBar.BOTTOM = 4;
35466 Roo.namespace("Roo.bootstrap.layout");/*
35467  * Based on:
35468  * Ext JS Library 1.1.1
35469  * Copyright(c) 2006-2007, Ext JS, LLC.
35470  *
35471  * Originally Released Under LGPL - original licence link has changed is not relivant.
35472  *
35473  * Fork - LGPL
35474  * <script type="text/javascript">
35475  */
35476
35477 /**
35478  * @class Roo.bootstrap.layout.Manager
35479  * @extends Roo.bootstrap.Component
35480  * Base class for layout managers.
35481  */
35482 Roo.bootstrap.layout.Manager = function(config)
35483 {
35484     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
35485
35486
35487
35488
35489
35490     /** false to disable window resize monitoring @type Boolean */
35491     this.monitorWindowResize = true;
35492     this.regions = {};
35493     this.addEvents({
35494         /**
35495          * @event layout
35496          * Fires when a layout is performed.
35497          * @param {Roo.LayoutManager} this
35498          */
35499         "layout" : true,
35500         /**
35501          * @event regionresized
35502          * Fires when the user resizes a region.
35503          * @param {Roo.LayoutRegion} region The resized region
35504          * @param {Number} newSize The new size (width for east/west, height for north/south)
35505          */
35506         "regionresized" : true,
35507         /**
35508          * @event regioncollapsed
35509          * Fires when a region is collapsed.
35510          * @param {Roo.LayoutRegion} region The collapsed region
35511          */
35512         "regioncollapsed" : true,
35513         /**
35514          * @event regionexpanded
35515          * Fires when a region is expanded.
35516          * @param {Roo.LayoutRegion} region The expanded region
35517          */
35518         "regionexpanded" : true
35519     });
35520     this.updating = false;
35521
35522     if (config.el) {
35523         this.el = Roo.get(config.el);
35524         this.initEvents();
35525     }
35526
35527 };
35528
35529 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35530
35531
35532     regions : null,
35533
35534     monitorWindowResize : true,
35535
35536
35537     updating : false,
35538
35539
35540     onRender : function(ct, position)
35541     {
35542         if(!this.el){
35543             this.el = Roo.get(ct);
35544             this.initEvents();
35545         }
35546         //this.fireEvent('render',this);
35547     },
35548
35549
35550     initEvents: function()
35551     {
35552
35553
35554         // ie scrollbar fix
35555         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35556             document.body.scroll = "no";
35557         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35558             this.el.position('relative');
35559         }
35560         this.id = this.el.id;
35561         this.el.addClass("roo-layout-container");
35562         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35563         if(this.el.dom != document.body ) {
35564             this.el.on('resize', this.layout,this);
35565             this.el.on('show', this.layout,this);
35566         }
35567
35568     },
35569
35570     /**
35571      * Returns true if this layout is currently being updated
35572      * @return {Boolean}
35573      */
35574     isUpdating : function(){
35575         return this.updating;
35576     },
35577
35578     /**
35579      * Suspend the LayoutManager from doing auto-layouts while
35580      * making multiple add or remove calls
35581      */
35582     beginUpdate : function(){
35583         this.updating = true;
35584     },
35585
35586     /**
35587      * Restore auto-layouts and optionally disable the manager from performing a layout
35588      * @param {Boolean} noLayout true to disable a layout update
35589      */
35590     endUpdate : function(noLayout){
35591         this.updating = false;
35592         if(!noLayout){
35593             this.layout();
35594         }
35595     },
35596
35597     layout: function(){
35598         // abstract...
35599     },
35600
35601     onRegionResized : function(region, newSize){
35602         this.fireEvent("regionresized", region, newSize);
35603         this.layout();
35604     },
35605
35606     onRegionCollapsed : function(region){
35607         this.fireEvent("regioncollapsed", region);
35608     },
35609
35610     onRegionExpanded : function(region){
35611         this.fireEvent("regionexpanded", region);
35612     },
35613
35614     /**
35615      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35616      * performs box-model adjustments.
35617      * @return {Object} The size as an object {width: (the width), height: (the height)}
35618      */
35619     getViewSize : function()
35620     {
35621         var size;
35622         if(this.el.dom != document.body){
35623             size = this.el.getSize();
35624         }else{
35625             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35626         }
35627         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35628         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35629         return size;
35630     },
35631
35632     /**
35633      * Returns the Element this layout is bound to.
35634      * @return {Roo.Element}
35635      */
35636     getEl : function(){
35637         return this.el;
35638     },
35639
35640     /**
35641      * Returns the specified region.
35642      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35643      * @return {Roo.LayoutRegion}
35644      */
35645     getRegion : function(target){
35646         return this.regions[target.toLowerCase()];
35647     },
35648
35649     onWindowResize : function(){
35650         if(this.monitorWindowResize){
35651             this.layout();
35652         }
35653     }
35654 });
35655 /*
35656  * Based on:
35657  * Ext JS Library 1.1.1
35658  * Copyright(c) 2006-2007, Ext JS, LLC.
35659  *
35660  * Originally Released Under LGPL - original licence link has changed is not relivant.
35661  *
35662  * Fork - LGPL
35663  * <script type="text/javascript">
35664  */
35665 /**
35666  * @class Roo.bootstrap.layout.Border
35667  * @extends Roo.bootstrap.layout.Manager
35668  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35669  * please see: examples/bootstrap/nested.html<br><br>
35670  
35671 <b>The container the layout is rendered into can be either the body element or any other element.
35672 If it is not the body element, the container needs to either be an absolute positioned element,
35673 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35674 the container size if it is not the body element.</b>
35675
35676 * @constructor
35677 * Create a new Border
35678 * @param {Object} config Configuration options
35679  */
35680 Roo.bootstrap.layout.Border = function(config){
35681     config = config || {};
35682     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35683     
35684     
35685     
35686     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35687         if(config[region]){
35688             config[region].region = region;
35689             this.addRegion(config[region]);
35690         }
35691     },this);
35692     
35693 };
35694
35695 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35696
35697 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35698     
35699     parent : false, // this might point to a 'nest' or a ???
35700     
35701     /**
35702      * Creates and adds a new region if it doesn't already exist.
35703      * @param {String} target The target region key (north, south, east, west or center).
35704      * @param {Object} config The regions config object
35705      * @return {BorderLayoutRegion} The new region
35706      */
35707     addRegion : function(config)
35708     {
35709         if(!this.regions[config.region]){
35710             var r = this.factory(config);
35711             this.bindRegion(r);
35712         }
35713         return this.regions[config.region];
35714     },
35715
35716     // private (kinda)
35717     bindRegion : function(r){
35718         this.regions[r.config.region] = r;
35719         
35720         r.on("visibilitychange",    this.layout, this);
35721         r.on("paneladded",          this.layout, this);
35722         r.on("panelremoved",        this.layout, this);
35723         r.on("invalidated",         this.layout, this);
35724         r.on("resized",             this.onRegionResized, this);
35725         r.on("collapsed",           this.onRegionCollapsed, this);
35726         r.on("expanded",            this.onRegionExpanded, this);
35727     },
35728
35729     /**
35730      * Performs a layout update.
35731      */
35732     layout : function()
35733     {
35734         if(this.updating) {
35735             return;
35736         }
35737         
35738         // render all the rebions if they have not been done alreayd?
35739         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35740             if(this.regions[region] && !this.regions[region].bodyEl){
35741                 this.regions[region].onRender(this.el)
35742             }
35743         },this);
35744         
35745         var size = this.getViewSize();
35746         var w = size.width;
35747         var h = size.height;
35748         var centerW = w;
35749         var centerH = h;
35750         var centerY = 0;
35751         var centerX = 0;
35752         //var x = 0, y = 0;
35753
35754         var rs = this.regions;
35755         var north = rs["north"];
35756         var south = rs["south"]; 
35757         var west = rs["west"];
35758         var east = rs["east"];
35759         var center = rs["center"];
35760         //if(this.hideOnLayout){ // not supported anymore
35761             //c.el.setStyle("display", "none");
35762         //}
35763         if(north && north.isVisible()){
35764             var b = north.getBox();
35765             var m = north.getMargins();
35766             b.width = w - (m.left+m.right);
35767             b.x = m.left;
35768             b.y = m.top;
35769             centerY = b.height + b.y + m.bottom;
35770             centerH -= centerY;
35771             north.updateBox(this.safeBox(b));
35772         }
35773         if(south && south.isVisible()){
35774             var b = south.getBox();
35775             var m = south.getMargins();
35776             b.width = w - (m.left+m.right);
35777             b.x = m.left;
35778             var totalHeight = (b.height + m.top + m.bottom);
35779             b.y = h - totalHeight + m.top;
35780             centerH -= totalHeight;
35781             south.updateBox(this.safeBox(b));
35782         }
35783         if(west && west.isVisible()){
35784             var b = west.getBox();
35785             var m = west.getMargins();
35786             b.height = centerH - (m.top+m.bottom);
35787             b.x = m.left;
35788             b.y = centerY + m.top;
35789             var totalWidth = (b.width + m.left + m.right);
35790             centerX += totalWidth;
35791             centerW -= totalWidth;
35792             west.updateBox(this.safeBox(b));
35793         }
35794         if(east && east.isVisible()){
35795             var b = east.getBox();
35796             var m = east.getMargins();
35797             b.height = centerH - (m.top+m.bottom);
35798             var totalWidth = (b.width + m.left + m.right);
35799             b.x = w - totalWidth + m.left;
35800             b.y = centerY + m.top;
35801             centerW -= totalWidth;
35802             east.updateBox(this.safeBox(b));
35803         }
35804         if(center){
35805             var m = center.getMargins();
35806             var centerBox = {
35807                 x: centerX + m.left,
35808                 y: centerY + m.top,
35809                 width: centerW - (m.left+m.right),
35810                 height: centerH - (m.top+m.bottom)
35811             };
35812             //if(this.hideOnLayout){
35813                 //center.el.setStyle("display", "block");
35814             //}
35815             center.updateBox(this.safeBox(centerBox));
35816         }
35817         this.el.repaint();
35818         this.fireEvent("layout", this);
35819     },
35820
35821     // private
35822     safeBox : function(box){
35823         box.width = Math.max(0, box.width);
35824         box.height = Math.max(0, box.height);
35825         return box;
35826     },
35827
35828     /**
35829      * Adds a ContentPanel (or subclass) to this layout.
35830      * @param {String} target The target region key (north, south, east, west or center).
35831      * @param {Roo.ContentPanel} panel The panel to add
35832      * @return {Roo.ContentPanel} The added panel
35833      */
35834     add : function(target, panel){
35835          
35836         target = target.toLowerCase();
35837         return this.regions[target].add(panel);
35838     },
35839
35840     /**
35841      * Remove a ContentPanel (or subclass) to this layout.
35842      * @param {String} target The target region key (north, south, east, west or center).
35843      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35844      * @return {Roo.ContentPanel} The removed panel
35845      */
35846     remove : function(target, panel){
35847         target = target.toLowerCase();
35848         return this.regions[target].remove(panel);
35849     },
35850
35851     /**
35852      * Searches all regions for a panel with the specified id
35853      * @param {String} panelId
35854      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35855      */
35856     findPanel : function(panelId){
35857         var rs = this.regions;
35858         for(var target in rs){
35859             if(typeof rs[target] != "function"){
35860                 var p = rs[target].getPanel(panelId);
35861                 if(p){
35862                     return p;
35863                 }
35864             }
35865         }
35866         return null;
35867     },
35868
35869     /**
35870      * Searches all regions for a panel with the specified id and activates (shows) it.
35871      * @param {String/ContentPanel} panelId The panels id or the panel itself
35872      * @return {Roo.ContentPanel} The shown panel or null
35873      */
35874     showPanel : function(panelId) {
35875       var rs = this.regions;
35876       for(var target in rs){
35877          var r = rs[target];
35878          if(typeof r != "function"){
35879             if(r.hasPanel(panelId)){
35880                return r.showPanel(panelId);
35881             }
35882          }
35883       }
35884       return null;
35885    },
35886
35887    /**
35888      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35889      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35890      */
35891    /*
35892     restoreState : function(provider){
35893         if(!provider){
35894             provider = Roo.state.Manager;
35895         }
35896         var sm = new Roo.LayoutStateManager();
35897         sm.init(this, provider);
35898     },
35899 */
35900  
35901  
35902     /**
35903      * Adds a xtype elements to the layout.
35904      * <pre><code>
35905
35906 layout.addxtype({
35907        xtype : 'ContentPanel',
35908        region: 'west',
35909        items: [ .... ]
35910    }
35911 );
35912
35913 layout.addxtype({
35914         xtype : 'NestedLayoutPanel',
35915         region: 'west',
35916         layout: {
35917            center: { },
35918            west: { }   
35919         },
35920         items : [ ... list of content panels or nested layout panels.. ]
35921    }
35922 );
35923 </code></pre>
35924      * @param {Object} cfg Xtype definition of item to add.
35925      */
35926     addxtype : function(cfg)
35927     {
35928         // basically accepts a pannel...
35929         // can accept a layout region..!?!?
35930         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35931         
35932         
35933         // theory?  children can only be panels??
35934         
35935         //if (!cfg.xtype.match(/Panel$/)) {
35936         //    return false;
35937         //}
35938         var ret = false;
35939         
35940         if (typeof(cfg.region) == 'undefined') {
35941             Roo.log("Failed to add Panel, region was not set");
35942             Roo.log(cfg);
35943             return false;
35944         }
35945         var region = cfg.region;
35946         delete cfg.region;
35947         
35948           
35949         var xitems = [];
35950         if (cfg.items) {
35951             xitems = cfg.items;
35952             delete cfg.items;
35953         }
35954         var nb = false;
35955         
35956         if ( region == 'center') {
35957             Roo.log("Center: " + cfg.title);
35958         }
35959         
35960         
35961         switch(cfg.xtype) 
35962         {
35963             case 'Content':  // ContentPanel (el, cfg)
35964             case 'Scroll':  // ContentPanel (el, cfg)
35965             case 'View': 
35966                 cfg.autoCreate = cfg.autoCreate || true;
35967                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35968                 //} else {
35969                 //    var el = this.el.createChild();
35970                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35971                 //}
35972                 
35973                 this.add(region, ret);
35974                 break;
35975             
35976             /*
35977             case 'TreePanel': // our new panel!
35978                 cfg.el = this.el.createChild();
35979                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35980                 this.add(region, ret);
35981                 break;
35982             */
35983             
35984             case 'Nest': 
35985                 // create a new Layout (which is  a Border Layout...
35986                 
35987                 var clayout = cfg.layout;
35988                 clayout.el  = this.el.createChild();
35989                 clayout.items   = clayout.items  || [];
35990                 
35991                 delete cfg.layout;
35992                 
35993                 // replace this exitems with the clayout ones..
35994                 xitems = clayout.items;
35995                  
35996                 // force background off if it's in center...
35997                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35998                     cfg.background = false;
35999                 }
36000                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
36001                 
36002                 
36003                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36004                 //console.log('adding nested layout panel '  + cfg.toSource());
36005                 this.add(region, ret);
36006                 nb = {}; /// find first...
36007                 break;
36008             
36009             case 'Grid':
36010                 
36011                 // needs grid and region
36012                 
36013                 //var el = this.getRegion(region).el.createChild();
36014                 /*
36015                  *var el = this.el.createChild();
36016                 // create the grid first...
36017                 cfg.grid.container = el;
36018                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
36019                 */
36020                 
36021                 if (region == 'center' && this.active ) {
36022                     cfg.background = false;
36023                 }
36024                 
36025                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36026                 
36027                 this.add(region, ret);
36028                 /*
36029                 if (cfg.background) {
36030                     // render grid on panel activation (if panel background)
36031                     ret.on('activate', function(gp) {
36032                         if (!gp.grid.rendered) {
36033                     //        gp.grid.render(el);
36034                         }
36035                     });
36036                 } else {
36037                   //  cfg.grid.render(el);
36038                 }
36039                 */
36040                 break;
36041            
36042            
36043             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
36044                 // it was the old xcomponent building that caused this before.
36045                 // espeically if border is the top element in the tree.
36046                 ret = this;
36047                 break; 
36048                 
36049                     
36050                 
36051                 
36052                 
36053             default:
36054                 /*
36055                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
36056                     
36057                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
36058                     this.add(region, ret);
36059                 } else {
36060                 */
36061                     Roo.log(cfg);
36062                     throw "Can not add '" + cfg.xtype + "' to Border";
36063                     return null;
36064              
36065                                 
36066              
36067         }
36068         this.beginUpdate();
36069         // add children..
36070         var region = '';
36071         var abn = {};
36072         Roo.each(xitems, function(i)  {
36073             region = nb && i.region ? i.region : false;
36074             
36075             var add = ret.addxtype(i);
36076            
36077             if (region) {
36078                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
36079                 if (!i.background) {
36080                     abn[region] = nb[region] ;
36081                 }
36082             }
36083             
36084         });
36085         this.endUpdate();
36086
36087         // make the last non-background panel active..
36088         //if (nb) { Roo.log(abn); }
36089         if (nb) {
36090             
36091             for(var r in abn) {
36092                 region = this.getRegion(r);
36093                 if (region) {
36094                     // tried using nb[r], but it does not work..
36095                      
36096                     region.showPanel(abn[r]);
36097                    
36098                 }
36099             }
36100         }
36101         return ret;
36102         
36103     },
36104     
36105     
36106 // private
36107     factory : function(cfg)
36108     {
36109         
36110         var validRegions = Roo.bootstrap.layout.Border.regions;
36111
36112         var target = cfg.region;
36113         cfg.mgr = this;
36114         
36115         var r = Roo.bootstrap.layout;
36116         Roo.log(target);
36117         switch(target){
36118             case "north":
36119                 return new r.North(cfg);
36120             case "south":
36121                 return new r.South(cfg);
36122             case "east":
36123                 return new r.East(cfg);
36124             case "west":
36125                 return new r.West(cfg);
36126             case "center":
36127                 return new r.Center(cfg);
36128         }
36129         throw 'Layout region "'+target+'" not supported.';
36130     }
36131     
36132     
36133 });
36134  /*
36135  * Based on:
36136  * Ext JS Library 1.1.1
36137  * Copyright(c) 2006-2007, Ext JS, LLC.
36138  *
36139  * Originally Released Under LGPL - original licence link has changed is not relivant.
36140  *
36141  * Fork - LGPL
36142  * <script type="text/javascript">
36143  */
36144  
36145 /**
36146  * @class Roo.bootstrap.layout.Basic
36147  * @extends Roo.util.Observable
36148  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
36149  * and does not have a titlebar, tabs or any other features. All it does is size and position 
36150  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
36151  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
36152  * @cfg {string}   region  the region that it inhabits..
36153  * @cfg {bool}   skipConfig skip config?
36154  * 
36155
36156  */
36157 Roo.bootstrap.layout.Basic = function(config){
36158     
36159     this.mgr = config.mgr;
36160     
36161     this.position = config.region;
36162     
36163     var skipConfig = config.skipConfig;
36164     
36165     this.events = {
36166         /**
36167          * @scope Roo.BasicLayoutRegion
36168          */
36169         
36170         /**
36171          * @event beforeremove
36172          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
36173          * @param {Roo.LayoutRegion} this
36174          * @param {Roo.ContentPanel} panel The panel
36175          * @param {Object} e The cancel event object
36176          */
36177         "beforeremove" : true,
36178         /**
36179          * @event invalidated
36180          * Fires when the layout for this region is changed.
36181          * @param {Roo.LayoutRegion} this
36182          */
36183         "invalidated" : true,
36184         /**
36185          * @event visibilitychange
36186          * Fires when this region is shown or hidden 
36187          * @param {Roo.LayoutRegion} this
36188          * @param {Boolean} visibility true or false
36189          */
36190         "visibilitychange" : true,
36191         /**
36192          * @event paneladded
36193          * Fires when a panel is added. 
36194          * @param {Roo.LayoutRegion} this
36195          * @param {Roo.ContentPanel} panel The panel
36196          */
36197         "paneladded" : true,
36198         /**
36199          * @event panelremoved
36200          * Fires when a panel is removed. 
36201          * @param {Roo.LayoutRegion} this
36202          * @param {Roo.ContentPanel} panel The panel
36203          */
36204         "panelremoved" : true,
36205         /**
36206          * @event beforecollapse
36207          * Fires when this region before collapse.
36208          * @param {Roo.LayoutRegion} this
36209          */
36210         "beforecollapse" : true,
36211         /**
36212          * @event collapsed
36213          * Fires when this region is collapsed.
36214          * @param {Roo.LayoutRegion} this
36215          */
36216         "collapsed" : true,
36217         /**
36218          * @event expanded
36219          * Fires when this region is expanded.
36220          * @param {Roo.LayoutRegion} this
36221          */
36222         "expanded" : true,
36223         /**
36224          * @event slideshow
36225          * Fires when this region is slid into view.
36226          * @param {Roo.LayoutRegion} this
36227          */
36228         "slideshow" : true,
36229         /**
36230          * @event slidehide
36231          * Fires when this region slides out of view. 
36232          * @param {Roo.LayoutRegion} this
36233          */
36234         "slidehide" : true,
36235         /**
36236          * @event panelactivated
36237          * Fires when a panel is activated. 
36238          * @param {Roo.LayoutRegion} this
36239          * @param {Roo.ContentPanel} panel The activated panel
36240          */
36241         "panelactivated" : true,
36242         /**
36243          * @event resized
36244          * Fires when the user resizes this region. 
36245          * @param {Roo.LayoutRegion} this
36246          * @param {Number} newSize The new size (width for east/west, height for north/south)
36247          */
36248         "resized" : true
36249     };
36250     /** A collection of panels in this region. @type Roo.util.MixedCollection */
36251     this.panels = new Roo.util.MixedCollection();
36252     this.panels.getKey = this.getPanelId.createDelegate(this);
36253     this.box = null;
36254     this.activePanel = null;
36255     // ensure listeners are added...
36256     
36257     if (config.listeners || config.events) {
36258         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
36259             listeners : config.listeners || {},
36260             events : config.events || {}
36261         });
36262     }
36263     
36264     if(skipConfig !== true){
36265         this.applyConfig(config);
36266     }
36267 };
36268
36269 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
36270 {
36271     getPanelId : function(p){
36272         return p.getId();
36273     },
36274     
36275     applyConfig : function(config){
36276         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36277         this.config = config;
36278         
36279     },
36280     
36281     /**
36282      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
36283      * the width, for horizontal (north, south) the height.
36284      * @param {Number} newSize The new width or height
36285      */
36286     resizeTo : function(newSize){
36287         var el = this.el ? this.el :
36288                  (this.activePanel ? this.activePanel.getEl() : null);
36289         if(el){
36290             switch(this.position){
36291                 case "east":
36292                 case "west":
36293                     el.setWidth(newSize);
36294                     this.fireEvent("resized", this, newSize);
36295                 break;
36296                 case "north":
36297                 case "south":
36298                     el.setHeight(newSize);
36299                     this.fireEvent("resized", this, newSize);
36300                 break;                
36301             }
36302         }
36303     },
36304     
36305     getBox : function(){
36306         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
36307     },
36308     
36309     getMargins : function(){
36310         return this.margins;
36311     },
36312     
36313     updateBox : function(box){
36314         this.box = box;
36315         var el = this.activePanel.getEl();
36316         el.dom.style.left = box.x + "px";
36317         el.dom.style.top = box.y + "px";
36318         this.activePanel.setSize(box.width, box.height);
36319     },
36320     
36321     /**
36322      * Returns the container element for this region.
36323      * @return {Roo.Element}
36324      */
36325     getEl : function(){
36326         return this.activePanel;
36327     },
36328     
36329     /**
36330      * Returns true if this region is currently visible.
36331      * @return {Boolean}
36332      */
36333     isVisible : function(){
36334         return this.activePanel ? true : false;
36335     },
36336     
36337     setActivePanel : function(panel){
36338         panel = this.getPanel(panel);
36339         if(this.activePanel && this.activePanel != panel){
36340             this.activePanel.setActiveState(false);
36341             this.activePanel.getEl().setLeftTop(-10000,-10000);
36342         }
36343         this.activePanel = panel;
36344         panel.setActiveState(true);
36345         if(this.box){
36346             panel.setSize(this.box.width, this.box.height);
36347         }
36348         this.fireEvent("panelactivated", this, panel);
36349         this.fireEvent("invalidated");
36350     },
36351     
36352     /**
36353      * Show the specified panel.
36354      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
36355      * @return {Roo.ContentPanel} The shown panel or null
36356      */
36357     showPanel : function(panel){
36358         panel = this.getPanel(panel);
36359         if(panel){
36360             this.setActivePanel(panel);
36361         }
36362         return panel;
36363     },
36364     
36365     /**
36366      * Get the active panel for this region.
36367      * @return {Roo.ContentPanel} The active panel or null
36368      */
36369     getActivePanel : function(){
36370         return this.activePanel;
36371     },
36372     
36373     /**
36374      * Add the passed ContentPanel(s)
36375      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36376      * @return {Roo.ContentPanel} The panel added (if only one was added)
36377      */
36378     add : function(panel){
36379         if(arguments.length > 1){
36380             for(var i = 0, len = arguments.length; i < len; i++) {
36381                 this.add(arguments[i]);
36382             }
36383             return null;
36384         }
36385         if(this.hasPanel(panel)){
36386             this.showPanel(panel);
36387             return panel;
36388         }
36389         var el = panel.getEl();
36390         if(el.dom.parentNode != this.mgr.el.dom){
36391             this.mgr.el.dom.appendChild(el.dom);
36392         }
36393         if(panel.setRegion){
36394             panel.setRegion(this);
36395         }
36396         this.panels.add(panel);
36397         el.setStyle("position", "absolute");
36398         if(!panel.background){
36399             this.setActivePanel(panel);
36400             if(this.config.initialSize && this.panels.getCount()==1){
36401                 this.resizeTo(this.config.initialSize);
36402             }
36403         }
36404         this.fireEvent("paneladded", this, panel);
36405         return panel;
36406     },
36407     
36408     /**
36409      * Returns true if the panel is in this region.
36410      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36411      * @return {Boolean}
36412      */
36413     hasPanel : function(panel){
36414         if(typeof panel == "object"){ // must be panel obj
36415             panel = panel.getId();
36416         }
36417         return this.getPanel(panel) ? true : false;
36418     },
36419     
36420     /**
36421      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36422      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36423      * @param {Boolean} preservePanel Overrides the config preservePanel option
36424      * @return {Roo.ContentPanel} The panel that was removed
36425      */
36426     remove : function(panel, preservePanel){
36427         panel = this.getPanel(panel);
36428         if(!panel){
36429             return null;
36430         }
36431         var e = {};
36432         this.fireEvent("beforeremove", this, panel, e);
36433         if(e.cancel === true){
36434             return null;
36435         }
36436         var panelId = panel.getId();
36437         this.panels.removeKey(panelId);
36438         return panel;
36439     },
36440     
36441     /**
36442      * Returns the panel specified or null if it's not in this region.
36443      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36444      * @return {Roo.ContentPanel}
36445      */
36446     getPanel : function(id){
36447         if(typeof id == "object"){ // must be panel obj
36448             return id;
36449         }
36450         return this.panels.get(id);
36451     },
36452     
36453     /**
36454      * Returns this regions position (north/south/east/west/center).
36455      * @return {String} 
36456      */
36457     getPosition: function(){
36458         return this.position;    
36459     }
36460 });/*
36461  * Based on:
36462  * Ext JS Library 1.1.1
36463  * Copyright(c) 2006-2007, Ext JS, LLC.
36464  *
36465  * Originally Released Under LGPL - original licence link has changed is not relivant.
36466  *
36467  * Fork - LGPL
36468  * <script type="text/javascript">
36469  */
36470  
36471 /**
36472  * @class Roo.bootstrap.layout.Region
36473  * @extends Roo.bootstrap.layout.Basic
36474  * This class represents a region in a layout manager.
36475  
36476  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36477  * @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})
36478  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
36479  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
36480  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
36481  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
36482  * @cfg {String}    title           The title for the region (overrides panel titles)
36483  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
36484  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36485  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
36486  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36487  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
36488  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36489  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
36490  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
36491  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
36492  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
36493
36494  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
36495  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
36496  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
36497  * @cfg {Number}    width           For East/West panels
36498  * @cfg {Number}    height          For North/South panels
36499  * @cfg {Boolean}   split           To show the splitter
36500  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
36501  * 
36502  * @cfg {string}   cls             Extra CSS classes to add to region
36503  * 
36504  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
36505  * @cfg {string}   region  the region that it inhabits..
36506  *
36507
36508  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
36509  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
36510
36511  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
36512  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
36513  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
36514  */
36515 Roo.bootstrap.layout.Region = function(config)
36516 {
36517     this.applyConfig(config);
36518
36519     var mgr = config.mgr;
36520     var pos = config.region;
36521     config.skipConfig = true;
36522     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36523     
36524     if (mgr.el) {
36525         this.onRender(mgr.el);   
36526     }
36527      
36528     this.visible = true;
36529     this.collapsed = false;
36530     this.unrendered_panels = [];
36531 };
36532
36533 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36534
36535     position: '', // set by wrapper (eg. north/south etc..)
36536     unrendered_panels : null,  // unrendered panels.
36537     
36538     tabPosition : false,
36539     
36540     mgr: false, // points to 'Border'
36541     
36542     
36543     createBody : function(){
36544         /** This region's body element 
36545         * @type Roo.Element */
36546         this.bodyEl = this.el.createChild({
36547                 tag: "div",
36548                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36549         });
36550     },
36551
36552     onRender: function(ctr, pos)
36553     {
36554         var dh = Roo.DomHelper;
36555         /** This region's container element 
36556         * @type Roo.Element */
36557         this.el = dh.append(ctr.dom, {
36558                 tag: "div",
36559                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36560             }, true);
36561         /** This region's title element 
36562         * @type Roo.Element */
36563     
36564         this.titleEl = dh.append(this.el.dom,  {
36565                 tag: "div",
36566                 unselectable: "on",
36567                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36568                 children:[
36569                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
36570                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36571                 ]
36572             }, true);
36573         
36574         this.titleEl.enableDisplayMode();
36575         /** This region's title text element 
36576         * @type HTMLElement */
36577         this.titleTextEl = this.titleEl.dom.firstChild;
36578         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36579         /*
36580         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36581         this.closeBtn.enableDisplayMode();
36582         this.closeBtn.on("click", this.closeClicked, this);
36583         this.closeBtn.hide();
36584     */
36585         this.createBody(this.config);
36586         if(this.config.hideWhenEmpty){
36587             this.hide();
36588             this.on("paneladded", this.validateVisibility, this);
36589             this.on("panelremoved", this.validateVisibility, this);
36590         }
36591         if(this.autoScroll){
36592             this.bodyEl.setStyle("overflow", "auto");
36593         }else{
36594             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36595         }
36596         //if(c.titlebar !== false){
36597             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36598                 this.titleEl.hide();
36599             }else{
36600                 this.titleEl.show();
36601                 if(this.config.title){
36602                     this.titleTextEl.innerHTML = this.config.title;
36603                 }
36604             }
36605         //}
36606         if(this.config.collapsed){
36607             this.collapse(true);
36608         }
36609         if(this.config.hidden){
36610             this.hide();
36611         }
36612         
36613         if (this.unrendered_panels && this.unrendered_panels.length) {
36614             for (var i =0;i< this.unrendered_panels.length; i++) {
36615                 this.add(this.unrendered_panels[i]);
36616             }
36617             this.unrendered_panels = null;
36618             
36619         }
36620         
36621     },
36622     
36623     applyConfig : function(c)
36624     {
36625         /*
36626          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36627             var dh = Roo.DomHelper;
36628             if(c.titlebar !== false){
36629                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36630                 this.collapseBtn.on("click", this.collapse, this);
36631                 this.collapseBtn.enableDisplayMode();
36632                 /*
36633                 if(c.showPin === true || this.showPin){
36634                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36635                     this.stickBtn.enableDisplayMode();
36636                     this.stickBtn.on("click", this.expand, this);
36637                     this.stickBtn.hide();
36638                 }
36639                 
36640             }
36641             */
36642             /** This region's collapsed element
36643             * @type Roo.Element */
36644             /*
36645              *
36646             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36647                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36648             ]}, true);
36649             
36650             if(c.floatable !== false){
36651                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36652                this.collapsedEl.on("click", this.collapseClick, this);
36653             }
36654
36655             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36656                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36657                    id: "message", unselectable: "on", style:{"float":"left"}});
36658                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36659              }
36660             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36661             this.expandBtn.on("click", this.expand, this);
36662             
36663         }
36664         
36665         if(this.collapseBtn){
36666             this.collapseBtn.setVisible(c.collapsible == true);
36667         }
36668         
36669         this.cmargins = c.cmargins || this.cmargins ||
36670                          (this.position == "west" || this.position == "east" ?
36671                              {top: 0, left: 2, right:2, bottom: 0} :
36672                              {top: 2, left: 0, right:0, bottom: 2});
36673         */
36674         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36675         
36676         
36677         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36678         
36679         this.autoScroll = c.autoScroll || false;
36680         
36681         
36682        
36683         
36684         this.duration = c.duration || .30;
36685         this.slideDuration = c.slideDuration || .45;
36686         this.config = c;
36687        
36688     },
36689     /**
36690      * Returns true if this region is currently visible.
36691      * @return {Boolean}
36692      */
36693     isVisible : function(){
36694         return this.visible;
36695     },
36696
36697     /**
36698      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36699      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36700      */
36701     //setCollapsedTitle : function(title){
36702     //    title = title || "&#160;";
36703      //   if(this.collapsedTitleTextEl){
36704       //      this.collapsedTitleTextEl.innerHTML = title;
36705        // }
36706     //},
36707
36708     getBox : function(){
36709         var b;
36710       //  if(!this.collapsed){
36711             b = this.el.getBox(false, true);
36712        // }else{
36713           //  b = this.collapsedEl.getBox(false, true);
36714         //}
36715         return b;
36716     },
36717
36718     getMargins : function(){
36719         return this.margins;
36720         //return this.collapsed ? this.cmargins : this.margins;
36721     },
36722 /*
36723     highlight : function(){
36724         this.el.addClass("x-layout-panel-dragover");
36725     },
36726
36727     unhighlight : function(){
36728         this.el.removeClass("x-layout-panel-dragover");
36729     },
36730 */
36731     updateBox : function(box)
36732     {
36733         if (!this.bodyEl) {
36734             return; // not rendered yet..
36735         }
36736         
36737         this.box = box;
36738         if(!this.collapsed){
36739             this.el.dom.style.left = box.x + "px";
36740             this.el.dom.style.top = box.y + "px";
36741             this.updateBody(box.width, box.height);
36742         }else{
36743             this.collapsedEl.dom.style.left = box.x + "px";
36744             this.collapsedEl.dom.style.top = box.y + "px";
36745             this.collapsedEl.setSize(box.width, box.height);
36746         }
36747         if(this.tabs){
36748             this.tabs.autoSizeTabs();
36749         }
36750     },
36751
36752     updateBody : function(w, h)
36753     {
36754         if(w !== null){
36755             this.el.setWidth(w);
36756             w -= this.el.getBorderWidth("rl");
36757             if(this.config.adjustments){
36758                 w += this.config.adjustments[0];
36759             }
36760         }
36761         if(h !== null && h > 0){
36762             this.el.setHeight(h);
36763             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36764             h -= this.el.getBorderWidth("tb");
36765             if(this.config.adjustments){
36766                 h += this.config.adjustments[1];
36767             }
36768             this.bodyEl.setHeight(h);
36769             if(this.tabs){
36770                 h = this.tabs.syncHeight(h);
36771             }
36772         }
36773         if(this.panelSize){
36774             w = w !== null ? w : this.panelSize.width;
36775             h = h !== null ? h : this.panelSize.height;
36776         }
36777         if(this.activePanel){
36778             var el = this.activePanel.getEl();
36779             w = w !== null ? w : el.getWidth();
36780             h = h !== null ? h : el.getHeight();
36781             this.panelSize = {width: w, height: h};
36782             this.activePanel.setSize(w, h);
36783         }
36784         if(Roo.isIE && this.tabs){
36785             this.tabs.el.repaint();
36786         }
36787     },
36788
36789     /**
36790      * Returns the container element for this region.
36791      * @return {Roo.Element}
36792      */
36793     getEl : function(){
36794         return this.el;
36795     },
36796
36797     /**
36798      * Hides this region.
36799      */
36800     hide : function(){
36801         //if(!this.collapsed){
36802             this.el.dom.style.left = "-2000px";
36803             this.el.hide();
36804         //}else{
36805          //   this.collapsedEl.dom.style.left = "-2000px";
36806          //   this.collapsedEl.hide();
36807        // }
36808         this.visible = false;
36809         this.fireEvent("visibilitychange", this, false);
36810     },
36811
36812     /**
36813      * Shows this region if it was previously hidden.
36814      */
36815     show : function(){
36816         //if(!this.collapsed){
36817             this.el.show();
36818         //}else{
36819         //    this.collapsedEl.show();
36820        // }
36821         this.visible = true;
36822         this.fireEvent("visibilitychange", this, true);
36823     },
36824 /*
36825     closeClicked : function(){
36826         if(this.activePanel){
36827             this.remove(this.activePanel);
36828         }
36829     },
36830
36831     collapseClick : function(e){
36832         if(this.isSlid){
36833            e.stopPropagation();
36834            this.slideIn();
36835         }else{
36836            e.stopPropagation();
36837            this.slideOut();
36838         }
36839     },
36840 */
36841     /**
36842      * Collapses this region.
36843      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36844      */
36845     /*
36846     collapse : function(skipAnim, skipCheck = false){
36847         if(this.collapsed) {
36848             return;
36849         }
36850         
36851         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36852             
36853             this.collapsed = true;
36854             if(this.split){
36855                 this.split.el.hide();
36856             }
36857             if(this.config.animate && skipAnim !== true){
36858                 this.fireEvent("invalidated", this);
36859                 this.animateCollapse();
36860             }else{
36861                 this.el.setLocation(-20000,-20000);
36862                 this.el.hide();
36863                 this.collapsedEl.show();
36864                 this.fireEvent("collapsed", this);
36865                 this.fireEvent("invalidated", this);
36866             }
36867         }
36868         
36869     },
36870 */
36871     animateCollapse : function(){
36872         // overridden
36873     },
36874
36875     /**
36876      * Expands this region if it was previously collapsed.
36877      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36878      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36879      */
36880     /*
36881     expand : function(e, skipAnim){
36882         if(e) {
36883             e.stopPropagation();
36884         }
36885         if(!this.collapsed || this.el.hasActiveFx()) {
36886             return;
36887         }
36888         if(this.isSlid){
36889             this.afterSlideIn();
36890             skipAnim = true;
36891         }
36892         this.collapsed = false;
36893         if(this.config.animate && skipAnim !== true){
36894             this.animateExpand();
36895         }else{
36896             this.el.show();
36897             if(this.split){
36898                 this.split.el.show();
36899             }
36900             this.collapsedEl.setLocation(-2000,-2000);
36901             this.collapsedEl.hide();
36902             this.fireEvent("invalidated", this);
36903             this.fireEvent("expanded", this);
36904         }
36905     },
36906 */
36907     animateExpand : function(){
36908         // overridden
36909     },
36910
36911     initTabs : function()
36912     {
36913         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36914         
36915         var ts = new Roo.bootstrap.panel.Tabs({
36916             el: this.bodyEl.dom,
36917             region : this,
36918             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
36919             disableTooltips: this.config.disableTabTips,
36920             toolbar : this.config.toolbar
36921         });
36922         
36923         if(this.config.hideTabs){
36924             ts.stripWrap.setDisplayed(false);
36925         }
36926         this.tabs = ts;
36927         ts.resizeTabs = this.config.resizeTabs === true;
36928         ts.minTabWidth = this.config.minTabWidth || 40;
36929         ts.maxTabWidth = this.config.maxTabWidth || 250;
36930         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36931         ts.monitorResize = false;
36932         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36933         ts.bodyEl.addClass('roo-layout-tabs-body');
36934         this.panels.each(this.initPanelAsTab, this);
36935     },
36936
36937     initPanelAsTab : function(panel){
36938         var ti = this.tabs.addTab(
36939             panel.getEl().id,
36940             panel.getTitle(),
36941             null,
36942             this.config.closeOnTab && panel.isClosable(),
36943             panel.tpl
36944         );
36945         if(panel.tabTip !== undefined){
36946             ti.setTooltip(panel.tabTip);
36947         }
36948         ti.on("activate", function(){
36949               this.setActivePanel(panel);
36950         }, this);
36951         
36952         if(this.config.closeOnTab){
36953             ti.on("beforeclose", function(t, e){
36954                 e.cancel = true;
36955                 this.remove(panel);
36956             }, this);
36957         }
36958         
36959         panel.tabItem = ti;
36960         
36961         return ti;
36962     },
36963
36964     updatePanelTitle : function(panel, title)
36965     {
36966         if(this.activePanel == panel){
36967             this.updateTitle(title);
36968         }
36969         if(this.tabs){
36970             var ti = this.tabs.getTab(panel.getEl().id);
36971             ti.setText(title);
36972             if(panel.tabTip !== undefined){
36973                 ti.setTooltip(panel.tabTip);
36974             }
36975         }
36976     },
36977
36978     updateTitle : function(title){
36979         if(this.titleTextEl && !this.config.title){
36980             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36981         }
36982     },
36983
36984     setActivePanel : function(panel)
36985     {
36986         panel = this.getPanel(panel);
36987         if(this.activePanel && this.activePanel != panel){
36988             if(this.activePanel.setActiveState(false) === false){
36989                 return;
36990             }
36991         }
36992         this.activePanel = panel;
36993         panel.setActiveState(true);
36994         if(this.panelSize){
36995             panel.setSize(this.panelSize.width, this.panelSize.height);
36996         }
36997         if(this.closeBtn){
36998             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36999         }
37000         this.updateTitle(panel.getTitle());
37001         if(this.tabs){
37002             this.fireEvent("invalidated", this);
37003         }
37004         this.fireEvent("panelactivated", this, panel);
37005     },
37006
37007     /**
37008      * Shows the specified panel.
37009      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
37010      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
37011      */
37012     showPanel : function(panel)
37013     {
37014         panel = this.getPanel(panel);
37015         if(panel){
37016             if(this.tabs){
37017                 var tab = this.tabs.getTab(panel.getEl().id);
37018                 if(tab.isHidden()){
37019                     this.tabs.unhideTab(tab.id);
37020                 }
37021                 tab.activate();
37022             }else{
37023                 this.setActivePanel(panel);
37024             }
37025         }
37026         return panel;
37027     },
37028
37029     /**
37030      * Get the active panel for this region.
37031      * @return {Roo.ContentPanel} The active panel or null
37032      */
37033     getActivePanel : function(){
37034         return this.activePanel;
37035     },
37036
37037     validateVisibility : function(){
37038         if(this.panels.getCount() < 1){
37039             this.updateTitle("&#160;");
37040             this.closeBtn.hide();
37041             this.hide();
37042         }else{
37043             if(!this.isVisible()){
37044                 this.show();
37045             }
37046         }
37047     },
37048
37049     /**
37050      * Adds the passed ContentPanel(s) to this region.
37051      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37052      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
37053      */
37054     add : function(panel)
37055     {
37056         if(arguments.length > 1){
37057             for(var i = 0, len = arguments.length; i < len; i++) {
37058                 this.add(arguments[i]);
37059             }
37060             return null;
37061         }
37062         
37063         // if we have not been rendered yet, then we can not really do much of this..
37064         if (!this.bodyEl) {
37065             this.unrendered_panels.push(panel);
37066             return panel;
37067         }
37068         
37069         
37070         
37071         
37072         if(this.hasPanel(panel)){
37073             this.showPanel(panel);
37074             return panel;
37075         }
37076         panel.setRegion(this);
37077         this.panels.add(panel);
37078        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
37079             // sinle panel - no tab...?? would it not be better to render it with the tabs,
37080             // and hide them... ???
37081             this.bodyEl.dom.appendChild(panel.getEl().dom);
37082             if(panel.background !== true){
37083                 this.setActivePanel(panel);
37084             }
37085             this.fireEvent("paneladded", this, panel);
37086             return panel;
37087         }
37088         */
37089         if(!this.tabs){
37090             this.initTabs();
37091         }else{
37092             this.initPanelAsTab(panel);
37093         }
37094         
37095         
37096         if(panel.background !== true){
37097             this.tabs.activate(panel.getEl().id);
37098         }
37099         this.fireEvent("paneladded", this, panel);
37100         return panel;
37101     },
37102
37103     /**
37104      * Hides the tab for the specified panel.
37105      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37106      */
37107     hidePanel : function(panel){
37108         if(this.tabs && (panel = this.getPanel(panel))){
37109             this.tabs.hideTab(panel.getEl().id);
37110         }
37111     },
37112
37113     /**
37114      * Unhides the tab for a previously hidden panel.
37115      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37116      */
37117     unhidePanel : function(panel){
37118         if(this.tabs && (panel = this.getPanel(panel))){
37119             this.tabs.unhideTab(panel.getEl().id);
37120         }
37121     },
37122
37123     clearPanels : function(){
37124         while(this.panels.getCount() > 0){
37125              this.remove(this.panels.first());
37126         }
37127     },
37128
37129     /**
37130      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37131      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37132      * @param {Boolean} preservePanel Overrides the config preservePanel option
37133      * @return {Roo.ContentPanel} The panel that was removed
37134      */
37135     remove : function(panel, preservePanel)
37136     {
37137         panel = this.getPanel(panel);
37138         if(!panel){
37139             return null;
37140         }
37141         var e = {};
37142         this.fireEvent("beforeremove", this, panel, e);
37143         if(e.cancel === true){
37144             return null;
37145         }
37146         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
37147         var panelId = panel.getId();
37148         this.panels.removeKey(panelId);
37149         if(preservePanel){
37150             document.body.appendChild(panel.getEl().dom);
37151         }
37152         if(this.tabs){
37153             this.tabs.removeTab(panel.getEl().id);
37154         }else if (!preservePanel){
37155             this.bodyEl.dom.removeChild(panel.getEl().dom);
37156         }
37157         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
37158             var p = this.panels.first();
37159             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
37160             tempEl.appendChild(p.getEl().dom);
37161             this.bodyEl.update("");
37162             this.bodyEl.dom.appendChild(p.getEl().dom);
37163             tempEl = null;
37164             this.updateTitle(p.getTitle());
37165             this.tabs = null;
37166             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
37167             this.setActivePanel(p);
37168         }
37169         panel.setRegion(null);
37170         if(this.activePanel == panel){
37171             this.activePanel = null;
37172         }
37173         if(this.config.autoDestroy !== false && preservePanel !== true){
37174             try{panel.destroy();}catch(e){}
37175         }
37176         this.fireEvent("panelremoved", this, panel);
37177         return panel;
37178     },
37179
37180     /**
37181      * Returns the TabPanel component used by this region
37182      * @return {Roo.TabPanel}
37183      */
37184     getTabs : function(){
37185         return this.tabs;
37186     },
37187
37188     createTool : function(parentEl, className){
37189         var btn = Roo.DomHelper.append(parentEl, {
37190             tag: "div",
37191             cls: "x-layout-tools-button",
37192             children: [ {
37193                 tag: "div",
37194                 cls: "roo-layout-tools-button-inner " + className,
37195                 html: "&#160;"
37196             }]
37197         }, true);
37198         btn.addClassOnOver("roo-layout-tools-button-over");
37199         return btn;
37200     }
37201 });/*
37202  * Based on:
37203  * Ext JS Library 1.1.1
37204  * Copyright(c) 2006-2007, Ext JS, LLC.
37205  *
37206  * Originally Released Under LGPL - original licence link has changed is not relivant.
37207  *
37208  * Fork - LGPL
37209  * <script type="text/javascript">
37210  */
37211  
37212
37213
37214 /**
37215  * @class Roo.SplitLayoutRegion
37216  * @extends Roo.LayoutRegion
37217  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
37218  */
37219 Roo.bootstrap.layout.Split = function(config){
37220     this.cursor = config.cursor;
37221     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
37222 };
37223
37224 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
37225 {
37226     splitTip : "Drag to resize.",
37227     collapsibleSplitTip : "Drag to resize. Double click to hide.",
37228     useSplitTips : false,
37229
37230     applyConfig : function(config){
37231         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
37232     },
37233     
37234     onRender : function(ctr,pos) {
37235         
37236         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
37237         if(!this.config.split){
37238             return;
37239         }
37240         if(!this.split){
37241             
37242             var splitEl = Roo.DomHelper.append(ctr.dom,  {
37243                             tag: "div",
37244                             id: this.el.id + "-split",
37245                             cls: "roo-layout-split roo-layout-split-"+this.position,
37246                             html: "&#160;"
37247             });
37248             /** The SplitBar for this region 
37249             * @type Roo.SplitBar */
37250             // does not exist yet...
37251             Roo.log([this.position, this.orientation]);
37252             
37253             this.split = new Roo.bootstrap.SplitBar({
37254                 dragElement : splitEl,
37255                 resizingElement: this.el,
37256                 orientation : this.orientation
37257             });
37258             
37259             this.split.on("moved", this.onSplitMove, this);
37260             this.split.useShim = this.config.useShim === true;
37261             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
37262             if(this.useSplitTips){
37263                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
37264             }
37265             //if(config.collapsible){
37266             //    this.split.el.on("dblclick", this.collapse,  this);
37267             //}
37268         }
37269         if(typeof this.config.minSize != "undefined"){
37270             this.split.minSize = this.config.minSize;
37271         }
37272         if(typeof this.config.maxSize != "undefined"){
37273             this.split.maxSize = this.config.maxSize;
37274         }
37275         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
37276             this.hideSplitter();
37277         }
37278         
37279     },
37280
37281     getHMaxSize : function(){
37282          var cmax = this.config.maxSize || 10000;
37283          var center = this.mgr.getRegion("center");
37284          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
37285     },
37286
37287     getVMaxSize : function(){
37288          var cmax = this.config.maxSize || 10000;
37289          var center = this.mgr.getRegion("center");
37290          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
37291     },
37292
37293     onSplitMove : function(split, newSize){
37294         this.fireEvent("resized", this, newSize);
37295     },
37296     
37297     /** 
37298      * Returns the {@link Roo.SplitBar} for this region.
37299      * @return {Roo.SplitBar}
37300      */
37301     getSplitBar : function(){
37302         return this.split;
37303     },
37304     
37305     hide : function(){
37306         this.hideSplitter();
37307         Roo.bootstrap.layout.Split.superclass.hide.call(this);
37308     },
37309
37310     hideSplitter : function(){
37311         if(this.split){
37312             this.split.el.setLocation(-2000,-2000);
37313             this.split.el.hide();
37314         }
37315     },
37316
37317     show : function(){
37318         if(this.split){
37319             this.split.el.show();
37320         }
37321         Roo.bootstrap.layout.Split.superclass.show.call(this);
37322     },
37323     
37324     beforeSlide: function(){
37325         if(Roo.isGecko){// firefox overflow auto bug workaround
37326             this.bodyEl.clip();
37327             if(this.tabs) {
37328                 this.tabs.bodyEl.clip();
37329             }
37330             if(this.activePanel){
37331                 this.activePanel.getEl().clip();
37332                 
37333                 if(this.activePanel.beforeSlide){
37334                     this.activePanel.beforeSlide();
37335                 }
37336             }
37337         }
37338     },
37339     
37340     afterSlide : function(){
37341         if(Roo.isGecko){// firefox overflow auto bug workaround
37342             this.bodyEl.unclip();
37343             if(this.tabs) {
37344                 this.tabs.bodyEl.unclip();
37345             }
37346             if(this.activePanel){
37347                 this.activePanel.getEl().unclip();
37348                 if(this.activePanel.afterSlide){
37349                     this.activePanel.afterSlide();
37350                 }
37351             }
37352         }
37353     },
37354
37355     initAutoHide : function(){
37356         if(this.autoHide !== false){
37357             if(!this.autoHideHd){
37358                 var st = new Roo.util.DelayedTask(this.slideIn, this);
37359                 this.autoHideHd = {
37360                     "mouseout": function(e){
37361                         if(!e.within(this.el, true)){
37362                             st.delay(500);
37363                         }
37364                     },
37365                     "mouseover" : function(e){
37366                         st.cancel();
37367                     },
37368                     scope : this
37369                 };
37370             }
37371             this.el.on(this.autoHideHd);
37372         }
37373     },
37374
37375     clearAutoHide : function(){
37376         if(this.autoHide !== false){
37377             this.el.un("mouseout", this.autoHideHd.mouseout);
37378             this.el.un("mouseover", this.autoHideHd.mouseover);
37379         }
37380     },
37381
37382     clearMonitor : function(){
37383         Roo.get(document).un("click", this.slideInIf, this);
37384     },
37385
37386     // these names are backwards but not changed for compat
37387     slideOut : function(){
37388         if(this.isSlid || this.el.hasActiveFx()){
37389             return;
37390         }
37391         this.isSlid = true;
37392         if(this.collapseBtn){
37393             this.collapseBtn.hide();
37394         }
37395         this.closeBtnState = this.closeBtn.getStyle('display');
37396         this.closeBtn.hide();
37397         if(this.stickBtn){
37398             this.stickBtn.show();
37399         }
37400         this.el.show();
37401         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
37402         this.beforeSlide();
37403         this.el.setStyle("z-index", 10001);
37404         this.el.slideIn(this.getSlideAnchor(), {
37405             callback: function(){
37406                 this.afterSlide();
37407                 this.initAutoHide();
37408                 Roo.get(document).on("click", this.slideInIf, this);
37409                 this.fireEvent("slideshow", this);
37410             },
37411             scope: this,
37412             block: true
37413         });
37414     },
37415
37416     afterSlideIn : function(){
37417         this.clearAutoHide();
37418         this.isSlid = false;
37419         this.clearMonitor();
37420         this.el.setStyle("z-index", "");
37421         if(this.collapseBtn){
37422             this.collapseBtn.show();
37423         }
37424         this.closeBtn.setStyle('display', this.closeBtnState);
37425         if(this.stickBtn){
37426             this.stickBtn.hide();
37427         }
37428         this.fireEvent("slidehide", this);
37429     },
37430
37431     slideIn : function(cb){
37432         if(!this.isSlid || this.el.hasActiveFx()){
37433             Roo.callback(cb);
37434             return;
37435         }
37436         this.isSlid = false;
37437         this.beforeSlide();
37438         this.el.slideOut(this.getSlideAnchor(), {
37439             callback: function(){
37440                 this.el.setLeftTop(-10000, -10000);
37441                 this.afterSlide();
37442                 this.afterSlideIn();
37443                 Roo.callback(cb);
37444             },
37445             scope: this,
37446             block: true
37447         });
37448     },
37449     
37450     slideInIf : function(e){
37451         if(!e.within(this.el)){
37452             this.slideIn();
37453         }
37454     },
37455
37456     animateCollapse : function(){
37457         this.beforeSlide();
37458         this.el.setStyle("z-index", 20000);
37459         var anchor = this.getSlideAnchor();
37460         this.el.slideOut(anchor, {
37461             callback : function(){
37462                 this.el.setStyle("z-index", "");
37463                 this.collapsedEl.slideIn(anchor, {duration:.3});
37464                 this.afterSlide();
37465                 this.el.setLocation(-10000,-10000);
37466                 this.el.hide();
37467                 this.fireEvent("collapsed", this);
37468             },
37469             scope: this,
37470             block: true
37471         });
37472     },
37473
37474     animateExpand : function(){
37475         this.beforeSlide();
37476         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37477         this.el.setStyle("z-index", 20000);
37478         this.collapsedEl.hide({
37479             duration:.1
37480         });
37481         this.el.slideIn(this.getSlideAnchor(), {
37482             callback : function(){
37483                 this.el.setStyle("z-index", "");
37484                 this.afterSlide();
37485                 if(this.split){
37486                     this.split.el.show();
37487                 }
37488                 this.fireEvent("invalidated", this);
37489                 this.fireEvent("expanded", this);
37490             },
37491             scope: this,
37492             block: true
37493         });
37494     },
37495
37496     anchors : {
37497         "west" : "left",
37498         "east" : "right",
37499         "north" : "top",
37500         "south" : "bottom"
37501     },
37502
37503     sanchors : {
37504         "west" : "l",
37505         "east" : "r",
37506         "north" : "t",
37507         "south" : "b"
37508     },
37509
37510     canchors : {
37511         "west" : "tl-tr",
37512         "east" : "tr-tl",
37513         "north" : "tl-bl",
37514         "south" : "bl-tl"
37515     },
37516
37517     getAnchor : function(){
37518         return this.anchors[this.position];
37519     },
37520
37521     getCollapseAnchor : function(){
37522         return this.canchors[this.position];
37523     },
37524
37525     getSlideAnchor : function(){
37526         return this.sanchors[this.position];
37527     },
37528
37529     getAlignAdj : function(){
37530         var cm = this.cmargins;
37531         switch(this.position){
37532             case "west":
37533                 return [0, 0];
37534             break;
37535             case "east":
37536                 return [0, 0];
37537             break;
37538             case "north":
37539                 return [0, 0];
37540             break;
37541             case "south":
37542                 return [0, 0];
37543             break;
37544         }
37545     },
37546
37547     getExpandAdj : function(){
37548         var c = this.collapsedEl, cm = this.cmargins;
37549         switch(this.position){
37550             case "west":
37551                 return [-(cm.right+c.getWidth()+cm.left), 0];
37552             break;
37553             case "east":
37554                 return [cm.right+c.getWidth()+cm.left, 0];
37555             break;
37556             case "north":
37557                 return [0, -(cm.top+cm.bottom+c.getHeight())];
37558             break;
37559             case "south":
37560                 return [0, cm.top+cm.bottom+c.getHeight()];
37561             break;
37562         }
37563     }
37564 });/*
37565  * Based on:
37566  * Ext JS Library 1.1.1
37567  * Copyright(c) 2006-2007, Ext JS, LLC.
37568  *
37569  * Originally Released Under LGPL - original licence link has changed is not relivant.
37570  *
37571  * Fork - LGPL
37572  * <script type="text/javascript">
37573  */
37574 /*
37575  * These classes are private internal classes
37576  */
37577 Roo.bootstrap.layout.Center = function(config){
37578     config.region = "center";
37579     Roo.bootstrap.layout.Region.call(this, config);
37580     this.visible = true;
37581     this.minWidth = config.minWidth || 20;
37582     this.minHeight = config.minHeight || 20;
37583 };
37584
37585 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37586     hide : function(){
37587         // center panel can't be hidden
37588     },
37589     
37590     show : function(){
37591         // center panel can't be hidden
37592     },
37593     
37594     getMinWidth: function(){
37595         return this.minWidth;
37596     },
37597     
37598     getMinHeight: function(){
37599         return this.minHeight;
37600     }
37601 });
37602
37603
37604
37605
37606  
37607
37608
37609
37610
37611
37612
37613 Roo.bootstrap.layout.North = function(config)
37614 {
37615     config.region = 'north';
37616     config.cursor = 'n-resize';
37617     
37618     Roo.bootstrap.layout.Split.call(this, config);
37619     
37620     
37621     if(this.split){
37622         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37623         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37624         this.split.el.addClass("roo-layout-split-v");
37625     }
37626     var size = config.initialSize || config.height;
37627     if(typeof size != "undefined"){
37628         this.el.setHeight(size);
37629     }
37630 };
37631 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37632 {
37633     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37634     
37635     
37636     
37637     getBox : function(){
37638         if(this.collapsed){
37639             return this.collapsedEl.getBox();
37640         }
37641         var box = this.el.getBox();
37642         if(this.split){
37643             box.height += this.split.el.getHeight();
37644         }
37645         return box;
37646     },
37647     
37648     updateBox : function(box){
37649         if(this.split && !this.collapsed){
37650             box.height -= this.split.el.getHeight();
37651             this.split.el.setLeft(box.x);
37652             this.split.el.setTop(box.y+box.height);
37653             this.split.el.setWidth(box.width);
37654         }
37655         if(this.collapsed){
37656             this.updateBody(box.width, null);
37657         }
37658         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37659     }
37660 });
37661
37662
37663
37664
37665
37666 Roo.bootstrap.layout.South = function(config){
37667     config.region = 'south';
37668     config.cursor = 's-resize';
37669     Roo.bootstrap.layout.Split.call(this, config);
37670     if(this.split){
37671         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37672         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37673         this.split.el.addClass("roo-layout-split-v");
37674     }
37675     var size = config.initialSize || config.height;
37676     if(typeof size != "undefined"){
37677         this.el.setHeight(size);
37678     }
37679 };
37680
37681 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37682     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37683     getBox : function(){
37684         if(this.collapsed){
37685             return this.collapsedEl.getBox();
37686         }
37687         var box = this.el.getBox();
37688         if(this.split){
37689             var sh = this.split.el.getHeight();
37690             box.height += sh;
37691             box.y -= sh;
37692         }
37693         return box;
37694     },
37695     
37696     updateBox : function(box){
37697         if(this.split && !this.collapsed){
37698             var sh = this.split.el.getHeight();
37699             box.height -= sh;
37700             box.y += sh;
37701             this.split.el.setLeft(box.x);
37702             this.split.el.setTop(box.y-sh);
37703             this.split.el.setWidth(box.width);
37704         }
37705         if(this.collapsed){
37706             this.updateBody(box.width, null);
37707         }
37708         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37709     }
37710 });
37711
37712 Roo.bootstrap.layout.East = function(config){
37713     config.region = "east";
37714     config.cursor = "e-resize";
37715     Roo.bootstrap.layout.Split.call(this, config);
37716     if(this.split){
37717         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37718         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37719         this.split.el.addClass("roo-layout-split-h");
37720     }
37721     var size = config.initialSize || config.width;
37722     if(typeof size != "undefined"){
37723         this.el.setWidth(size);
37724     }
37725 };
37726 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37727     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37728     getBox : function(){
37729         if(this.collapsed){
37730             return this.collapsedEl.getBox();
37731         }
37732         var box = this.el.getBox();
37733         if(this.split){
37734             var sw = this.split.el.getWidth();
37735             box.width += sw;
37736             box.x -= sw;
37737         }
37738         return box;
37739     },
37740
37741     updateBox : function(box){
37742         if(this.split && !this.collapsed){
37743             var sw = this.split.el.getWidth();
37744             box.width -= sw;
37745             this.split.el.setLeft(box.x);
37746             this.split.el.setTop(box.y);
37747             this.split.el.setHeight(box.height);
37748             box.x += sw;
37749         }
37750         if(this.collapsed){
37751             this.updateBody(null, box.height);
37752         }
37753         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37754     }
37755 });
37756
37757 Roo.bootstrap.layout.West = function(config){
37758     config.region = "west";
37759     config.cursor = "w-resize";
37760     
37761     Roo.bootstrap.layout.Split.call(this, config);
37762     if(this.split){
37763         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37764         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37765         this.split.el.addClass("roo-layout-split-h");
37766     }
37767     
37768 };
37769 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37770     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37771     
37772     onRender: function(ctr, pos)
37773     {
37774         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37775         var size = this.config.initialSize || this.config.width;
37776         if(typeof size != "undefined"){
37777             this.el.setWidth(size);
37778         }
37779     },
37780     
37781     getBox : function(){
37782         if(this.collapsed){
37783             return this.collapsedEl.getBox();
37784         }
37785         var box = this.el.getBox();
37786         if(this.split){
37787             box.width += this.split.el.getWidth();
37788         }
37789         return box;
37790     },
37791     
37792     updateBox : function(box){
37793         if(this.split && !this.collapsed){
37794             var sw = this.split.el.getWidth();
37795             box.width -= sw;
37796             this.split.el.setLeft(box.x+box.width);
37797             this.split.el.setTop(box.y);
37798             this.split.el.setHeight(box.height);
37799         }
37800         if(this.collapsed){
37801             this.updateBody(null, box.height);
37802         }
37803         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37804     }
37805 });Roo.namespace("Roo.bootstrap.panel");/*
37806  * Based on:
37807  * Ext JS Library 1.1.1
37808  * Copyright(c) 2006-2007, Ext JS, LLC.
37809  *
37810  * Originally Released Under LGPL - original licence link has changed is not relivant.
37811  *
37812  * Fork - LGPL
37813  * <script type="text/javascript">
37814  */
37815 /**
37816  * @class Roo.ContentPanel
37817  * @extends Roo.util.Observable
37818  * A basic ContentPanel element.
37819  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37820  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37821  * @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
37822  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37823  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37824  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37825  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37826  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37827  * @cfg {String} title          The title for this panel
37828  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37829  * @cfg {String} url            Calls {@link #setUrl} with this value
37830  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37831  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37832  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37833  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37834  * @cfg {Boolean} badges render the badges
37835
37836  * @constructor
37837  * Create a new ContentPanel.
37838  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37839  * @param {String/Object} config A string to set only the title or a config object
37840  * @param {String} content (optional) Set the HTML content for this panel
37841  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37842  */
37843 Roo.bootstrap.panel.Content = function( config){
37844     
37845     this.tpl = config.tpl || false;
37846     
37847     var el = config.el;
37848     var content = config.content;
37849
37850     if(config.autoCreate){ // xtype is available if this is called from factory
37851         el = Roo.id();
37852     }
37853     this.el = Roo.get(el);
37854     if(!this.el && config && config.autoCreate){
37855         if(typeof config.autoCreate == "object"){
37856             if(!config.autoCreate.id){
37857                 config.autoCreate.id = config.id||el;
37858             }
37859             this.el = Roo.DomHelper.append(document.body,
37860                         config.autoCreate, true);
37861         }else{
37862             var elcfg =  {   tag: "div",
37863                             cls: "roo-layout-inactive-content",
37864                             id: config.id||el
37865                             };
37866             if (config.html) {
37867                 elcfg.html = config.html;
37868                 
37869             }
37870                         
37871             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37872         }
37873     } 
37874     this.closable = false;
37875     this.loaded = false;
37876     this.active = false;
37877    
37878       
37879     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37880         
37881         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37882         
37883         this.wrapEl = this.el; //this.el.wrap();
37884         var ti = [];
37885         if (config.toolbar.items) {
37886             ti = config.toolbar.items ;
37887             delete config.toolbar.items ;
37888         }
37889         
37890         var nitems = [];
37891         this.toolbar.render(this.wrapEl, 'before');
37892         for(var i =0;i < ti.length;i++) {
37893           //  Roo.log(['add child', items[i]]);
37894             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37895         }
37896         this.toolbar.items = nitems;
37897         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37898         delete config.toolbar;
37899         
37900     }
37901     /*
37902     // xtype created footer. - not sure if will work as we normally have to render first..
37903     if (this.footer && !this.footer.el && this.footer.xtype) {
37904         if (!this.wrapEl) {
37905             this.wrapEl = this.el.wrap();
37906         }
37907     
37908         this.footer.container = this.wrapEl.createChild();
37909          
37910         this.footer = Roo.factory(this.footer, Roo);
37911         
37912     }
37913     */
37914     
37915      if(typeof config == "string"){
37916         this.title = config;
37917     }else{
37918         Roo.apply(this, config);
37919     }
37920     
37921     if(this.resizeEl){
37922         this.resizeEl = Roo.get(this.resizeEl, true);
37923     }else{
37924         this.resizeEl = this.el;
37925     }
37926     // handle view.xtype
37927     
37928  
37929     
37930     
37931     this.addEvents({
37932         /**
37933          * @event activate
37934          * Fires when this panel is activated. 
37935          * @param {Roo.ContentPanel} this
37936          */
37937         "activate" : true,
37938         /**
37939          * @event deactivate
37940          * Fires when this panel is activated. 
37941          * @param {Roo.ContentPanel} this
37942          */
37943         "deactivate" : true,
37944
37945         /**
37946          * @event resize
37947          * Fires when this panel is resized if fitToFrame is true.
37948          * @param {Roo.ContentPanel} this
37949          * @param {Number} width The width after any component adjustments
37950          * @param {Number} height The height after any component adjustments
37951          */
37952         "resize" : true,
37953         
37954          /**
37955          * @event render
37956          * Fires when this tab is created
37957          * @param {Roo.ContentPanel} this
37958          */
37959         "render" : true
37960         
37961         
37962         
37963     });
37964     
37965
37966     
37967     
37968     if(this.autoScroll){
37969         this.resizeEl.setStyle("overflow", "auto");
37970     } else {
37971         // fix randome scrolling
37972         //this.el.on('scroll', function() {
37973         //    Roo.log('fix random scolling');
37974         //    this.scrollTo('top',0); 
37975         //});
37976     }
37977     content = content || this.content;
37978     if(content){
37979         this.setContent(content);
37980     }
37981     if(config && config.url){
37982         this.setUrl(this.url, this.params, this.loadOnce);
37983     }
37984     
37985     
37986     
37987     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37988     
37989     if (this.view && typeof(this.view.xtype) != 'undefined') {
37990         this.view.el = this.el.appendChild(document.createElement("div"));
37991         this.view = Roo.factory(this.view); 
37992         this.view.render  &&  this.view.render(false, '');  
37993     }
37994     
37995     
37996     this.fireEvent('render', this);
37997 };
37998
37999 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
38000     
38001     tabTip : '',
38002     
38003     setRegion : function(region){
38004         this.region = region;
38005         this.setActiveClass(region && !this.background);
38006     },
38007     
38008     
38009     setActiveClass: function(state)
38010     {
38011         if(state){
38012            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
38013            this.el.setStyle('position','relative');
38014         }else{
38015            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
38016            this.el.setStyle('position', 'absolute');
38017         } 
38018     },
38019     
38020     /**
38021      * Returns the toolbar for this Panel if one was configured. 
38022      * @return {Roo.Toolbar} 
38023      */
38024     getToolbar : function(){
38025         return this.toolbar;
38026     },
38027     
38028     setActiveState : function(active)
38029     {
38030         this.active = active;
38031         this.setActiveClass(active);
38032         if(!active){
38033             if(this.fireEvent("deactivate", this) === false){
38034                 return false;
38035             }
38036             return true;
38037         }
38038         this.fireEvent("activate", this);
38039         return true;
38040     },
38041     /**
38042      * Updates this panel's element
38043      * @param {String} content The new content
38044      * @param {Boolean} loadScripts (optional) true to look for and process scripts
38045     */
38046     setContent : function(content, loadScripts){
38047         this.el.update(content, loadScripts);
38048     },
38049
38050     ignoreResize : function(w, h){
38051         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
38052             return true;
38053         }else{
38054             this.lastSize = {width: w, height: h};
38055             return false;
38056         }
38057     },
38058     /**
38059      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
38060      * @return {Roo.UpdateManager} The UpdateManager
38061      */
38062     getUpdateManager : function(){
38063         return this.el.getUpdateManager();
38064     },
38065      /**
38066      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
38067      * @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:
38068 <pre><code>
38069 panel.load({
38070     url: "your-url.php",
38071     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
38072     callback: yourFunction,
38073     scope: yourObject, //(optional scope)
38074     discardUrl: false,
38075     nocache: false,
38076     text: "Loading...",
38077     timeout: 30,
38078     scripts: false
38079 });
38080 </code></pre>
38081      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
38082      * 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.
38083      * @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}
38084      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
38085      * @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.
38086      * @return {Roo.ContentPanel} this
38087      */
38088     load : function(){
38089         var um = this.el.getUpdateManager();
38090         um.update.apply(um, arguments);
38091         return this;
38092     },
38093
38094
38095     /**
38096      * 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.
38097      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
38098      * @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)
38099      * @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)
38100      * @return {Roo.UpdateManager} The UpdateManager
38101      */
38102     setUrl : function(url, params, loadOnce){
38103         if(this.refreshDelegate){
38104             this.removeListener("activate", this.refreshDelegate);
38105         }
38106         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38107         this.on("activate", this.refreshDelegate);
38108         return this.el.getUpdateManager();
38109     },
38110     
38111     _handleRefresh : function(url, params, loadOnce){
38112         if(!loadOnce || !this.loaded){
38113             var updater = this.el.getUpdateManager();
38114             updater.update(url, params, this._setLoaded.createDelegate(this));
38115         }
38116     },
38117     
38118     _setLoaded : function(){
38119         this.loaded = true;
38120     }, 
38121     
38122     /**
38123      * Returns this panel's id
38124      * @return {String} 
38125      */
38126     getId : function(){
38127         return this.el.id;
38128     },
38129     
38130     /** 
38131      * Returns this panel's element - used by regiosn to add.
38132      * @return {Roo.Element} 
38133      */
38134     getEl : function(){
38135         return this.wrapEl || this.el;
38136     },
38137     
38138    
38139     
38140     adjustForComponents : function(width, height)
38141     {
38142         //Roo.log('adjustForComponents ');
38143         if(this.resizeEl != this.el){
38144             width -= this.el.getFrameWidth('lr');
38145             height -= this.el.getFrameWidth('tb');
38146         }
38147         if(this.toolbar){
38148             var te = this.toolbar.getEl();
38149             te.setWidth(width);
38150             height -= te.getHeight();
38151         }
38152         if(this.footer){
38153             var te = this.footer.getEl();
38154             te.setWidth(width);
38155             height -= te.getHeight();
38156         }
38157         
38158         
38159         if(this.adjustments){
38160             width += this.adjustments[0];
38161             height += this.adjustments[1];
38162         }
38163         return {"width": width, "height": height};
38164     },
38165     
38166     setSize : function(width, height){
38167         if(this.fitToFrame && !this.ignoreResize(width, height)){
38168             if(this.fitContainer && this.resizeEl != this.el){
38169                 this.el.setSize(width, height);
38170             }
38171             var size = this.adjustForComponents(width, height);
38172             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
38173             this.fireEvent('resize', this, size.width, size.height);
38174         }
38175     },
38176     
38177     /**
38178      * Returns this panel's title
38179      * @return {String} 
38180      */
38181     getTitle : function(){
38182         
38183         if (typeof(this.title) != 'object') {
38184             return this.title;
38185         }
38186         
38187         var t = '';
38188         for (var k in this.title) {
38189             if (!this.title.hasOwnProperty(k)) {
38190                 continue;
38191             }
38192             
38193             if (k.indexOf('-') >= 0) {
38194                 var s = k.split('-');
38195                 for (var i = 0; i<s.length; i++) {
38196                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
38197                 }
38198             } else {
38199                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
38200             }
38201         }
38202         return t;
38203     },
38204     
38205     /**
38206      * Set this panel's title
38207      * @param {String} title
38208      */
38209     setTitle : function(title){
38210         this.title = title;
38211         if(this.region){
38212             this.region.updatePanelTitle(this, title);
38213         }
38214     },
38215     
38216     /**
38217      * Returns true is this panel was configured to be closable
38218      * @return {Boolean} 
38219      */
38220     isClosable : function(){
38221         return this.closable;
38222     },
38223     
38224     beforeSlide : function(){
38225         this.el.clip();
38226         this.resizeEl.clip();
38227     },
38228     
38229     afterSlide : function(){
38230         this.el.unclip();
38231         this.resizeEl.unclip();
38232     },
38233     
38234     /**
38235      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
38236      *   Will fail silently if the {@link #setUrl} method has not been called.
38237      *   This does not activate the panel, just updates its content.
38238      */
38239     refresh : function(){
38240         if(this.refreshDelegate){
38241            this.loaded = false;
38242            this.refreshDelegate();
38243         }
38244     },
38245     
38246     /**
38247      * Destroys this panel
38248      */
38249     destroy : function(){
38250         this.el.removeAllListeners();
38251         var tempEl = document.createElement("span");
38252         tempEl.appendChild(this.el.dom);
38253         tempEl.innerHTML = "";
38254         this.el.remove();
38255         this.el = null;
38256     },
38257     
38258     /**
38259      * form - if the content panel contains a form - this is a reference to it.
38260      * @type {Roo.form.Form}
38261      */
38262     form : false,
38263     /**
38264      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
38265      *    This contains a reference to it.
38266      * @type {Roo.View}
38267      */
38268     view : false,
38269     
38270       /**
38271      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
38272      * <pre><code>
38273
38274 layout.addxtype({
38275        xtype : 'Form',
38276        items: [ .... ]
38277    }
38278 );
38279
38280 </code></pre>
38281      * @param {Object} cfg Xtype definition of item to add.
38282      */
38283     
38284     
38285     getChildContainer: function () {
38286         return this.getEl();
38287     }
38288     
38289     
38290     /*
38291         var  ret = new Roo.factory(cfg);
38292         return ret;
38293         
38294         
38295         // add form..
38296         if (cfg.xtype.match(/^Form$/)) {
38297             
38298             var el;
38299             //if (this.footer) {
38300             //    el = this.footer.container.insertSibling(false, 'before');
38301             //} else {
38302                 el = this.el.createChild();
38303             //}
38304
38305             this.form = new  Roo.form.Form(cfg);
38306             
38307             
38308             if ( this.form.allItems.length) {
38309                 this.form.render(el.dom);
38310             }
38311             return this.form;
38312         }
38313         // should only have one of theses..
38314         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
38315             // views.. should not be just added - used named prop 'view''
38316             
38317             cfg.el = this.el.appendChild(document.createElement("div"));
38318             // factory?
38319             
38320             var ret = new Roo.factory(cfg);
38321              
38322              ret.render && ret.render(false, ''); // render blank..
38323             this.view = ret;
38324             return ret;
38325         }
38326         return false;
38327     }
38328     \*/
38329 });
38330  
38331 /**
38332  * @class Roo.bootstrap.panel.Grid
38333  * @extends Roo.bootstrap.panel.Content
38334  * @constructor
38335  * Create a new GridPanel.
38336  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
38337  * @param {Object} config A the config object
38338   
38339  */
38340
38341
38342
38343 Roo.bootstrap.panel.Grid = function(config)
38344 {
38345     
38346       
38347     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
38348         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
38349
38350     config.el = this.wrapper;
38351     //this.el = this.wrapper;
38352     
38353       if (config.container) {
38354         // ctor'ed from a Border/panel.grid
38355         
38356         
38357         this.wrapper.setStyle("overflow", "hidden");
38358         this.wrapper.addClass('roo-grid-container');
38359
38360     }
38361     
38362     
38363     if(config.toolbar){
38364         var tool_el = this.wrapper.createChild();    
38365         this.toolbar = Roo.factory(config.toolbar);
38366         var ti = [];
38367         if (config.toolbar.items) {
38368             ti = config.toolbar.items ;
38369             delete config.toolbar.items ;
38370         }
38371         
38372         var nitems = [];
38373         this.toolbar.render(tool_el);
38374         for(var i =0;i < ti.length;i++) {
38375           //  Roo.log(['add child', items[i]]);
38376             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38377         }
38378         this.toolbar.items = nitems;
38379         
38380         delete config.toolbar;
38381     }
38382     
38383     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
38384     config.grid.scrollBody = true;;
38385     config.grid.monitorWindowResize = false; // turn off autosizing
38386     config.grid.autoHeight = false;
38387     config.grid.autoWidth = false;
38388     
38389     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
38390     
38391     if (config.background) {
38392         // render grid on panel activation (if panel background)
38393         this.on('activate', function(gp) {
38394             if (!gp.grid.rendered) {
38395                 gp.grid.render(this.wrapper);
38396                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
38397             }
38398         });
38399             
38400     } else {
38401         this.grid.render(this.wrapper);
38402         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
38403
38404     }
38405     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
38406     // ??? needed ??? config.el = this.wrapper;
38407     
38408     
38409     
38410   
38411     // xtype created footer. - not sure if will work as we normally have to render first..
38412     if (this.footer && !this.footer.el && this.footer.xtype) {
38413         
38414         var ctr = this.grid.getView().getFooterPanel(true);
38415         this.footer.dataSource = this.grid.dataSource;
38416         this.footer = Roo.factory(this.footer, Roo);
38417         this.footer.render(ctr);
38418         
38419     }
38420     
38421     
38422     
38423     
38424      
38425 };
38426
38427 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38428     getId : function(){
38429         return this.grid.id;
38430     },
38431     
38432     /**
38433      * Returns the grid for this panel
38434      * @return {Roo.bootstrap.Table} 
38435      */
38436     getGrid : function(){
38437         return this.grid;    
38438     },
38439     
38440     setSize : function(width, height){
38441         if(!this.ignoreResize(width, height)){
38442             var grid = this.grid;
38443             var size = this.adjustForComponents(width, height);
38444             var gridel = grid.getGridEl();
38445             gridel.setSize(size.width, size.height);
38446             /*
38447             var thd = grid.getGridEl().select('thead',true).first();
38448             var tbd = grid.getGridEl().select('tbody', true).first();
38449             if (tbd) {
38450                 tbd.setSize(width, height - thd.getHeight());
38451             }
38452             */
38453             grid.autoSize();
38454         }
38455     },
38456      
38457     
38458     
38459     beforeSlide : function(){
38460         this.grid.getView().scroller.clip();
38461     },
38462     
38463     afterSlide : function(){
38464         this.grid.getView().scroller.unclip();
38465     },
38466     
38467     destroy : function(){
38468         this.grid.destroy();
38469         delete this.grid;
38470         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
38471     }
38472 });
38473
38474 /**
38475  * @class Roo.bootstrap.panel.Nest
38476  * @extends Roo.bootstrap.panel.Content
38477  * @constructor
38478  * Create a new Panel, that can contain a layout.Border.
38479  * 
38480  * 
38481  * @param {Roo.BorderLayout} layout The layout for this panel
38482  * @param {String/Object} config A string to set only the title or a config object
38483  */
38484 Roo.bootstrap.panel.Nest = function(config)
38485 {
38486     // construct with only one argument..
38487     /* FIXME - implement nicer consturctors
38488     if (layout.layout) {
38489         config = layout;
38490         layout = config.layout;
38491         delete config.layout;
38492     }
38493     if (layout.xtype && !layout.getEl) {
38494         // then layout needs constructing..
38495         layout = Roo.factory(layout, Roo);
38496     }
38497     */
38498     
38499     config.el =  config.layout.getEl();
38500     
38501     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
38502     
38503     config.layout.monitorWindowResize = false; // turn off autosizing
38504     this.layout = config.layout;
38505     this.layout.getEl().addClass("roo-layout-nested-layout");
38506     this.layout.parent = this;
38507     
38508     
38509     
38510     
38511 };
38512
38513 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38514
38515     setSize : function(width, height){
38516         if(!this.ignoreResize(width, height)){
38517             var size = this.adjustForComponents(width, height);
38518             var el = this.layout.getEl();
38519             if (size.height < 1) {
38520                 el.setWidth(size.width);   
38521             } else {
38522                 el.setSize(size.width, size.height);
38523             }
38524             var touch = el.dom.offsetWidth;
38525             this.layout.layout();
38526             // ie requires a double layout on the first pass
38527             if(Roo.isIE && !this.initialized){
38528                 this.initialized = true;
38529                 this.layout.layout();
38530             }
38531         }
38532     },
38533     
38534     // activate all subpanels if not currently active..
38535     
38536     setActiveState : function(active){
38537         this.active = active;
38538         this.setActiveClass(active);
38539         
38540         if(!active){
38541             this.fireEvent("deactivate", this);
38542             return;
38543         }
38544         
38545         this.fireEvent("activate", this);
38546         // not sure if this should happen before or after..
38547         if (!this.layout) {
38548             return; // should not happen..
38549         }
38550         var reg = false;
38551         for (var r in this.layout.regions) {
38552             reg = this.layout.getRegion(r);
38553             if (reg.getActivePanel()) {
38554                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
38555                 reg.setActivePanel(reg.getActivePanel());
38556                 continue;
38557             }
38558             if (!reg.panels.length) {
38559                 continue;
38560             }
38561             reg.showPanel(reg.getPanel(0));
38562         }
38563         
38564         
38565         
38566         
38567     },
38568     
38569     /**
38570      * Returns the nested BorderLayout for this panel
38571      * @return {Roo.BorderLayout} 
38572      */
38573     getLayout : function(){
38574         return this.layout;
38575     },
38576     
38577      /**
38578      * Adds a xtype elements to the layout of the nested panel
38579      * <pre><code>
38580
38581 panel.addxtype({
38582        xtype : 'ContentPanel',
38583        region: 'west',
38584        items: [ .... ]
38585    }
38586 );
38587
38588 panel.addxtype({
38589         xtype : 'NestedLayoutPanel',
38590         region: 'west',
38591         layout: {
38592            center: { },
38593            west: { }   
38594         },
38595         items : [ ... list of content panels or nested layout panels.. ]
38596    }
38597 );
38598 </code></pre>
38599      * @param {Object} cfg Xtype definition of item to add.
38600      */
38601     addxtype : function(cfg) {
38602         return this.layout.addxtype(cfg);
38603     
38604     }
38605 });/*
38606  * Based on:
38607  * Ext JS Library 1.1.1
38608  * Copyright(c) 2006-2007, Ext JS, LLC.
38609  *
38610  * Originally Released Under LGPL - original licence link has changed is not relivant.
38611  *
38612  * Fork - LGPL
38613  * <script type="text/javascript">
38614  */
38615 /**
38616  * @class Roo.TabPanel
38617  * @extends Roo.util.Observable
38618  * A lightweight tab container.
38619  * <br><br>
38620  * Usage:
38621  * <pre><code>
38622 // basic tabs 1, built from existing content
38623 var tabs = new Roo.TabPanel("tabs1");
38624 tabs.addTab("script", "View Script");
38625 tabs.addTab("markup", "View Markup");
38626 tabs.activate("script");
38627
38628 // more advanced tabs, built from javascript
38629 var jtabs = new Roo.TabPanel("jtabs");
38630 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38631
38632 // set up the UpdateManager
38633 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38634 var updater = tab2.getUpdateManager();
38635 updater.setDefaultUrl("ajax1.htm");
38636 tab2.on('activate', updater.refresh, updater, true);
38637
38638 // Use setUrl for Ajax loading
38639 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38640 tab3.setUrl("ajax2.htm", null, true);
38641
38642 // Disabled tab
38643 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38644 tab4.disable();
38645
38646 jtabs.activate("jtabs-1");
38647  * </code></pre>
38648  * @constructor
38649  * Create a new TabPanel.
38650  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38651  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38652  */
38653 Roo.bootstrap.panel.Tabs = function(config){
38654     /**
38655     * The container element for this TabPanel.
38656     * @type Roo.Element
38657     */
38658     this.el = Roo.get(config.el);
38659     delete config.el;
38660     if(config){
38661         if(typeof config == "boolean"){
38662             this.tabPosition = config ? "bottom" : "top";
38663         }else{
38664             Roo.apply(this, config);
38665         }
38666     }
38667     
38668     if(this.tabPosition == "bottom"){
38669         // if tabs are at the bottom = create the body first.
38670         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38671         this.el.addClass("roo-tabs-bottom");
38672     }
38673     // next create the tabs holders
38674     
38675     if (this.tabPosition == "west"){
38676         
38677         var reg = this.region; // fake it..
38678         while (reg) {
38679             if (!reg.mgr.parent) {
38680                 break;
38681             }
38682             reg = reg.mgr.parent.region;
38683         }
38684         Roo.log("got nest?");
38685         Roo.log(reg);
38686         if (reg.mgr.getRegion('west')) {
38687             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38688             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38689             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38690             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38691             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38692         
38693             
38694         }
38695         
38696         
38697     } else {
38698      
38699         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38700         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38701         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38702         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38703     }
38704     
38705     
38706     if(Roo.isIE){
38707         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38708     }
38709     
38710     // finally - if tabs are at the top, then create the body last..
38711     if(this.tabPosition != "bottom"){
38712         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38713          * @type Roo.Element
38714          */
38715         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38716         this.el.addClass("roo-tabs-top");
38717     }
38718     this.items = [];
38719
38720     this.bodyEl.setStyle("position", "relative");
38721
38722     this.active = null;
38723     this.activateDelegate = this.activate.createDelegate(this);
38724
38725     this.addEvents({
38726         /**
38727          * @event tabchange
38728          * Fires when the active tab changes
38729          * @param {Roo.TabPanel} this
38730          * @param {Roo.TabPanelItem} activePanel The new active tab
38731          */
38732         "tabchange": true,
38733         /**
38734          * @event beforetabchange
38735          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38736          * @param {Roo.TabPanel} this
38737          * @param {Object} e Set cancel to true on this object to cancel the tab change
38738          * @param {Roo.TabPanelItem} tab The tab being changed to
38739          */
38740         "beforetabchange" : true
38741     });
38742
38743     Roo.EventManager.onWindowResize(this.onResize, this);
38744     this.cpad = this.el.getPadding("lr");
38745     this.hiddenCount = 0;
38746
38747
38748     // toolbar on the tabbar support...
38749     if (this.toolbar) {
38750         alert("no toolbar support yet");
38751         this.toolbar  = false;
38752         /*
38753         var tcfg = this.toolbar;
38754         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38755         this.toolbar = new Roo.Toolbar(tcfg);
38756         if (Roo.isSafari) {
38757             var tbl = tcfg.container.child('table', true);
38758             tbl.setAttribute('width', '100%');
38759         }
38760         */
38761         
38762     }
38763    
38764
38765
38766     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38767 };
38768
38769 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38770     /*
38771      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38772      */
38773     tabPosition : "top",
38774     /*
38775      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38776      */
38777     currentTabWidth : 0,
38778     /*
38779      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38780      */
38781     minTabWidth : 40,
38782     /*
38783      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38784      */
38785     maxTabWidth : 250,
38786     /*
38787      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38788      */
38789     preferredTabWidth : 175,
38790     /*
38791      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38792      */
38793     resizeTabs : false,
38794     /*
38795      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38796      */
38797     monitorResize : true,
38798     /*
38799      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38800      */
38801     toolbar : false,  // set by caller..
38802     
38803     region : false, /// set by caller
38804     
38805     disableTooltips : true, // not used yet...
38806
38807     /**
38808      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38809      * @param {String} id The id of the div to use <b>or create</b>
38810      * @param {String} text The text for the tab
38811      * @param {String} content (optional) Content to put in the TabPanelItem body
38812      * @param {Boolean} closable (optional) True to create a close icon on the tab
38813      * @return {Roo.TabPanelItem} The created TabPanelItem
38814      */
38815     addTab : function(id, text, content, closable, tpl)
38816     {
38817         var item = new Roo.bootstrap.panel.TabItem({
38818             panel: this,
38819             id : id,
38820             text : text,
38821             closable : closable,
38822             tpl : tpl
38823         });
38824         this.addTabItem(item);
38825         if(content){
38826             item.setContent(content);
38827         }
38828         return item;
38829     },
38830
38831     /**
38832      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38833      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38834      * @return {Roo.TabPanelItem}
38835      */
38836     getTab : function(id){
38837         return this.items[id];
38838     },
38839
38840     /**
38841      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38842      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38843      */
38844     hideTab : function(id){
38845         var t = this.items[id];
38846         if(!t.isHidden()){
38847            t.setHidden(true);
38848            this.hiddenCount++;
38849            this.autoSizeTabs();
38850         }
38851     },
38852
38853     /**
38854      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38855      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38856      */
38857     unhideTab : function(id){
38858         var t = this.items[id];
38859         if(t.isHidden()){
38860            t.setHidden(false);
38861            this.hiddenCount--;
38862            this.autoSizeTabs();
38863         }
38864     },
38865
38866     /**
38867      * Adds an existing {@link Roo.TabPanelItem}.
38868      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38869      */
38870     addTabItem : function(item)
38871     {
38872         this.items[item.id] = item;
38873         this.items.push(item);
38874         this.autoSizeTabs();
38875       //  if(this.resizeTabs){
38876     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38877   //         this.autoSizeTabs();
38878 //        }else{
38879 //            item.autoSize();
38880        // }
38881     },
38882
38883     /**
38884      * Removes a {@link Roo.TabPanelItem}.
38885      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38886      */
38887     removeTab : function(id){
38888         var items = this.items;
38889         var tab = items[id];
38890         if(!tab) { return; }
38891         var index = items.indexOf(tab);
38892         if(this.active == tab && items.length > 1){
38893             var newTab = this.getNextAvailable(index);
38894             if(newTab) {
38895                 newTab.activate();
38896             }
38897         }
38898         this.stripEl.dom.removeChild(tab.pnode.dom);
38899         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38900             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38901         }
38902         items.splice(index, 1);
38903         delete this.items[tab.id];
38904         tab.fireEvent("close", tab);
38905         tab.purgeListeners();
38906         this.autoSizeTabs();
38907     },
38908
38909     getNextAvailable : function(start){
38910         var items = this.items;
38911         var index = start;
38912         // look for a next tab that will slide over to
38913         // replace the one being removed
38914         while(index < items.length){
38915             var item = items[++index];
38916             if(item && !item.isHidden()){
38917                 return item;
38918             }
38919         }
38920         // if one isn't found select the previous tab (on the left)
38921         index = start;
38922         while(index >= 0){
38923             var item = items[--index];
38924             if(item && !item.isHidden()){
38925                 return item;
38926             }
38927         }
38928         return null;
38929     },
38930
38931     /**
38932      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38933      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38934      */
38935     disableTab : function(id){
38936         var tab = this.items[id];
38937         if(tab && this.active != tab){
38938             tab.disable();
38939         }
38940     },
38941
38942     /**
38943      * Enables a {@link Roo.TabPanelItem} that is disabled.
38944      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38945      */
38946     enableTab : function(id){
38947         var tab = this.items[id];
38948         tab.enable();
38949     },
38950
38951     /**
38952      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38953      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38954      * @return {Roo.TabPanelItem} The TabPanelItem.
38955      */
38956     activate : function(id)
38957     {
38958         //Roo.log('activite:'  + id);
38959         
38960         var tab = this.items[id];
38961         if(!tab){
38962             return null;
38963         }
38964         if(tab == this.active || tab.disabled){
38965             return tab;
38966         }
38967         var e = {};
38968         this.fireEvent("beforetabchange", this, e, tab);
38969         if(e.cancel !== true && !tab.disabled){
38970             if(this.active){
38971                 this.active.hide();
38972             }
38973             this.active = this.items[id];
38974             this.active.show();
38975             this.fireEvent("tabchange", this, this.active);
38976         }
38977         return tab;
38978     },
38979
38980     /**
38981      * Gets the active {@link Roo.TabPanelItem}.
38982      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38983      */
38984     getActiveTab : function(){
38985         return this.active;
38986     },
38987
38988     /**
38989      * Updates the tab body element to fit the height of the container element
38990      * for overflow scrolling
38991      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38992      */
38993     syncHeight : function(targetHeight){
38994         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38995         var bm = this.bodyEl.getMargins();
38996         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38997         this.bodyEl.setHeight(newHeight);
38998         return newHeight;
38999     },
39000
39001     onResize : function(){
39002         if(this.monitorResize){
39003             this.autoSizeTabs();
39004         }
39005     },
39006
39007     /**
39008      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
39009      */
39010     beginUpdate : function(){
39011         this.updating = true;
39012     },
39013
39014     /**
39015      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
39016      */
39017     endUpdate : function(){
39018         this.updating = false;
39019         this.autoSizeTabs();
39020     },
39021
39022     /**
39023      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
39024      */
39025     autoSizeTabs : function()
39026     {
39027         var count = this.items.length;
39028         var vcount = count - this.hiddenCount;
39029         
39030         if (vcount < 2) {
39031             this.stripEl.hide();
39032         } else {
39033             this.stripEl.show();
39034         }
39035         
39036         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
39037             return;
39038         }
39039         
39040         
39041         var w = Math.max(this.el.getWidth() - this.cpad, 10);
39042         var availWidth = Math.floor(w / vcount);
39043         var b = this.stripBody;
39044         if(b.getWidth() > w){
39045             var tabs = this.items;
39046             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
39047             if(availWidth < this.minTabWidth){
39048                 /*if(!this.sleft){    // incomplete scrolling code
39049                     this.createScrollButtons();
39050                 }
39051                 this.showScroll();
39052                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
39053             }
39054         }else{
39055             if(this.currentTabWidth < this.preferredTabWidth){
39056                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
39057             }
39058         }
39059     },
39060
39061     /**
39062      * Returns the number of tabs in this TabPanel.
39063      * @return {Number}
39064      */
39065      getCount : function(){
39066          return this.items.length;
39067      },
39068
39069     /**
39070      * Resizes all the tabs to the passed width
39071      * @param {Number} The new width
39072      */
39073     setTabWidth : function(width){
39074         this.currentTabWidth = width;
39075         for(var i = 0, len = this.items.length; i < len; i++) {
39076                 if(!this.items[i].isHidden()) {
39077                 this.items[i].setWidth(width);
39078             }
39079         }
39080     },
39081
39082     /**
39083      * Destroys this TabPanel
39084      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
39085      */
39086     destroy : function(removeEl){
39087         Roo.EventManager.removeResizeListener(this.onResize, this);
39088         for(var i = 0, len = this.items.length; i < len; i++){
39089             this.items[i].purgeListeners();
39090         }
39091         if(removeEl === true){
39092             this.el.update("");
39093             this.el.remove();
39094         }
39095     },
39096     
39097     createStrip : function(container)
39098     {
39099         var strip = document.createElement("nav");
39100         strip.className = Roo.bootstrap.version == 4 ?
39101             "navbar-light bg-light" : 
39102             "navbar navbar-default"; //"x-tabs-wrap";
39103         container.appendChild(strip);
39104         return strip;
39105     },
39106     
39107     createStripList : function(strip)
39108     {
39109         // div wrapper for retard IE
39110         // returns the "tr" element.
39111         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
39112         //'<div class="x-tabs-strip-wrap">'+
39113           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
39114           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
39115         return strip.firstChild; //.firstChild.firstChild.firstChild;
39116     },
39117     createBody : function(container)
39118     {
39119         var body = document.createElement("div");
39120         Roo.id(body, "tab-body");
39121         //Roo.fly(body).addClass("x-tabs-body");
39122         Roo.fly(body).addClass("tab-content");
39123         container.appendChild(body);
39124         return body;
39125     },
39126     createItemBody :function(bodyEl, id){
39127         var body = Roo.getDom(id);
39128         if(!body){
39129             body = document.createElement("div");
39130             body.id = id;
39131         }
39132         //Roo.fly(body).addClass("x-tabs-item-body");
39133         Roo.fly(body).addClass("tab-pane");
39134          bodyEl.insertBefore(body, bodyEl.firstChild);
39135         return body;
39136     },
39137     /** @private */
39138     createStripElements :  function(stripEl, text, closable, tpl)
39139     {
39140         var td = document.createElement("li"); // was td..
39141         td.className = 'nav-item';
39142         
39143         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
39144         
39145         
39146         stripEl.appendChild(td);
39147         /*if(closable){
39148             td.className = "x-tabs-closable";
39149             if(!this.closeTpl){
39150                 this.closeTpl = new Roo.Template(
39151                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39152                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
39153                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
39154                 );
39155             }
39156             var el = this.closeTpl.overwrite(td, {"text": text});
39157             var close = el.getElementsByTagName("div")[0];
39158             var inner = el.getElementsByTagName("em")[0];
39159             return {"el": el, "close": close, "inner": inner};
39160         } else {
39161         */
39162         // not sure what this is..
39163 //            if(!this.tabTpl){
39164                 //this.tabTpl = new Roo.Template(
39165                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39166                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
39167                 //);
39168 //                this.tabTpl = new Roo.Template(
39169 //                   '<a href="#">' +
39170 //                   '<span unselectable="on"' +
39171 //                            (this.disableTooltips ? '' : ' title="{text}"') +
39172 //                            ' >{text}</span></a>'
39173 //                );
39174 //                
39175 //            }
39176
39177
39178             var template = tpl || this.tabTpl || false;
39179             
39180             if(!template){
39181                 template =  new Roo.Template(
39182                         Roo.bootstrap.version == 4 ? 
39183                             (
39184                                 '<a class="nav-link" href="#" unselectable="on"' +
39185                                      (this.disableTooltips ? '' : ' title="{text}"') +
39186                                      ' >{text}</a>'
39187                             ) : (
39188                                 '<a class="nav-link" href="#">' +
39189                                 '<span unselectable="on"' +
39190                                          (this.disableTooltips ? '' : ' title="{text}"') +
39191                                     ' >{text}</span></a>'
39192                             )
39193                 );
39194             }
39195             
39196             switch (typeof(template)) {
39197                 case 'object' :
39198                     break;
39199                 case 'string' :
39200                     template = new Roo.Template(template);
39201                     break;
39202                 default :
39203                     break;
39204             }
39205             
39206             var el = template.overwrite(td, {"text": text});
39207             
39208             var inner = el.getElementsByTagName("span")[0];
39209             
39210             return {"el": el, "inner": inner};
39211             
39212     }
39213         
39214     
39215 });
39216
39217 /**
39218  * @class Roo.TabPanelItem
39219  * @extends Roo.util.Observable
39220  * Represents an individual item (tab plus body) in a TabPanel.
39221  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
39222  * @param {String} id The id of this TabPanelItem
39223  * @param {String} text The text for the tab of this TabPanelItem
39224  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
39225  */
39226 Roo.bootstrap.panel.TabItem = function(config){
39227     /**
39228      * The {@link Roo.TabPanel} this TabPanelItem belongs to
39229      * @type Roo.TabPanel
39230      */
39231     this.tabPanel = config.panel;
39232     /**
39233      * The id for this TabPanelItem
39234      * @type String
39235      */
39236     this.id = config.id;
39237     /** @private */
39238     this.disabled = false;
39239     /** @private */
39240     this.text = config.text;
39241     /** @private */
39242     this.loaded = false;
39243     this.closable = config.closable;
39244
39245     /**
39246      * The body element for this TabPanelItem.
39247      * @type Roo.Element
39248      */
39249     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
39250     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
39251     this.bodyEl.setStyle("display", "block");
39252     this.bodyEl.setStyle("zoom", "1");
39253     //this.hideAction();
39254
39255     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
39256     /** @private */
39257     this.el = Roo.get(els.el);
39258     this.inner = Roo.get(els.inner, true);
39259      this.textEl = Roo.bootstrap.version == 4 ?
39260         this.el : Roo.get(this.el.dom.firstChild, true);
39261
39262     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
39263     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
39264
39265     
39266 //    this.el.on("mousedown", this.onTabMouseDown, this);
39267     this.el.on("click", this.onTabClick, this);
39268     /** @private */
39269     if(config.closable){
39270         var c = Roo.get(els.close, true);
39271         c.dom.title = this.closeText;
39272         c.addClassOnOver("close-over");
39273         c.on("click", this.closeClick, this);
39274      }
39275
39276     this.addEvents({
39277          /**
39278          * @event activate
39279          * Fires when this tab becomes the active tab.
39280          * @param {Roo.TabPanel} tabPanel The parent TabPanel
39281          * @param {Roo.TabPanelItem} this
39282          */
39283         "activate": true,
39284         /**
39285          * @event beforeclose
39286          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
39287          * @param {Roo.TabPanelItem} this
39288          * @param {Object} e Set cancel to true on this object to cancel the close.
39289          */
39290         "beforeclose": true,
39291         /**
39292          * @event close
39293          * Fires when this tab is closed.
39294          * @param {Roo.TabPanelItem} this
39295          */
39296          "close": true,
39297         /**
39298          * @event deactivate
39299          * Fires when this tab is no longer the active tab.
39300          * @param {Roo.TabPanel} tabPanel The parent TabPanel
39301          * @param {Roo.TabPanelItem} this
39302          */
39303          "deactivate" : true
39304     });
39305     this.hidden = false;
39306
39307     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
39308 };
39309
39310 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
39311            {
39312     purgeListeners : function(){
39313        Roo.util.Observable.prototype.purgeListeners.call(this);
39314        this.el.removeAllListeners();
39315     },
39316     /**
39317      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
39318      */
39319     show : function(){
39320         this.status_node.addClass("active");
39321         this.showAction();
39322         if(Roo.isOpera){
39323             this.tabPanel.stripWrap.repaint();
39324         }
39325         this.fireEvent("activate", this.tabPanel, this);
39326     },
39327
39328     /**
39329      * Returns true if this tab is the active tab.
39330      * @return {Boolean}
39331      */
39332     isActive : function(){
39333         return this.tabPanel.getActiveTab() == this;
39334     },
39335
39336     /**
39337      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
39338      */
39339     hide : function(){
39340         this.status_node.removeClass("active");
39341         this.hideAction();
39342         this.fireEvent("deactivate", this.tabPanel, this);
39343     },
39344
39345     hideAction : function(){
39346         this.bodyEl.hide();
39347         this.bodyEl.setStyle("position", "absolute");
39348         this.bodyEl.setLeft("-20000px");
39349         this.bodyEl.setTop("-20000px");
39350     },
39351
39352     showAction : function(){
39353         this.bodyEl.setStyle("position", "relative");
39354         this.bodyEl.setTop("");
39355         this.bodyEl.setLeft("");
39356         this.bodyEl.show();
39357     },
39358
39359     /**
39360      * Set the tooltip for the tab.
39361      * @param {String} tooltip The tab's tooltip
39362      */
39363     setTooltip : function(text){
39364         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
39365             this.textEl.dom.qtip = text;
39366             this.textEl.dom.removeAttribute('title');
39367         }else{
39368             this.textEl.dom.title = text;
39369         }
39370     },
39371
39372     onTabClick : function(e){
39373         e.preventDefault();
39374         this.tabPanel.activate(this.id);
39375     },
39376
39377     onTabMouseDown : function(e){
39378         e.preventDefault();
39379         this.tabPanel.activate(this.id);
39380     },
39381 /*
39382     getWidth : function(){
39383         return this.inner.getWidth();
39384     },
39385
39386     setWidth : function(width){
39387         var iwidth = width - this.linode.getPadding("lr");
39388         this.inner.setWidth(iwidth);
39389         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
39390         this.linode.setWidth(width);
39391     },
39392 */
39393     /**
39394      * Show or hide the tab
39395      * @param {Boolean} hidden True to hide or false to show.
39396      */
39397     setHidden : function(hidden){
39398         this.hidden = hidden;
39399         this.linode.setStyle("display", hidden ? "none" : "");
39400     },
39401
39402     /**
39403      * Returns true if this tab is "hidden"
39404      * @return {Boolean}
39405      */
39406     isHidden : function(){
39407         return this.hidden;
39408     },
39409
39410     /**
39411      * Returns the text for this tab
39412      * @return {String}
39413      */
39414     getText : function(){
39415         return this.text;
39416     },
39417     /*
39418     autoSize : function(){
39419         //this.el.beginMeasure();
39420         this.textEl.setWidth(1);
39421         /*
39422          *  #2804 [new] Tabs in Roojs
39423          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
39424          */
39425         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
39426         //this.el.endMeasure();
39427     //},
39428
39429     /**
39430      * Sets the text for the tab (Note: this also sets the tooltip text)
39431      * @param {String} text The tab's text and tooltip
39432      */
39433     setText : function(text){
39434         this.text = text;
39435         this.textEl.update(text);
39436         this.setTooltip(text);
39437         //if(!this.tabPanel.resizeTabs){
39438         //    this.autoSize();
39439         //}
39440     },
39441     /**
39442      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39443      */
39444     activate : function(){
39445         this.tabPanel.activate(this.id);
39446     },
39447
39448     /**
39449      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39450      */
39451     disable : function(){
39452         if(this.tabPanel.active != this){
39453             this.disabled = true;
39454             this.status_node.addClass("disabled");
39455         }
39456     },
39457
39458     /**
39459      * Enables this TabPanelItem if it was previously disabled.
39460      */
39461     enable : function(){
39462         this.disabled = false;
39463         this.status_node.removeClass("disabled");
39464     },
39465
39466     /**
39467      * Sets the content for this TabPanelItem.
39468      * @param {String} content The content
39469      * @param {Boolean} loadScripts true to look for and load scripts
39470      */
39471     setContent : function(content, loadScripts){
39472         this.bodyEl.update(content, loadScripts);
39473     },
39474
39475     /**
39476      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
39477      * @return {Roo.UpdateManager} The UpdateManager
39478      */
39479     getUpdateManager : function(){
39480         return this.bodyEl.getUpdateManager();
39481     },
39482
39483     /**
39484      * Set a URL to be used to load the content for this TabPanelItem.
39485      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39486      * @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)
39487      * @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)
39488      * @return {Roo.UpdateManager} The UpdateManager
39489      */
39490     setUrl : function(url, params, loadOnce){
39491         if(this.refreshDelegate){
39492             this.un('activate', this.refreshDelegate);
39493         }
39494         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39495         this.on("activate", this.refreshDelegate);
39496         return this.bodyEl.getUpdateManager();
39497     },
39498
39499     /** @private */
39500     _handleRefresh : function(url, params, loadOnce){
39501         if(!loadOnce || !this.loaded){
39502             var updater = this.bodyEl.getUpdateManager();
39503             updater.update(url, params, this._setLoaded.createDelegate(this));
39504         }
39505     },
39506
39507     /**
39508      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
39509      *   Will fail silently if the setUrl method has not been called.
39510      *   This does not activate the panel, just updates its content.
39511      */
39512     refresh : function(){
39513         if(this.refreshDelegate){
39514            this.loaded = false;
39515            this.refreshDelegate();
39516         }
39517     },
39518
39519     /** @private */
39520     _setLoaded : function(){
39521         this.loaded = true;
39522     },
39523
39524     /** @private */
39525     closeClick : function(e){
39526         var o = {};
39527         e.stopEvent();
39528         this.fireEvent("beforeclose", this, o);
39529         if(o.cancel !== true){
39530             this.tabPanel.removeTab(this.id);
39531         }
39532     },
39533     /**
39534      * The text displayed in the tooltip for the close icon.
39535      * @type String
39536      */
39537     closeText : "Close this tab"
39538 });
39539 /**
39540 *    This script refer to:
39541 *    Title: International Telephone Input
39542 *    Author: Jack O'Connor
39543 *    Code version:  v12.1.12
39544 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39545 **/
39546
39547 Roo.bootstrap.PhoneInputData = function() {
39548     var d = [
39549       [
39550         "Afghanistan (‫افغانستان‬‎)",
39551         "af",
39552         "93"
39553       ],
39554       [
39555         "Albania (Shqipëri)",
39556         "al",
39557         "355"
39558       ],
39559       [
39560         "Algeria (‫الجزائر‬‎)",
39561         "dz",
39562         "213"
39563       ],
39564       [
39565         "American Samoa",
39566         "as",
39567         "1684"
39568       ],
39569       [
39570         "Andorra",
39571         "ad",
39572         "376"
39573       ],
39574       [
39575         "Angola",
39576         "ao",
39577         "244"
39578       ],
39579       [
39580         "Anguilla",
39581         "ai",
39582         "1264"
39583       ],
39584       [
39585         "Antigua and Barbuda",
39586         "ag",
39587         "1268"
39588       ],
39589       [
39590         "Argentina",
39591         "ar",
39592         "54"
39593       ],
39594       [
39595         "Armenia (Հայաստան)",
39596         "am",
39597         "374"
39598       ],
39599       [
39600         "Aruba",
39601         "aw",
39602         "297"
39603       ],
39604       [
39605         "Australia",
39606         "au",
39607         "61",
39608         0
39609       ],
39610       [
39611         "Austria (Österreich)",
39612         "at",
39613         "43"
39614       ],
39615       [
39616         "Azerbaijan (Azərbaycan)",
39617         "az",
39618         "994"
39619       ],
39620       [
39621         "Bahamas",
39622         "bs",
39623         "1242"
39624       ],
39625       [
39626         "Bahrain (‫البحرين‬‎)",
39627         "bh",
39628         "973"
39629       ],
39630       [
39631         "Bangladesh (বাংলাদেশ)",
39632         "bd",
39633         "880"
39634       ],
39635       [
39636         "Barbados",
39637         "bb",
39638         "1246"
39639       ],
39640       [
39641         "Belarus (Беларусь)",
39642         "by",
39643         "375"
39644       ],
39645       [
39646         "Belgium (België)",
39647         "be",
39648         "32"
39649       ],
39650       [
39651         "Belize",
39652         "bz",
39653         "501"
39654       ],
39655       [
39656         "Benin (Bénin)",
39657         "bj",
39658         "229"
39659       ],
39660       [
39661         "Bermuda",
39662         "bm",
39663         "1441"
39664       ],
39665       [
39666         "Bhutan (འབྲུག)",
39667         "bt",
39668         "975"
39669       ],
39670       [
39671         "Bolivia",
39672         "bo",
39673         "591"
39674       ],
39675       [
39676         "Bosnia and Herzegovina (Босна и Херцеговина)",
39677         "ba",
39678         "387"
39679       ],
39680       [
39681         "Botswana",
39682         "bw",
39683         "267"
39684       ],
39685       [
39686         "Brazil (Brasil)",
39687         "br",
39688         "55"
39689       ],
39690       [
39691         "British Indian Ocean Territory",
39692         "io",
39693         "246"
39694       ],
39695       [
39696         "British Virgin Islands",
39697         "vg",
39698         "1284"
39699       ],
39700       [
39701         "Brunei",
39702         "bn",
39703         "673"
39704       ],
39705       [
39706         "Bulgaria (България)",
39707         "bg",
39708         "359"
39709       ],
39710       [
39711         "Burkina Faso",
39712         "bf",
39713         "226"
39714       ],
39715       [
39716         "Burundi (Uburundi)",
39717         "bi",
39718         "257"
39719       ],
39720       [
39721         "Cambodia (កម្ពុជា)",
39722         "kh",
39723         "855"
39724       ],
39725       [
39726         "Cameroon (Cameroun)",
39727         "cm",
39728         "237"
39729       ],
39730       [
39731         "Canada",
39732         "ca",
39733         "1",
39734         1,
39735         ["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"]
39736       ],
39737       [
39738         "Cape Verde (Kabu Verdi)",
39739         "cv",
39740         "238"
39741       ],
39742       [
39743         "Caribbean Netherlands",
39744         "bq",
39745         "599",
39746         1
39747       ],
39748       [
39749         "Cayman Islands",
39750         "ky",
39751         "1345"
39752       ],
39753       [
39754         "Central African Republic (République centrafricaine)",
39755         "cf",
39756         "236"
39757       ],
39758       [
39759         "Chad (Tchad)",
39760         "td",
39761         "235"
39762       ],
39763       [
39764         "Chile",
39765         "cl",
39766         "56"
39767       ],
39768       [
39769         "China (中国)",
39770         "cn",
39771         "86"
39772       ],
39773       [
39774         "Christmas Island",
39775         "cx",
39776         "61",
39777         2
39778       ],
39779       [
39780         "Cocos (Keeling) Islands",
39781         "cc",
39782         "61",
39783         1
39784       ],
39785       [
39786         "Colombia",
39787         "co",
39788         "57"
39789       ],
39790       [
39791         "Comoros (‫جزر القمر‬‎)",
39792         "km",
39793         "269"
39794       ],
39795       [
39796         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39797         "cd",
39798         "243"
39799       ],
39800       [
39801         "Congo (Republic) (Congo-Brazzaville)",
39802         "cg",
39803         "242"
39804       ],
39805       [
39806         "Cook Islands",
39807         "ck",
39808         "682"
39809       ],
39810       [
39811         "Costa Rica",
39812         "cr",
39813         "506"
39814       ],
39815       [
39816         "Côte d’Ivoire",
39817         "ci",
39818         "225"
39819       ],
39820       [
39821         "Croatia (Hrvatska)",
39822         "hr",
39823         "385"
39824       ],
39825       [
39826         "Cuba",
39827         "cu",
39828         "53"
39829       ],
39830       [
39831         "Curaçao",
39832         "cw",
39833         "599",
39834         0
39835       ],
39836       [
39837         "Cyprus (Κύπρος)",
39838         "cy",
39839         "357"
39840       ],
39841       [
39842         "Czech Republic (Česká republika)",
39843         "cz",
39844         "420"
39845       ],
39846       [
39847         "Denmark (Danmark)",
39848         "dk",
39849         "45"
39850       ],
39851       [
39852         "Djibouti",
39853         "dj",
39854         "253"
39855       ],
39856       [
39857         "Dominica",
39858         "dm",
39859         "1767"
39860       ],
39861       [
39862         "Dominican Republic (República Dominicana)",
39863         "do",
39864         "1",
39865         2,
39866         ["809", "829", "849"]
39867       ],
39868       [
39869         "Ecuador",
39870         "ec",
39871         "593"
39872       ],
39873       [
39874         "Egypt (‫مصر‬‎)",
39875         "eg",
39876         "20"
39877       ],
39878       [
39879         "El Salvador",
39880         "sv",
39881         "503"
39882       ],
39883       [
39884         "Equatorial Guinea (Guinea Ecuatorial)",
39885         "gq",
39886         "240"
39887       ],
39888       [
39889         "Eritrea",
39890         "er",
39891         "291"
39892       ],
39893       [
39894         "Estonia (Eesti)",
39895         "ee",
39896         "372"
39897       ],
39898       [
39899         "Ethiopia",
39900         "et",
39901         "251"
39902       ],
39903       [
39904         "Falkland Islands (Islas Malvinas)",
39905         "fk",
39906         "500"
39907       ],
39908       [
39909         "Faroe Islands (Føroyar)",
39910         "fo",
39911         "298"
39912       ],
39913       [
39914         "Fiji",
39915         "fj",
39916         "679"
39917       ],
39918       [
39919         "Finland (Suomi)",
39920         "fi",
39921         "358",
39922         0
39923       ],
39924       [
39925         "France",
39926         "fr",
39927         "33"
39928       ],
39929       [
39930         "French Guiana (Guyane française)",
39931         "gf",
39932         "594"
39933       ],
39934       [
39935         "French Polynesia (Polynésie française)",
39936         "pf",
39937         "689"
39938       ],
39939       [
39940         "Gabon",
39941         "ga",
39942         "241"
39943       ],
39944       [
39945         "Gambia",
39946         "gm",
39947         "220"
39948       ],
39949       [
39950         "Georgia (საქართველო)",
39951         "ge",
39952         "995"
39953       ],
39954       [
39955         "Germany (Deutschland)",
39956         "de",
39957         "49"
39958       ],
39959       [
39960         "Ghana (Gaana)",
39961         "gh",
39962         "233"
39963       ],
39964       [
39965         "Gibraltar",
39966         "gi",
39967         "350"
39968       ],
39969       [
39970         "Greece (Ελλάδα)",
39971         "gr",
39972         "30"
39973       ],
39974       [
39975         "Greenland (Kalaallit Nunaat)",
39976         "gl",
39977         "299"
39978       ],
39979       [
39980         "Grenada",
39981         "gd",
39982         "1473"
39983       ],
39984       [
39985         "Guadeloupe",
39986         "gp",
39987         "590",
39988         0
39989       ],
39990       [
39991         "Guam",
39992         "gu",
39993         "1671"
39994       ],
39995       [
39996         "Guatemala",
39997         "gt",
39998         "502"
39999       ],
40000       [
40001         "Guernsey",
40002         "gg",
40003         "44",
40004         1
40005       ],
40006       [
40007         "Guinea (Guinée)",
40008         "gn",
40009         "224"
40010       ],
40011       [
40012         "Guinea-Bissau (Guiné Bissau)",
40013         "gw",
40014         "245"
40015       ],
40016       [
40017         "Guyana",
40018         "gy",
40019         "592"
40020       ],
40021       [
40022         "Haiti",
40023         "ht",
40024         "509"
40025       ],
40026       [
40027         "Honduras",
40028         "hn",
40029         "504"
40030       ],
40031       [
40032         "Hong Kong (香港)",
40033         "hk",
40034         "852"
40035       ],
40036       [
40037         "Hungary (Magyarország)",
40038         "hu",
40039         "36"
40040       ],
40041       [
40042         "Iceland (Ísland)",
40043         "is",
40044         "354"
40045       ],
40046       [
40047         "India (भारत)",
40048         "in",
40049         "91"
40050       ],
40051       [
40052         "Indonesia",
40053         "id",
40054         "62"
40055       ],
40056       [
40057         "Iran (‫ایران‬‎)",
40058         "ir",
40059         "98"
40060       ],
40061       [
40062         "Iraq (‫العراق‬‎)",
40063         "iq",
40064         "964"
40065       ],
40066       [
40067         "Ireland",
40068         "ie",
40069         "353"
40070       ],
40071       [
40072         "Isle of Man",
40073         "im",
40074         "44",
40075         2
40076       ],
40077       [
40078         "Israel (‫ישראל‬‎)",
40079         "il",
40080         "972"
40081       ],
40082       [
40083         "Italy (Italia)",
40084         "it",
40085         "39",
40086         0
40087       ],
40088       [
40089         "Jamaica",
40090         "jm",
40091         "1876"
40092       ],
40093       [
40094         "Japan (日本)",
40095         "jp",
40096         "81"
40097       ],
40098       [
40099         "Jersey",
40100         "je",
40101         "44",
40102         3
40103       ],
40104       [
40105         "Jordan (‫الأردن‬‎)",
40106         "jo",
40107         "962"
40108       ],
40109       [
40110         "Kazakhstan (Казахстан)",
40111         "kz",
40112         "7",
40113         1
40114       ],
40115       [
40116         "Kenya",
40117         "ke",
40118         "254"
40119       ],
40120       [
40121         "Kiribati",
40122         "ki",
40123         "686"
40124       ],
40125       [
40126         "Kosovo",
40127         "xk",
40128         "383"
40129       ],
40130       [
40131         "Kuwait (‫الكويت‬‎)",
40132         "kw",
40133         "965"
40134       ],
40135       [
40136         "Kyrgyzstan (Кыргызстан)",
40137         "kg",
40138         "996"
40139       ],
40140       [
40141         "Laos (ລາວ)",
40142         "la",
40143         "856"
40144       ],
40145       [
40146         "Latvia (Latvija)",
40147         "lv",
40148         "371"
40149       ],
40150       [
40151         "Lebanon (‫لبنان‬‎)",
40152         "lb",
40153         "961"
40154       ],
40155       [
40156         "Lesotho",
40157         "ls",
40158         "266"
40159       ],
40160       [
40161         "Liberia",
40162         "lr",
40163         "231"
40164       ],
40165       [
40166         "Libya (‫ليبيا‬‎)",
40167         "ly",
40168         "218"
40169       ],
40170       [
40171         "Liechtenstein",
40172         "li",
40173         "423"
40174       ],
40175       [
40176         "Lithuania (Lietuva)",
40177         "lt",
40178         "370"
40179       ],
40180       [
40181         "Luxembourg",
40182         "lu",
40183         "352"
40184       ],
40185       [
40186         "Macau (澳門)",
40187         "mo",
40188         "853"
40189       ],
40190       [
40191         "Macedonia (FYROM) (Македонија)",
40192         "mk",
40193         "389"
40194       ],
40195       [
40196         "Madagascar (Madagasikara)",
40197         "mg",
40198         "261"
40199       ],
40200       [
40201         "Malawi",
40202         "mw",
40203         "265"
40204       ],
40205       [
40206         "Malaysia",
40207         "my",
40208         "60"
40209       ],
40210       [
40211         "Maldives",
40212         "mv",
40213         "960"
40214       ],
40215       [
40216         "Mali",
40217         "ml",
40218         "223"
40219       ],
40220       [
40221         "Malta",
40222         "mt",
40223         "356"
40224       ],
40225       [
40226         "Marshall Islands",
40227         "mh",
40228         "692"
40229       ],
40230       [
40231         "Martinique",
40232         "mq",
40233         "596"
40234       ],
40235       [
40236         "Mauritania (‫موريتانيا‬‎)",
40237         "mr",
40238         "222"
40239       ],
40240       [
40241         "Mauritius (Moris)",
40242         "mu",
40243         "230"
40244       ],
40245       [
40246         "Mayotte",
40247         "yt",
40248         "262",
40249         1
40250       ],
40251       [
40252         "Mexico (México)",
40253         "mx",
40254         "52"
40255       ],
40256       [
40257         "Micronesia",
40258         "fm",
40259         "691"
40260       ],
40261       [
40262         "Moldova (Republica Moldova)",
40263         "md",
40264         "373"
40265       ],
40266       [
40267         "Monaco",
40268         "mc",
40269         "377"
40270       ],
40271       [
40272         "Mongolia (Монгол)",
40273         "mn",
40274         "976"
40275       ],
40276       [
40277         "Montenegro (Crna Gora)",
40278         "me",
40279         "382"
40280       ],
40281       [
40282         "Montserrat",
40283         "ms",
40284         "1664"
40285       ],
40286       [
40287         "Morocco (‫المغرب‬‎)",
40288         "ma",
40289         "212",
40290         0
40291       ],
40292       [
40293         "Mozambique (Moçambique)",
40294         "mz",
40295         "258"
40296       ],
40297       [
40298         "Myanmar (Burma) (မြန်မာ)",
40299         "mm",
40300         "95"
40301       ],
40302       [
40303         "Namibia (Namibië)",
40304         "na",
40305         "264"
40306       ],
40307       [
40308         "Nauru",
40309         "nr",
40310         "674"
40311       ],
40312       [
40313         "Nepal (नेपाल)",
40314         "np",
40315         "977"
40316       ],
40317       [
40318         "Netherlands (Nederland)",
40319         "nl",
40320         "31"
40321       ],
40322       [
40323         "New Caledonia (Nouvelle-Calédonie)",
40324         "nc",
40325         "687"
40326       ],
40327       [
40328         "New Zealand",
40329         "nz",
40330         "64"
40331       ],
40332       [
40333         "Nicaragua",
40334         "ni",
40335         "505"
40336       ],
40337       [
40338         "Niger (Nijar)",
40339         "ne",
40340         "227"
40341       ],
40342       [
40343         "Nigeria",
40344         "ng",
40345         "234"
40346       ],
40347       [
40348         "Niue",
40349         "nu",
40350         "683"
40351       ],
40352       [
40353         "Norfolk Island",
40354         "nf",
40355         "672"
40356       ],
40357       [
40358         "North Korea (조선 민주주의 인민 공화국)",
40359         "kp",
40360         "850"
40361       ],
40362       [
40363         "Northern Mariana Islands",
40364         "mp",
40365         "1670"
40366       ],
40367       [
40368         "Norway (Norge)",
40369         "no",
40370         "47",
40371         0
40372       ],
40373       [
40374         "Oman (‫عُمان‬‎)",
40375         "om",
40376         "968"
40377       ],
40378       [
40379         "Pakistan (‫پاکستان‬‎)",
40380         "pk",
40381         "92"
40382       ],
40383       [
40384         "Palau",
40385         "pw",
40386         "680"
40387       ],
40388       [
40389         "Palestine (‫فلسطين‬‎)",
40390         "ps",
40391         "970"
40392       ],
40393       [
40394         "Panama (Panamá)",
40395         "pa",
40396         "507"
40397       ],
40398       [
40399         "Papua New Guinea",
40400         "pg",
40401         "675"
40402       ],
40403       [
40404         "Paraguay",
40405         "py",
40406         "595"
40407       ],
40408       [
40409         "Peru (Perú)",
40410         "pe",
40411         "51"
40412       ],
40413       [
40414         "Philippines",
40415         "ph",
40416         "63"
40417       ],
40418       [
40419         "Poland (Polska)",
40420         "pl",
40421         "48"
40422       ],
40423       [
40424         "Portugal",
40425         "pt",
40426         "351"
40427       ],
40428       [
40429         "Puerto Rico",
40430         "pr",
40431         "1",
40432         3,
40433         ["787", "939"]
40434       ],
40435       [
40436         "Qatar (‫قطر‬‎)",
40437         "qa",
40438         "974"
40439       ],
40440       [
40441         "Réunion (La Réunion)",
40442         "re",
40443         "262",
40444         0
40445       ],
40446       [
40447         "Romania (România)",
40448         "ro",
40449         "40"
40450       ],
40451       [
40452         "Russia (Россия)",
40453         "ru",
40454         "7",
40455         0
40456       ],
40457       [
40458         "Rwanda",
40459         "rw",
40460         "250"
40461       ],
40462       [
40463         "Saint Barthélemy",
40464         "bl",
40465         "590",
40466         1
40467       ],
40468       [
40469         "Saint Helena",
40470         "sh",
40471         "290"
40472       ],
40473       [
40474         "Saint Kitts and Nevis",
40475         "kn",
40476         "1869"
40477       ],
40478       [
40479         "Saint Lucia",
40480         "lc",
40481         "1758"
40482       ],
40483       [
40484         "Saint Martin (Saint-Martin (partie française))",
40485         "mf",
40486         "590",
40487         2
40488       ],
40489       [
40490         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
40491         "pm",
40492         "508"
40493       ],
40494       [
40495         "Saint Vincent and the Grenadines",
40496         "vc",
40497         "1784"
40498       ],
40499       [
40500         "Samoa",
40501         "ws",
40502         "685"
40503       ],
40504       [
40505         "San Marino",
40506         "sm",
40507         "378"
40508       ],
40509       [
40510         "São Tomé and Príncipe (São Tomé e Príncipe)",
40511         "st",
40512         "239"
40513       ],
40514       [
40515         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
40516         "sa",
40517         "966"
40518       ],
40519       [
40520         "Senegal (Sénégal)",
40521         "sn",
40522         "221"
40523       ],
40524       [
40525         "Serbia (Србија)",
40526         "rs",
40527         "381"
40528       ],
40529       [
40530         "Seychelles",
40531         "sc",
40532         "248"
40533       ],
40534       [
40535         "Sierra Leone",
40536         "sl",
40537         "232"
40538       ],
40539       [
40540         "Singapore",
40541         "sg",
40542         "65"
40543       ],
40544       [
40545         "Sint Maarten",
40546         "sx",
40547         "1721"
40548       ],
40549       [
40550         "Slovakia (Slovensko)",
40551         "sk",
40552         "421"
40553       ],
40554       [
40555         "Slovenia (Slovenija)",
40556         "si",
40557         "386"
40558       ],
40559       [
40560         "Solomon Islands",
40561         "sb",
40562         "677"
40563       ],
40564       [
40565         "Somalia (Soomaaliya)",
40566         "so",
40567         "252"
40568       ],
40569       [
40570         "South Africa",
40571         "za",
40572         "27"
40573       ],
40574       [
40575         "South Korea (대한민국)",
40576         "kr",
40577         "82"
40578       ],
40579       [
40580         "South Sudan (‫جنوب السودان‬‎)",
40581         "ss",
40582         "211"
40583       ],
40584       [
40585         "Spain (España)",
40586         "es",
40587         "34"
40588       ],
40589       [
40590         "Sri Lanka (ශ්‍රී ලංකාව)",
40591         "lk",
40592         "94"
40593       ],
40594       [
40595         "Sudan (‫السودان‬‎)",
40596         "sd",
40597         "249"
40598       ],
40599       [
40600         "Suriname",
40601         "sr",
40602         "597"
40603       ],
40604       [
40605         "Svalbard and Jan Mayen",
40606         "sj",
40607         "47",
40608         1
40609       ],
40610       [
40611         "Swaziland",
40612         "sz",
40613         "268"
40614       ],
40615       [
40616         "Sweden (Sverige)",
40617         "se",
40618         "46"
40619       ],
40620       [
40621         "Switzerland (Schweiz)",
40622         "ch",
40623         "41"
40624       ],
40625       [
40626         "Syria (‫سوريا‬‎)",
40627         "sy",
40628         "963"
40629       ],
40630       [
40631         "Taiwan (台灣)",
40632         "tw",
40633         "886"
40634       ],
40635       [
40636         "Tajikistan",
40637         "tj",
40638         "992"
40639       ],
40640       [
40641         "Tanzania",
40642         "tz",
40643         "255"
40644       ],
40645       [
40646         "Thailand (ไทย)",
40647         "th",
40648         "66"
40649       ],
40650       [
40651         "Timor-Leste",
40652         "tl",
40653         "670"
40654       ],
40655       [
40656         "Togo",
40657         "tg",
40658         "228"
40659       ],
40660       [
40661         "Tokelau",
40662         "tk",
40663         "690"
40664       ],
40665       [
40666         "Tonga",
40667         "to",
40668         "676"
40669       ],
40670       [
40671         "Trinidad and Tobago",
40672         "tt",
40673         "1868"
40674       ],
40675       [
40676         "Tunisia (‫تونس‬‎)",
40677         "tn",
40678         "216"
40679       ],
40680       [
40681         "Turkey (Türkiye)",
40682         "tr",
40683         "90"
40684       ],
40685       [
40686         "Turkmenistan",
40687         "tm",
40688         "993"
40689       ],
40690       [
40691         "Turks and Caicos Islands",
40692         "tc",
40693         "1649"
40694       ],
40695       [
40696         "Tuvalu",
40697         "tv",
40698         "688"
40699       ],
40700       [
40701         "U.S. Virgin Islands",
40702         "vi",
40703         "1340"
40704       ],
40705       [
40706         "Uganda",
40707         "ug",
40708         "256"
40709       ],
40710       [
40711         "Ukraine (Україна)",
40712         "ua",
40713         "380"
40714       ],
40715       [
40716         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40717         "ae",
40718         "971"
40719       ],
40720       [
40721         "United Kingdom",
40722         "gb",
40723         "44",
40724         0
40725       ],
40726       [
40727         "United States",
40728         "us",
40729         "1",
40730         0
40731       ],
40732       [
40733         "Uruguay",
40734         "uy",
40735         "598"
40736       ],
40737       [
40738         "Uzbekistan (Oʻzbekiston)",
40739         "uz",
40740         "998"
40741       ],
40742       [
40743         "Vanuatu",
40744         "vu",
40745         "678"
40746       ],
40747       [
40748         "Vatican City (Città del Vaticano)",
40749         "va",
40750         "39",
40751         1
40752       ],
40753       [
40754         "Venezuela",
40755         "ve",
40756         "58"
40757       ],
40758       [
40759         "Vietnam (Việt Nam)",
40760         "vn",
40761         "84"
40762       ],
40763       [
40764         "Wallis and Futuna (Wallis-et-Futuna)",
40765         "wf",
40766         "681"
40767       ],
40768       [
40769         "Western Sahara (‫الصحراء الغربية‬‎)",
40770         "eh",
40771         "212",
40772         1
40773       ],
40774       [
40775         "Yemen (‫اليمن‬‎)",
40776         "ye",
40777         "967"
40778       ],
40779       [
40780         "Zambia",
40781         "zm",
40782         "260"
40783       ],
40784       [
40785         "Zimbabwe",
40786         "zw",
40787         "263"
40788       ],
40789       [
40790         "Åland Islands",
40791         "ax",
40792         "358",
40793         1
40794       ]
40795   ];
40796   
40797   return d;
40798 }/**
40799 *    This script refer to:
40800 *    Title: International Telephone Input
40801 *    Author: Jack O'Connor
40802 *    Code version:  v12.1.12
40803 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40804 **/
40805
40806 /**
40807  * @class Roo.bootstrap.PhoneInput
40808  * @extends Roo.bootstrap.TriggerField
40809  * An input with International dial-code selection
40810  
40811  * @cfg {String} defaultDialCode default '+852'
40812  * @cfg {Array} preferedCountries default []
40813   
40814  * @constructor
40815  * Create a new PhoneInput.
40816  * @param {Object} config Configuration options
40817  */
40818
40819 Roo.bootstrap.PhoneInput = function(config) {
40820     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40821 };
40822
40823 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40824         
40825         listWidth: undefined,
40826         
40827         selectedClass: 'active',
40828         
40829         invalidClass : "has-warning",
40830         
40831         validClass: 'has-success',
40832         
40833         allowed: '0123456789',
40834         
40835         max_length: 15,
40836         
40837         /**
40838          * @cfg {String} defaultDialCode The default dial code when initializing the input
40839          */
40840         defaultDialCode: '+852',
40841         
40842         /**
40843          * @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
40844          */
40845         preferedCountries: false,
40846         
40847         getAutoCreate : function()
40848         {
40849             var data = Roo.bootstrap.PhoneInputData();
40850             var align = this.labelAlign || this.parentLabelAlign();
40851             var id = Roo.id();
40852             
40853             this.allCountries = [];
40854             this.dialCodeMapping = [];
40855             
40856             for (var i = 0; i < data.length; i++) {
40857               var c = data[i];
40858               this.allCountries[i] = {
40859                 name: c[0],
40860                 iso2: c[1],
40861                 dialCode: c[2],
40862                 priority: c[3] || 0,
40863                 areaCodes: c[4] || null
40864               };
40865               this.dialCodeMapping[c[2]] = {
40866                   name: c[0],
40867                   iso2: c[1],
40868                   priority: c[3] || 0,
40869                   areaCodes: c[4] || null
40870               };
40871             }
40872             
40873             var cfg = {
40874                 cls: 'form-group',
40875                 cn: []
40876             };
40877             
40878             var input =  {
40879                 tag: 'input',
40880                 id : id,
40881                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40882                 maxlength: this.max_length,
40883                 cls : 'form-control tel-input',
40884                 autocomplete: 'new-password'
40885             };
40886             
40887             var hiddenInput = {
40888                 tag: 'input',
40889                 type: 'hidden',
40890                 cls: 'hidden-tel-input'
40891             };
40892             
40893             if (this.name) {
40894                 hiddenInput.name = this.name;
40895             }
40896             
40897             if (this.disabled) {
40898                 input.disabled = true;
40899             }
40900             
40901             var flag_container = {
40902                 tag: 'div',
40903                 cls: 'flag-box',
40904                 cn: [
40905                     {
40906                         tag: 'div',
40907                         cls: 'flag'
40908                     },
40909                     {
40910                         tag: 'div',
40911                         cls: 'caret'
40912                     }
40913                 ]
40914             };
40915             
40916             var box = {
40917                 tag: 'div',
40918                 cls: this.hasFeedback ? 'has-feedback' : '',
40919                 cn: [
40920                     hiddenInput,
40921                     input,
40922                     {
40923                         tag: 'input',
40924                         cls: 'dial-code-holder',
40925                         disabled: true
40926                     }
40927                 ]
40928             };
40929             
40930             var container = {
40931                 cls: 'roo-select2-container input-group',
40932                 cn: [
40933                     flag_container,
40934                     box
40935                 ]
40936             };
40937             
40938             if (this.fieldLabel.length) {
40939                 var indicator = {
40940                     tag: 'i',
40941                     tooltip: 'This field is required'
40942                 };
40943                 
40944                 var label = {
40945                     tag: 'label',
40946                     'for':  id,
40947                     cls: 'control-label',
40948                     cn: []
40949                 };
40950                 
40951                 var label_text = {
40952                     tag: 'span',
40953                     html: this.fieldLabel
40954                 };
40955                 
40956                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40957                 label.cn = [
40958                     indicator,
40959                     label_text
40960                 ];
40961                 
40962                 if(this.indicatorpos == 'right') {
40963                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40964                     label.cn = [
40965                         label_text,
40966                         indicator
40967                     ];
40968                 }
40969                 
40970                 if(align == 'left') {
40971                     container = {
40972                         tag: 'div',
40973                         cn: [
40974                             container
40975                         ]
40976                     };
40977                     
40978                     if(this.labelWidth > 12){
40979                         label.style = "width: " + this.labelWidth + 'px';
40980                     }
40981                     if(this.labelWidth < 13 && this.labelmd == 0){
40982                         this.labelmd = this.labelWidth;
40983                     }
40984                     if(this.labellg > 0){
40985                         label.cls += ' col-lg-' + this.labellg;
40986                         input.cls += ' col-lg-' + (12 - this.labellg);
40987                     }
40988                     if(this.labelmd > 0){
40989                         label.cls += ' col-md-' + this.labelmd;
40990                         container.cls += ' col-md-' + (12 - this.labelmd);
40991                     }
40992                     if(this.labelsm > 0){
40993                         label.cls += ' col-sm-' + this.labelsm;
40994                         container.cls += ' col-sm-' + (12 - this.labelsm);
40995                     }
40996                     if(this.labelxs > 0){
40997                         label.cls += ' col-xs-' + this.labelxs;
40998                         container.cls += ' col-xs-' + (12 - this.labelxs);
40999                     }
41000                 }
41001             }
41002             
41003             cfg.cn = [
41004                 label,
41005                 container
41006             ];
41007             
41008             var settings = this;
41009             
41010             ['xs','sm','md','lg'].map(function(size){
41011                 if (settings[size]) {
41012                     cfg.cls += ' col-' + size + '-' + settings[size];
41013                 }
41014             });
41015             
41016             this.store = new Roo.data.Store({
41017                 proxy : new Roo.data.MemoryProxy({}),
41018                 reader : new Roo.data.JsonReader({
41019                     fields : [
41020                         {
41021                             'name' : 'name',
41022                             'type' : 'string'
41023                         },
41024                         {
41025                             'name' : 'iso2',
41026                             'type' : 'string'
41027                         },
41028                         {
41029                             'name' : 'dialCode',
41030                             'type' : 'string'
41031                         },
41032                         {
41033                             'name' : 'priority',
41034                             'type' : 'string'
41035                         },
41036                         {
41037                             'name' : 'areaCodes',
41038                             'type' : 'string'
41039                         }
41040                     ]
41041                 })
41042             });
41043             
41044             if(!this.preferedCountries) {
41045                 this.preferedCountries = [
41046                     'hk',
41047                     'gb',
41048                     'us'
41049                 ];
41050             }
41051             
41052             var p = this.preferedCountries.reverse();
41053             
41054             if(p) {
41055                 for (var i = 0; i < p.length; i++) {
41056                     for (var j = 0; j < this.allCountries.length; j++) {
41057                         if(this.allCountries[j].iso2 == p[i]) {
41058                             var t = this.allCountries[j];
41059                             this.allCountries.splice(j,1);
41060                             this.allCountries.unshift(t);
41061                         }
41062                     } 
41063                 }
41064             }
41065             
41066             this.store.proxy.data = {
41067                 success: true,
41068                 data: this.allCountries
41069             };
41070             
41071             return cfg;
41072         },
41073         
41074         initEvents : function()
41075         {
41076             this.createList();
41077             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
41078             
41079             this.indicator = this.indicatorEl();
41080             this.flag = this.flagEl();
41081             this.dialCodeHolder = this.dialCodeHolderEl();
41082             
41083             this.trigger = this.el.select('div.flag-box',true).first();
41084             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41085             
41086             var _this = this;
41087             
41088             (function(){
41089                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41090                 _this.list.setWidth(lw);
41091             }).defer(100);
41092             
41093             this.list.on('mouseover', this.onViewOver, this);
41094             this.list.on('mousemove', this.onViewMove, this);
41095             this.inputEl().on("keyup", this.onKeyUp, this);
41096             this.inputEl().on("keypress", this.onKeyPress, this);
41097             
41098             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
41099
41100             this.view = new Roo.View(this.list, this.tpl, {
41101                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41102             });
41103             
41104             this.view.on('click', this.onViewClick, this);
41105             this.setValue(this.defaultDialCode);
41106         },
41107         
41108         onTriggerClick : function(e)
41109         {
41110             Roo.log('trigger click');
41111             if(this.disabled){
41112                 return;
41113             }
41114             
41115             if(this.isExpanded()){
41116                 this.collapse();
41117                 this.hasFocus = false;
41118             }else {
41119                 this.store.load({});
41120                 this.hasFocus = true;
41121                 this.expand();
41122             }
41123         },
41124         
41125         isExpanded : function()
41126         {
41127             return this.list.isVisible();
41128         },
41129         
41130         collapse : function()
41131         {
41132             if(!this.isExpanded()){
41133                 return;
41134             }
41135             this.list.hide();
41136             Roo.get(document).un('mousedown', this.collapseIf, this);
41137             Roo.get(document).un('mousewheel', this.collapseIf, this);
41138             this.fireEvent('collapse', this);
41139             this.validate();
41140         },
41141         
41142         expand : function()
41143         {
41144             Roo.log('expand');
41145
41146             if(this.isExpanded() || !this.hasFocus){
41147                 return;
41148             }
41149             
41150             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
41151             this.list.setWidth(lw);
41152             
41153             this.list.show();
41154             this.restrictHeight();
41155             
41156             Roo.get(document).on('mousedown', this.collapseIf, this);
41157             Roo.get(document).on('mousewheel', this.collapseIf, this);
41158             
41159             this.fireEvent('expand', this);
41160         },
41161         
41162         restrictHeight : function()
41163         {
41164             this.list.alignTo(this.inputEl(), this.listAlign);
41165             this.list.alignTo(this.inputEl(), this.listAlign);
41166         },
41167         
41168         onViewOver : function(e, t)
41169         {
41170             if(this.inKeyMode){
41171                 return;
41172             }
41173             var item = this.view.findItemFromChild(t);
41174             
41175             if(item){
41176                 var index = this.view.indexOf(item);
41177                 this.select(index, false);
41178             }
41179         },
41180
41181         // private
41182         onViewClick : function(view, doFocus, el, e)
41183         {
41184             var index = this.view.getSelectedIndexes()[0];
41185             
41186             var r = this.store.getAt(index);
41187             
41188             if(r){
41189                 this.onSelect(r, index);
41190             }
41191             if(doFocus !== false && !this.blockFocus){
41192                 this.inputEl().focus();
41193             }
41194         },
41195         
41196         onViewMove : function(e, t)
41197         {
41198             this.inKeyMode = false;
41199         },
41200         
41201         select : function(index, scrollIntoView)
41202         {
41203             this.selectedIndex = index;
41204             this.view.select(index);
41205             if(scrollIntoView !== false){
41206                 var el = this.view.getNode(index);
41207                 if(el){
41208                     this.list.scrollChildIntoView(el, false);
41209                 }
41210             }
41211         },
41212         
41213         createList : function()
41214         {
41215             this.list = Roo.get(document.body).createChild({
41216                 tag: 'ul',
41217                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
41218                 style: 'display:none'
41219             });
41220             
41221             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
41222         },
41223         
41224         collapseIf : function(e)
41225         {
41226             var in_combo  = e.within(this.el);
41227             var in_list =  e.within(this.list);
41228             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
41229             
41230             if (in_combo || in_list || is_list) {
41231                 return;
41232             }
41233             this.collapse();
41234         },
41235         
41236         onSelect : function(record, index)
41237         {
41238             if(this.fireEvent('beforeselect', this, record, index) !== false){
41239                 
41240                 this.setFlagClass(record.data.iso2);
41241                 this.setDialCode(record.data.dialCode);
41242                 this.hasFocus = false;
41243                 this.collapse();
41244                 this.fireEvent('select', this, record, index);
41245             }
41246         },
41247         
41248         flagEl : function()
41249         {
41250             var flag = this.el.select('div.flag',true).first();
41251             if(!flag){
41252                 return false;
41253             }
41254             return flag;
41255         },
41256         
41257         dialCodeHolderEl : function()
41258         {
41259             var d = this.el.select('input.dial-code-holder',true).first();
41260             if(!d){
41261                 return false;
41262             }
41263             return d;
41264         },
41265         
41266         setDialCode : function(v)
41267         {
41268             this.dialCodeHolder.dom.value = '+'+v;
41269         },
41270         
41271         setFlagClass : function(n)
41272         {
41273             this.flag.dom.className = 'flag '+n;
41274         },
41275         
41276         getValue : function()
41277         {
41278             var v = this.inputEl().getValue();
41279             if(this.dialCodeHolder) {
41280                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
41281             }
41282             return v;
41283         },
41284         
41285         setValue : function(v)
41286         {
41287             var d = this.getDialCode(v);
41288             
41289             //invalid dial code
41290             if(v.length == 0 || !d || d.length == 0) {
41291                 if(this.rendered){
41292                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41293                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41294                 }
41295                 return;
41296             }
41297             
41298             //valid dial code
41299             this.setFlagClass(this.dialCodeMapping[d].iso2);
41300             this.setDialCode(d);
41301             this.inputEl().dom.value = v.replace('+'+d,'');
41302             this.hiddenEl().dom.value = this.getValue();
41303             
41304             this.validate();
41305         },
41306         
41307         getDialCode : function(v)
41308         {
41309             v = v ||  '';
41310             
41311             if (v.length == 0) {
41312                 return this.dialCodeHolder.dom.value;
41313             }
41314             
41315             var dialCode = "";
41316             if (v.charAt(0) != "+") {
41317                 return false;
41318             }
41319             var numericChars = "";
41320             for (var i = 1; i < v.length; i++) {
41321               var c = v.charAt(i);
41322               if (!isNaN(c)) {
41323                 numericChars += c;
41324                 if (this.dialCodeMapping[numericChars]) {
41325                   dialCode = v.substr(1, i);
41326                 }
41327                 if (numericChars.length == 4) {
41328                   break;
41329                 }
41330               }
41331             }
41332             return dialCode;
41333         },
41334         
41335         reset : function()
41336         {
41337             this.setValue(this.defaultDialCode);
41338             this.validate();
41339         },
41340         
41341         hiddenEl : function()
41342         {
41343             return this.el.select('input.hidden-tel-input',true).first();
41344         },
41345         
41346         // after setting val
41347         onKeyUp : function(e){
41348             this.setValue(this.getValue());
41349         },
41350         
41351         onKeyPress : function(e){
41352             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
41353                 e.stopEvent();
41354             }
41355         }
41356         
41357 });
41358 /**
41359  * @class Roo.bootstrap.MoneyField
41360  * @extends Roo.bootstrap.ComboBox
41361  * Bootstrap MoneyField class
41362  * 
41363  * @constructor
41364  * Create a new MoneyField.
41365  * @param {Object} config Configuration options
41366  */
41367
41368 Roo.bootstrap.MoneyField = function(config) {
41369     
41370     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
41371     
41372 };
41373
41374 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
41375     
41376     /**
41377      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41378      */
41379     allowDecimals : true,
41380     /**
41381      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41382      */
41383     decimalSeparator : ".",
41384     /**
41385      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41386      */
41387     decimalPrecision : 0,
41388     /**
41389      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41390      */
41391     allowNegative : true,
41392     /**
41393      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41394      */
41395     allowZero: true,
41396     /**
41397      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41398      */
41399     minValue : Number.NEGATIVE_INFINITY,
41400     /**
41401      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41402      */
41403     maxValue : Number.MAX_VALUE,
41404     /**
41405      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41406      */
41407     minText : "The minimum value for this field is {0}",
41408     /**
41409      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41410      */
41411     maxText : "The maximum value for this field is {0}",
41412     /**
41413      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41414      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41415      */
41416     nanText : "{0} is not a valid number",
41417     /**
41418      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
41419      */
41420     castInt : true,
41421     /**
41422      * @cfg {String} defaults currency of the MoneyField
41423      * value should be in lkey
41424      */
41425     defaultCurrency : false,
41426     /**
41427      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41428      */
41429     thousandsDelimiter : false,
41430     /**
41431      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41432      */
41433     max_length: false,
41434     
41435     inputlg : 9,
41436     inputmd : 9,
41437     inputsm : 9,
41438     inputxs : 6,
41439     
41440     store : false,
41441     
41442     getAutoCreate : function()
41443     {
41444         var align = this.labelAlign || this.parentLabelAlign();
41445         
41446         var id = Roo.id();
41447
41448         var cfg = {
41449             cls: 'form-group',
41450             cn: []
41451         };
41452
41453         var input =  {
41454             tag: 'input',
41455             id : id,
41456             cls : 'form-control roo-money-amount-input',
41457             autocomplete: 'new-password'
41458         };
41459         
41460         var hiddenInput = {
41461             tag: 'input',
41462             type: 'hidden',
41463             id: Roo.id(),
41464             cls: 'hidden-number-input'
41465         };
41466         
41467         if(this.max_length) {
41468             input.maxlength = this.max_length; 
41469         }
41470         
41471         if (this.name) {
41472             hiddenInput.name = this.name;
41473         }
41474
41475         if (this.disabled) {
41476             input.disabled = true;
41477         }
41478
41479         var clg = 12 - this.inputlg;
41480         var cmd = 12 - this.inputmd;
41481         var csm = 12 - this.inputsm;
41482         var cxs = 12 - this.inputxs;
41483         
41484         var container = {
41485             tag : 'div',
41486             cls : 'row roo-money-field',
41487             cn : [
41488                 {
41489                     tag : 'div',
41490                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
41491                     cn : [
41492                         {
41493                             tag : 'div',
41494                             cls: 'roo-select2-container input-group',
41495                             cn: [
41496                                 {
41497                                     tag : 'input',
41498                                     cls : 'form-control roo-money-currency-input',
41499                                     autocomplete: 'new-password',
41500                                     readOnly : 1,
41501                                     name : this.currencyName
41502                                 },
41503                                 {
41504                                     tag :'span',
41505                                     cls : 'input-group-addon',
41506                                     cn : [
41507                                         {
41508                                             tag: 'span',
41509                                             cls: 'caret'
41510                                         }
41511                                     ]
41512                                 }
41513                             ]
41514                         }
41515                     ]
41516                 },
41517                 {
41518                     tag : 'div',
41519                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41520                     cn : [
41521                         {
41522                             tag: 'div',
41523                             cls: this.hasFeedback ? 'has-feedback' : '',
41524                             cn: [
41525                                 input
41526                             ]
41527                         }
41528                     ]
41529                 }
41530             ]
41531             
41532         };
41533         
41534         if (this.fieldLabel.length) {
41535             var indicator = {
41536                 tag: 'i',
41537                 tooltip: 'This field is required'
41538             };
41539
41540             var label = {
41541                 tag: 'label',
41542                 'for':  id,
41543                 cls: 'control-label',
41544                 cn: []
41545             };
41546
41547             var label_text = {
41548                 tag: 'span',
41549                 html: this.fieldLabel
41550             };
41551
41552             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41553             label.cn = [
41554                 indicator,
41555                 label_text
41556             ];
41557
41558             if(this.indicatorpos == 'right') {
41559                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41560                 label.cn = [
41561                     label_text,
41562                     indicator
41563                 ];
41564             }
41565
41566             if(align == 'left') {
41567                 container = {
41568                     tag: 'div',
41569                     cn: [
41570                         container
41571                     ]
41572                 };
41573
41574                 if(this.labelWidth > 12){
41575                     label.style = "width: " + this.labelWidth + 'px';
41576                 }
41577                 if(this.labelWidth < 13 && this.labelmd == 0){
41578                     this.labelmd = this.labelWidth;
41579                 }
41580                 if(this.labellg > 0){
41581                     label.cls += ' col-lg-' + this.labellg;
41582                     input.cls += ' col-lg-' + (12 - this.labellg);
41583                 }
41584                 if(this.labelmd > 0){
41585                     label.cls += ' col-md-' + this.labelmd;
41586                     container.cls += ' col-md-' + (12 - this.labelmd);
41587                 }
41588                 if(this.labelsm > 0){
41589                     label.cls += ' col-sm-' + this.labelsm;
41590                     container.cls += ' col-sm-' + (12 - this.labelsm);
41591                 }
41592                 if(this.labelxs > 0){
41593                     label.cls += ' col-xs-' + this.labelxs;
41594                     container.cls += ' col-xs-' + (12 - this.labelxs);
41595                 }
41596             }
41597         }
41598
41599         cfg.cn = [
41600             label,
41601             container,
41602             hiddenInput
41603         ];
41604         
41605         var settings = this;
41606
41607         ['xs','sm','md','lg'].map(function(size){
41608             if (settings[size]) {
41609                 cfg.cls += ' col-' + size + '-' + settings[size];
41610             }
41611         });
41612         
41613         return cfg;
41614     },
41615     
41616     initEvents : function()
41617     {
41618         this.indicator = this.indicatorEl();
41619         
41620         this.initCurrencyEvent();
41621         
41622         this.initNumberEvent();
41623     },
41624     
41625     initCurrencyEvent : function()
41626     {
41627         if (!this.store) {
41628             throw "can not find store for combo";
41629         }
41630         
41631         this.store = Roo.factory(this.store, Roo.data);
41632         this.store.parent = this;
41633         
41634         this.createList();
41635         
41636         this.triggerEl = this.el.select('.input-group-addon', true).first();
41637         
41638         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41639         
41640         var _this = this;
41641         
41642         (function(){
41643             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41644             _this.list.setWidth(lw);
41645         }).defer(100);
41646         
41647         this.list.on('mouseover', this.onViewOver, this);
41648         this.list.on('mousemove', this.onViewMove, this);
41649         this.list.on('scroll', this.onViewScroll, this);
41650         
41651         if(!this.tpl){
41652             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41653         }
41654         
41655         this.view = new Roo.View(this.list, this.tpl, {
41656             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41657         });
41658         
41659         this.view.on('click', this.onViewClick, this);
41660         
41661         this.store.on('beforeload', this.onBeforeLoad, this);
41662         this.store.on('load', this.onLoad, this);
41663         this.store.on('loadexception', this.onLoadException, this);
41664         
41665         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41666             "up" : function(e){
41667                 this.inKeyMode = true;
41668                 this.selectPrev();
41669             },
41670
41671             "down" : function(e){
41672                 if(!this.isExpanded()){
41673                     this.onTriggerClick();
41674                 }else{
41675                     this.inKeyMode = true;
41676                     this.selectNext();
41677                 }
41678             },
41679
41680             "enter" : function(e){
41681                 this.collapse();
41682                 
41683                 if(this.fireEvent("specialkey", this, e)){
41684                     this.onViewClick(false);
41685                 }
41686                 
41687                 return true;
41688             },
41689
41690             "esc" : function(e){
41691                 this.collapse();
41692             },
41693
41694             "tab" : function(e){
41695                 this.collapse();
41696                 
41697                 if(this.fireEvent("specialkey", this, e)){
41698                     this.onViewClick(false);
41699                 }
41700                 
41701                 return true;
41702             },
41703
41704             scope : this,
41705
41706             doRelay : function(foo, bar, hname){
41707                 if(hname == 'down' || this.scope.isExpanded()){
41708                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41709                 }
41710                 return true;
41711             },
41712
41713             forceKeyDown: true
41714         });
41715         
41716         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41717         
41718     },
41719     
41720     initNumberEvent : function(e)
41721     {
41722         this.inputEl().on("keydown" , this.fireKey,  this);
41723         this.inputEl().on("focus", this.onFocus,  this);
41724         this.inputEl().on("blur", this.onBlur,  this);
41725         
41726         this.inputEl().relayEvent('keyup', this);
41727         
41728         if(this.indicator){
41729             this.indicator.addClass('invisible');
41730         }
41731  
41732         this.originalValue = this.getValue();
41733         
41734         if(this.validationEvent == 'keyup'){
41735             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41736             this.inputEl().on('keyup', this.filterValidation, this);
41737         }
41738         else if(this.validationEvent !== false){
41739             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41740         }
41741         
41742         if(this.selectOnFocus){
41743             this.on("focus", this.preFocus, this);
41744             
41745         }
41746         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41747             this.inputEl().on("keypress", this.filterKeys, this);
41748         } else {
41749             this.inputEl().relayEvent('keypress', this);
41750         }
41751         
41752         var allowed = "0123456789";
41753         
41754         if(this.allowDecimals){
41755             allowed += this.decimalSeparator;
41756         }
41757         
41758         if(this.allowNegative){
41759             allowed += "-";
41760         }
41761         
41762         if(this.thousandsDelimiter) {
41763             allowed += ",";
41764         }
41765         
41766         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41767         
41768         var keyPress = function(e){
41769             
41770             var k = e.getKey();
41771             
41772             var c = e.getCharCode();
41773             
41774             if(
41775                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41776                     allowed.indexOf(String.fromCharCode(c)) === -1
41777             ){
41778                 e.stopEvent();
41779                 return;
41780             }
41781             
41782             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41783                 return;
41784             }
41785             
41786             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41787                 e.stopEvent();
41788             }
41789         };
41790         
41791         this.inputEl().on("keypress", keyPress, this);
41792         
41793     },
41794     
41795     onTriggerClick : function(e)
41796     {   
41797         if(this.disabled){
41798             return;
41799         }
41800         
41801         this.page = 0;
41802         this.loadNext = false;
41803         
41804         if(this.isExpanded()){
41805             this.collapse();
41806             return;
41807         }
41808         
41809         this.hasFocus = true;
41810         
41811         if(this.triggerAction == 'all') {
41812             this.doQuery(this.allQuery, true);
41813             return;
41814         }
41815         
41816         this.doQuery(this.getRawValue());
41817     },
41818     
41819     getCurrency : function()
41820     {   
41821         var v = this.currencyEl().getValue();
41822         
41823         return v;
41824     },
41825     
41826     restrictHeight : function()
41827     {
41828         this.list.alignTo(this.currencyEl(), this.listAlign);
41829         this.list.alignTo(this.currencyEl(), this.listAlign);
41830     },
41831     
41832     onViewClick : function(view, doFocus, el, e)
41833     {
41834         var index = this.view.getSelectedIndexes()[0];
41835         
41836         var r = this.store.getAt(index);
41837         
41838         if(r){
41839             this.onSelect(r, index);
41840         }
41841     },
41842     
41843     onSelect : function(record, index){
41844         
41845         if(this.fireEvent('beforeselect', this, record, index) !== false){
41846         
41847             this.setFromCurrencyData(index > -1 ? record.data : false);
41848             
41849             this.collapse();
41850             
41851             this.fireEvent('select', this, record, index);
41852         }
41853     },
41854     
41855     setFromCurrencyData : function(o)
41856     {
41857         var currency = '';
41858         
41859         this.lastCurrency = o;
41860         
41861         if (this.currencyField) {
41862             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41863         } else {
41864             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41865         }
41866         
41867         this.lastSelectionText = currency;
41868         
41869         //setting default currency
41870         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41871             this.setCurrency(this.defaultCurrency);
41872             return;
41873         }
41874         
41875         this.setCurrency(currency);
41876     },
41877     
41878     setFromData : function(o)
41879     {
41880         var c = {};
41881         
41882         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41883         
41884         this.setFromCurrencyData(c);
41885         
41886         var value = '';
41887         
41888         if (this.name) {
41889             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41890         } else {
41891             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41892         }
41893         
41894         this.setValue(value);
41895         
41896     },
41897     
41898     setCurrency : function(v)
41899     {   
41900         this.currencyValue = v;
41901         
41902         if(this.rendered){
41903             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41904             this.validate();
41905         }
41906     },
41907     
41908     setValue : function(v)
41909     {
41910         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41911         
41912         this.value = v;
41913         
41914         if(this.rendered){
41915             
41916             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41917             
41918             this.inputEl().dom.value = (v == '') ? '' :
41919                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41920             
41921             if(!this.allowZero && v === '0') {
41922                 this.hiddenEl().dom.value = '';
41923                 this.inputEl().dom.value = '';
41924             }
41925             
41926             this.validate();
41927         }
41928     },
41929     
41930     getRawValue : function()
41931     {
41932         var v = this.inputEl().getValue();
41933         
41934         return v;
41935     },
41936     
41937     getValue : function()
41938     {
41939         return this.fixPrecision(this.parseValue(this.getRawValue()));
41940     },
41941     
41942     parseValue : function(value)
41943     {
41944         if(this.thousandsDelimiter) {
41945             value += "";
41946             r = new RegExp(",", "g");
41947             value = value.replace(r, "");
41948         }
41949         
41950         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41951         return isNaN(value) ? '' : value;
41952         
41953     },
41954     
41955     fixPrecision : function(value)
41956     {
41957         if(this.thousandsDelimiter) {
41958             value += "";
41959             r = new RegExp(",", "g");
41960             value = value.replace(r, "");
41961         }
41962         
41963         var nan = isNaN(value);
41964         
41965         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41966             return nan ? '' : value;
41967         }
41968         return parseFloat(value).toFixed(this.decimalPrecision);
41969     },
41970     
41971     decimalPrecisionFcn : function(v)
41972     {
41973         return Math.floor(v);
41974     },
41975     
41976     validateValue : function(value)
41977     {
41978         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41979             return false;
41980         }
41981         
41982         var num = this.parseValue(value);
41983         
41984         if(isNaN(num)){
41985             this.markInvalid(String.format(this.nanText, value));
41986             return false;
41987         }
41988         
41989         if(num < this.minValue){
41990             this.markInvalid(String.format(this.minText, this.minValue));
41991             return false;
41992         }
41993         
41994         if(num > this.maxValue){
41995             this.markInvalid(String.format(this.maxText, this.maxValue));
41996             return false;
41997         }
41998         
41999         return true;
42000     },
42001     
42002     validate : function()
42003     {
42004         if(this.disabled || this.allowBlank){
42005             this.markValid();
42006             return true;
42007         }
42008         
42009         var currency = this.getCurrency();
42010         
42011         if(this.validateValue(this.getRawValue()) && currency.length){
42012             this.markValid();
42013             return true;
42014         }
42015         
42016         this.markInvalid();
42017         return false;
42018     },
42019     
42020     getName: function()
42021     {
42022         return this.name;
42023     },
42024     
42025     beforeBlur : function()
42026     {
42027         if(!this.castInt){
42028             return;
42029         }
42030         
42031         var v = this.parseValue(this.getRawValue());
42032         
42033         if(v || v == 0){
42034             this.setValue(v);
42035         }
42036     },
42037     
42038     onBlur : function()
42039     {
42040         this.beforeBlur();
42041         
42042         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
42043             //this.el.removeClass(this.focusClass);
42044         }
42045         
42046         this.hasFocus = false;
42047         
42048         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
42049             this.validate();
42050         }
42051         
42052         var v = this.getValue();
42053         
42054         if(String(v) !== String(this.startValue)){
42055             this.fireEvent('change', this, v, this.startValue);
42056         }
42057         
42058         this.fireEvent("blur", this);
42059     },
42060     
42061     inputEl : function()
42062     {
42063         return this.el.select('.roo-money-amount-input', true).first();
42064     },
42065     
42066     currencyEl : function()
42067     {
42068         return this.el.select('.roo-money-currency-input', true).first();
42069     },
42070     
42071     hiddenEl : function()
42072     {
42073         return this.el.select('input.hidden-number-input',true).first();
42074     }
42075     
42076 });/**
42077  * @class Roo.bootstrap.BezierSignature
42078  * @extends Roo.bootstrap.Component
42079  * Bootstrap BezierSignature class
42080  * This script refer to:
42081  *    Title: Signature Pad
42082  *    Author: szimek
42083  *    Availability: https://github.com/szimek/signature_pad
42084  *
42085  * @constructor
42086  * Create a new BezierSignature
42087  * @param {Object} config The config object
42088  */
42089
42090 Roo.bootstrap.BezierSignature = function(config){
42091     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
42092     this.addEvents({
42093         "resize" : true
42094     });
42095 };
42096
42097 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
42098 {
42099      
42100     curve_data: [],
42101     
42102     is_empty: true,
42103     
42104     mouse_btn_down: true,
42105     
42106     /**
42107      * @cfg {int} canvas height
42108      */
42109     canvas_height: '200px',
42110     
42111     /**
42112      * @cfg {float|function} Radius of a single dot.
42113      */ 
42114     dot_size: false,
42115     
42116     /**
42117      * @cfg {float} Minimum width of a line. Defaults to 0.5.
42118      */
42119     min_width: 0.5,
42120     
42121     /**
42122      * @cfg {float} Maximum width of a line. Defaults to 2.5.
42123      */
42124     max_width: 2.5,
42125     
42126     /**
42127      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
42128      */
42129     throttle: 16,
42130     
42131     /**
42132      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
42133      */
42134     min_distance: 5,
42135     
42136     /**
42137      * @cfg {string} Color used to clear the background. Can be any color format accepted by context.fillStyle. Defaults to "rgba(0,0,0,0)" (transparent black). Use a non-transparent color e.g. "rgb(255,255,255)" (opaque white) if you'd like to save signatures as JPEG images.
42138      */
42139     bg_color: 'rgba(0, 0, 0, 0)',
42140     
42141     /**
42142      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
42143      */
42144     dot_color: 'black',
42145     
42146     /**
42147      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
42148      */ 
42149     velocity_filter_weight: 0.7,
42150     
42151     /**
42152      * @cfg {function} Callback when stroke begin. 
42153      */
42154     onBegin: false,
42155     
42156     /**
42157      * @cfg {function} Callback when stroke end.
42158      */
42159     onEnd: false,
42160     
42161     getAutoCreate : function()
42162     {
42163         var cls = 'roo-signature column';
42164         
42165         if(this.cls){
42166             cls += ' ' + this.cls;
42167         }
42168         
42169         var col_sizes = [
42170             'lg',
42171             'md',
42172             'sm',
42173             'xs'
42174         ];
42175         
42176         for(var i = 0; i < col_sizes.length; i++) {
42177             if(this[col_sizes[i]]) {
42178                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
42179             }
42180         }
42181         
42182         var cfg = {
42183             tag: 'div',
42184             cls: cls,
42185             cn: [
42186                 {
42187                     tag: 'div',
42188                     cls: 'roo-signature-body',
42189                     cn: [
42190                         {
42191                             tag: 'canvas',
42192                             cls: 'roo-signature-body-canvas',
42193                             height: this.canvas_height,
42194                             width: this.canvas_width
42195                         }
42196                     ]
42197                 },
42198                 {
42199                     tag: 'input',
42200                     type: 'file',
42201                     style: 'display: none'
42202                 }
42203             ]
42204         };
42205         
42206         return cfg;
42207     },
42208     
42209     initEvents: function() 
42210     {
42211         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
42212         
42213         var canvas = this.canvasEl();
42214         
42215         // mouse && touch event swapping...
42216         canvas.dom.style.touchAction = 'none';
42217         canvas.dom.style.msTouchAction = 'none';
42218         
42219         this.mouse_btn_down = false;
42220         canvas.on('mousedown', this._handleMouseDown, this);
42221         canvas.on('mousemove', this._handleMouseMove, this);
42222         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
42223         
42224         if (window.PointerEvent) {
42225             canvas.on('pointerdown', this._handleMouseDown, this);
42226             canvas.on('pointermove', this._handleMouseMove, this);
42227             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
42228         }
42229         
42230         if ('ontouchstart' in window) {
42231             canvas.on('touchstart', this._handleTouchStart, this);
42232             canvas.on('touchmove', this._handleTouchMove, this);
42233             canvas.on('touchend', this._handleTouchEnd, this);
42234         }
42235         
42236         Roo.EventManager.onWindowResize(this.resize, this, true);
42237         
42238         // file input event
42239         this.fileEl().on('change', this.uploadImage, this);
42240         
42241         this.clear();
42242         
42243         this.resize();
42244     },
42245     
42246     resize: function(){
42247         
42248         var canvas = this.canvasEl().dom;
42249         var ctx = this.canvasElCtx();
42250         var img_data = false;
42251         
42252         if(canvas.width > 0) {
42253             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
42254         }
42255         // setting canvas width will clean img data
42256         canvas.width = 0;
42257         
42258         var style = window.getComputedStyle ? 
42259             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
42260             
42261         var padding_left = parseInt(style.paddingLeft) || 0;
42262         var padding_right = parseInt(style.paddingRight) || 0;
42263         
42264         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
42265         
42266         if(img_data) {
42267             ctx.putImageData(img_data, 0, 0);
42268         }
42269     },
42270     
42271     _handleMouseDown: function(e)
42272     {
42273         if (e.browserEvent.which === 1) {
42274             this.mouse_btn_down = true;
42275             this.strokeBegin(e);
42276         }
42277     },
42278     
42279     _handleMouseMove: function (e)
42280     {
42281         if (this.mouse_btn_down) {
42282             this.strokeMoveUpdate(e);
42283         }
42284     },
42285     
42286     _handleMouseUp: function (e)
42287     {
42288         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
42289             this.mouse_btn_down = false;
42290             this.strokeEnd(e);
42291         }
42292     },
42293     
42294     _handleTouchStart: function (e) {
42295         
42296         e.preventDefault();
42297         if (e.browserEvent.targetTouches.length === 1) {
42298             // var touch = e.browserEvent.changedTouches[0];
42299             // this.strokeBegin(touch);
42300             
42301              this.strokeBegin(e); // assume e catching the correct xy...
42302         }
42303     },
42304     
42305     _handleTouchMove: function (e) {
42306         e.preventDefault();
42307         // var touch = event.targetTouches[0];
42308         // _this._strokeMoveUpdate(touch);
42309         this.strokeMoveUpdate(e);
42310     },
42311     
42312     _handleTouchEnd: function (e) {
42313         var wasCanvasTouched = e.target === this.canvasEl().dom;
42314         if (wasCanvasTouched) {
42315             e.preventDefault();
42316             // var touch = event.changedTouches[0];
42317             // _this._strokeEnd(touch);
42318             this.strokeEnd(e);
42319         }
42320     },
42321     
42322     reset: function () {
42323         this._lastPoints = [];
42324         this._lastVelocity = 0;
42325         this._lastWidth = (this.min_width + this.max_width) / 2;
42326         this.canvasElCtx().fillStyle = this.dot_color;
42327     },
42328     
42329     strokeMoveUpdate: function(e)
42330     {
42331         this.strokeUpdate(e);
42332         
42333         if (this.throttle) {
42334             this.throttleStroke(this.strokeUpdate, this.throttle);
42335         }
42336         else {
42337             this.strokeUpdate(e);
42338         }
42339     },
42340     
42341     strokeBegin: function(e)
42342     {
42343         var newPointGroup = {
42344             color: this.dot_color,
42345             points: []
42346         };
42347         
42348         if (typeof this.onBegin === 'function') {
42349             this.onBegin(e);
42350         }
42351         
42352         this.curve_data.push(newPointGroup);
42353         this.reset();
42354         this.strokeUpdate(e);
42355     },
42356     
42357     strokeUpdate: function(e)
42358     {
42359         var rect = this.canvasEl().dom.getBoundingClientRect();
42360         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
42361         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
42362         var lastPoints = lastPointGroup.points;
42363         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
42364         var isLastPointTooClose = lastPoint
42365             ? point.distanceTo(lastPoint) <= this.min_distance
42366             : false;
42367         var color = lastPointGroup.color;
42368         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
42369             var curve = this.addPoint(point);
42370             if (!lastPoint) {
42371                 this.drawDot({color: color, point: point});
42372             }
42373             else if (curve) {
42374                 this.drawCurve({color: color, curve: curve});
42375             }
42376             lastPoints.push({
42377                 time: point.time,
42378                 x: point.x,
42379                 y: point.y
42380             });
42381         }
42382     },
42383     
42384     strokeEnd: function(e)
42385     {
42386         this.strokeUpdate(e);
42387         if (typeof this.onEnd === 'function') {
42388             this.onEnd(e);
42389         }
42390     },
42391     
42392     addPoint:  function (point) {
42393         var _lastPoints = this._lastPoints;
42394         _lastPoints.push(point);
42395         if (_lastPoints.length > 2) {
42396             if (_lastPoints.length === 3) {
42397                 _lastPoints.unshift(_lastPoints[0]);
42398             }
42399             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
42400             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
42401             _lastPoints.shift();
42402             return curve;
42403         }
42404         return null;
42405     },
42406     
42407     calculateCurveWidths: function (startPoint, endPoint) {
42408         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
42409             (1 - this.velocity_filter_weight) * this._lastVelocity;
42410
42411         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
42412         var widths = {
42413             end: newWidth,
42414             start: this._lastWidth
42415         };
42416         
42417         this._lastVelocity = velocity;
42418         this._lastWidth = newWidth;
42419         return widths;
42420     },
42421     
42422     drawDot: function (_a) {
42423         var color = _a.color, point = _a.point;
42424         var ctx = this.canvasElCtx();
42425         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
42426         ctx.beginPath();
42427         this.drawCurveSegment(point.x, point.y, width);
42428         ctx.closePath();
42429         ctx.fillStyle = color;
42430         ctx.fill();
42431     },
42432     
42433     drawCurve: function (_a) {
42434         var color = _a.color, curve = _a.curve;
42435         var ctx = this.canvasElCtx();
42436         var widthDelta = curve.endWidth - curve.startWidth;
42437         var drawSteps = Math.floor(curve.length()) * 2;
42438         ctx.beginPath();
42439         ctx.fillStyle = color;
42440         for (var i = 0; i < drawSteps; i += 1) {
42441         var t = i / drawSteps;
42442         var tt = t * t;
42443         var ttt = tt * t;
42444         var u = 1 - t;
42445         var uu = u * u;
42446         var uuu = uu * u;
42447         var x = uuu * curve.startPoint.x;
42448         x += 3 * uu * t * curve.control1.x;
42449         x += 3 * u * tt * curve.control2.x;
42450         x += ttt * curve.endPoint.x;
42451         var y = uuu * curve.startPoint.y;
42452         y += 3 * uu * t * curve.control1.y;
42453         y += 3 * u * tt * curve.control2.y;
42454         y += ttt * curve.endPoint.y;
42455         var width = curve.startWidth + ttt * widthDelta;
42456         this.drawCurveSegment(x, y, width);
42457         }
42458         ctx.closePath();
42459         ctx.fill();
42460     },
42461     
42462     drawCurveSegment: function (x, y, width) {
42463         var ctx = this.canvasElCtx();
42464         ctx.moveTo(x, y);
42465         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
42466         this.is_empty = false;
42467     },
42468     
42469     clear: function()
42470     {
42471         var ctx = this.canvasElCtx();
42472         var canvas = this.canvasEl().dom;
42473         ctx.fillStyle = this.bg_color;
42474         ctx.clearRect(0, 0, canvas.width, canvas.height);
42475         ctx.fillRect(0, 0, canvas.width, canvas.height);
42476         this.curve_data = [];
42477         this.reset();
42478         this.is_empty = true;
42479     },
42480     
42481     fileEl: function()
42482     {
42483         return  this.el.select('input',true).first();
42484     },
42485     
42486     canvasEl: function()
42487     {
42488         return this.el.select('canvas',true).first();
42489     },
42490     
42491     canvasElCtx: function()
42492     {
42493         return this.el.select('canvas',true).first().dom.getContext('2d');
42494     },
42495     
42496     getImage: function(type)
42497     {
42498         if(this.is_empty) {
42499             return false;
42500         }
42501         
42502         // encryption ?
42503         return this.canvasEl().dom.toDataURL('image/'+type, 1);
42504     },
42505     
42506     drawFromImage: function(img_src)
42507     {
42508         var img = new Image();
42509         
42510         img.onload = function(){
42511             this.canvasElCtx().drawImage(img, 0, 0);
42512         }.bind(this);
42513         
42514         img.src = img_src;
42515         
42516         this.is_empty = false;
42517     },
42518     
42519     selectImage: function()
42520     {
42521         this.fileEl().dom.click();
42522     },
42523     
42524     uploadImage: function(e)
42525     {
42526         var reader = new FileReader();
42527         
42528         reader.onload = function(e){
42529             var img = new Image();
42530             img.onload = function(){
42531                 this.reset();
42532                 this.canvasElCtx().drawImage(img, 0, 0);
42533             }.bind(this);
42534             img.src = e.target.result;
42535         }.bind(this);
42536         
42537         reader.readAsDataURL(e.target.files[0]);
42538     },
42539     
42540     // Bezier Point Constructor
42541     Point: (function () {
42542         function Point(x, y, time) {
42543             this.x = x;
42544             this.y = y;
42545             this.time = time || Date.now();
42546         }
42547         Point.prototype.distanceTo = function (start) {
42548             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42549         };
42550         Point.prototype.equals = function (other) {
42551             return this.x === other.x && this.y === other.y && this.time === other.time;
42552         };
42553         Point.prototype.velocityFrom = function (start) {
42554             return this.time !== start.time
42555             ? this.distanceTo(start) / (this.time - start.time)
42556             : 0;
42557         };
42558         return Point;
42559     }()),
42560     
42561     
42562     // Bezier Constructor
42563     Bezier: (function () {
42564         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42565             this.startPoint = startPoint;
42566             this.control2 = control2;
42567             this.control1 = control1;
42568             this.endPoint = endPoint;
42569             this.startWidth = startWidth;
42570             this.endWidth = endWidth;
42571         }
42572         Bezier.fromPoints = function (points, widths, scope) {
42573             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42574             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42575             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42576         };
42577         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42578             var dx1 = s1.x - s2.x;
42579             var dy1 = s1.y - s2.y;
42580             var dx2 = s2.x - s3.x;
42581             var dy2 = s2.y - s3.y;
42582             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42583             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42584             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42585             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42586             var dxm = m1.x - m2.x;
42587             var dym = m1.y - m2.y;
42588             var k = l2 / (l1 + l2);
42589             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42590             var tx = s2.x - cm.x;
42591             var ty = s2.y - cm.y;
42592             return {
42593                 c1: new scope.Point(m1.x + tx, m1.y + ty),
42594                 c2: new scope.Point(m2.x + tx, m2.y + ty)
42595             };
42596         };
42597         Bezier.prototype.length = function () {
42598             var steps = 10;
42599             var length = 0;
42600             var px;
42601             var py;
42602             for (var i = 0; i <= steps; i += 1) {
42603                 var t = i / steps;
42604                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42605                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42606                 if (i > 0) {
42607                     var xdiff = cx - px;
42608                     var ydiff = cy - py;
42609                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42610                 }
42611                 px = cx;
42612                 py = cy;
42613             }
42614             return length;
42615         };
42616         Bezier.prototype.point = function (t, start, c1, c2, end) {
42617             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42618             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42619             + (3.0 * c2 * (1.0 - t) * t * t)
42620             + (end * t * t * t);
42621         };
42622         return Bezier;
42623     }()),
42624     
42625     throttleStroke: function(fn, wait) {
42626       if (wait === void 0) { wait = 250; }
42627       var previous = 0;
42628       var timeout = null;
42629       var result;
42630       var storedContext;
42631       var storedArgs;
42632       var later = function () {
42633           previous = Date.now();
42634           timeout = null;
42635           result = fn.apply(storedContext, storedArgs);
42636           if (!timeout) {
42637               storedContext = null;
42638               storedArgs = [];
42639           }
42640       };
42641       return function wrapper() {
42642           var args = [];
42643           for (var _i = 0; _i < arguments.length; _i++) {
42644               args[_i] = arguments[_i];
42645           }
42646           var now = Date.now();
42647           var remaining = wait - (now - previous);
42648           storedContext = this;
42649           storedArgs = args;
42650           if (remaining <= 0 || remaining > wait) {
42651               if (timeout) {
42652                   clearTimeout(timeout);
42653                   timeout = null;
42654               }
42655               previous = now;
42656               result = fn.apply(storedContext, storedArgs);
42657               if (!timeout) {
42658                   storedContext = null;
42659                   storedArgs = [];
42660               }
42661           }
42662           else if (!timeout) {
42663               timeout = window.setTimeout(later, remaining);
42664           }
42665           return result;
42666       };
42667   }
42668   
42669 });
42670
42671  
42672
42673