sync
[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  * dropable area
531  * 
532  */
533
534 /**
535  * @class Roo.bootstrap.DropTarget
536  * @extends Roo.bootstrap.Element
537  * Bootstrap DropTarget class
538  
539  * @cfg {string} name dropable name
540  * 
541  * @constructor
542  * Create a new Dropable Area
543  * @param {Object} config The config object
544  */
545
546 Roo.bootstrap.DropTarget = function(config){
547     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
548     
549     this.addEvents({
550         // raw events
551         /**
552          * @event click
553          * When a element is chick
554          * @param {Roo.bootstrap.Element} this
555          * @param {Roo.EventObject} e
556          */
557         "drop" : true
558     });
559 };
560
561 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
562     
563     
564     getAutoCreate : function(){
565         
566          
567     },
568     
569     initEvents: function() 
570     {
571         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
572         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
573             ddGroup: this.name,
574             listeners : {
575                 drop : this.dragDrop.createDelegate(this),
576                 enter : this.dragEnter.createDelegate(this),
577                 out : this.dragOut.createDelegate(this),
578                 over : this.dragOver.createDelegate(this)
579             }
580             
581         });
582         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
583     },
584     
585     dragDrop : function(source,e,data)
586     {
587         // user has to decide how to impliment this.
588         Roo.log('drop');
589         Roo.log(this);
590         //this.fireEvent('drop', this, source, e ,data);
591         return false;
592     },
593     
594     dragEnter : function(n, dd, e, data)
595     {
596         // probably want to resize the element to match the dropped element..
597         Roo.log("enter");
598         this.originalSize = this.el.getSize();
599         this.el.setSize( n.el.getSize());
600         this.dropZone.DDM.refreshCache(this.name);
601         Roo.log([n, dd, e, data]);
602     },
603     
604     dragOut : function(value)
605     {
606         // resize back to normal
607         Roo.log("out");
608         this.el.setSize(this.originalSize);
609         this.dropZone.resetConstraints();
610     },
611     
612     dragOver : function()
613     {
614         // ??? do nothing?
615     }
616    
617 });
618
619  
620
621  /*
622  * - LGPL
623  *
624  * Body
625  *
626  */
627
628 /**
629  * @class Roo.bootstrap.Body
630  * @extends Roo.bootstrap.Component
631  * Bootstrap Body class
632  *
633  * @constructor
634  * Create a new body
635  * @param {Object} config The config object
636  */
637
638 Roo.bootstrap.Body = function(config){
639
640     config = config || {};
641
642     Roo.bootstrap.Body.superclass.constructor.call(this, config);
643     this.el = Roo.get(config.el ? config.el : document.body );
644     if (this.cls && this.cls.length) {
645         Roo.get(document.body).addClass(this.cls);
646     }
647 };
648
649 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
650
651     is_body : true,// just to make sure it's constructed?
652
653         autoCreate : {
654         cls: 'container'
655     },
656     onRender : function(ct, position)
657     {
658        /* Roo.log("Roo.bootstrap.Body - onRender");
659         if (this.cls && this.cls.length) {
660             Roo.get(document.body).addClass(this.cls);
661         }
662         // style??? xttr???
663         */
664     }
665
666
667
668
669 });
670 /*
671  * - LGPL
672  *
673  * button group
674  * 
675  */
676
677
678 /**
679  * @class Roo.bootstrap.ButtonGroup
680  * @extends Roo.bootstrap.Component
681  * Bootstrap ButtonGroup class
682  * @cfg {String} size lg | sm | xs (default empty normal)
683  * @cfg {String} align vertical | justified  (default none)
684  * @cfg {String} direction up | down (default down)
685  * @cfg {Boolean} toolbar false | true
686  * @cfg {Boolean} btn true | false
687  * 
688  * 
689  * @constructor
690  * Create a new Input
691  * @param {Object} config The config object
692  */
693
694 Roo.bootstrap.ButtonGroup = function(config){
695     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
696 };
697
698 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
699     
700     size: '',
701     align: '',
702     direction: '',
703     toolbar: false,
704     btn: true,
705
706     getAutoCreate : function(){
707         var cfg = {
708             cls: 'btn-group',
709             html : null
710         };
711         
712         cfg.html = this.html || cfg.html;
713         
714         if (this.toolbar) {
715             cfg = {
716                 cls: 'btn-toolbar',
717                 html: null
718             };
719             
720             return cfg;
721         }
722         
723         if (['vertical','justified'].indexOf(this.align)!==-1) {
724             cfg.cls = 'btn-group-' + this.align;
725             
726             if (this.align == 'justified') {
727                 console.log(this.items);
728             }
729         }
730         
731         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
732             cfg.cls += ' btn-group-' + this.size;
733         }
734         
735         if (this.direction == 'up') {
736             cfg.cls += ' dropup' ;
737         }
738         
739         return cfg;
740     },
741     /**
742      * Add a button to the group (similar to NavItem API.)
743      */
744     addItem : function(cfg)
745     {
746         var cn = new Roo.bootstrap.Button(cfg);
747         //this.register(cn);
748         cn.parentId = this.id;
749         cn.onRender(this.el, null);
750         return cn;
751     }
752    
753 });
754
755  /*
756  * - LGPL
757  *
758  * button
759  * 
760  */
761
762 /**
763  * @class Roo.bootstrap.Button
764  * @extends Roo.bootstrap.Component
765  * Bootstrap Button class
766  * @cfg {String} html The button content
767  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
768  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
769  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
770  * @cfg {String} size ( lg | sm | xs)
771  * @cfg {String} tag ( a | input | submit)
772  * @cfg {String} href empty or href
773  * @cfg {Boolean} disabled default false;
774  * @cfg {Boolean} isClose default false;
775  * @cfg {String} glyphicon depricated - use fa
776  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
777  * @cfg {String} badge text for badge
778  * @cfg {String} theme (default|glow)  
779  * @cfg {Boolean} inverse dark themed version
780  * @cfg {Boolean} toggle is it a slidy toggle button
781  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
782  * @cfg {String} ontext text for on slidy toggle state
783  * @cfg {String} offtext text for off slidy toggle state
784  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
785  * @cfg {Boolean} removeClass remove the standard class..
786  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
787  * 
788  * @constructor
789  * Create a new button
790  * @param {Object} config The config object
791  */
792
793
794 Roo.bootstrap.Button = function(config){
795     Roo.bootstrap.Button.superclass.constructor.call(this, config);
796     this.weightClass = ["btn-default btn-outline-secondary", 
797                        "btn-primary", 
798                        "btn-success", 
799                        "btn-info", 
800                        "btn-warning",
801                        "btn-danger",
802                        "btn-link"
803                       ],  
804     this.addEvents({
805         // raw events
806         /**
807          * @event click
808          * When a butotn is pressed
809          * @param {Roo.bootstrap.Button} btn
810          * @param {Roo.EventObject} e
811          */
812         "click" : true,
813          /**
814          * @event toggle
815          * After the button has been toggles
816          * @param {Roo.bootstrap.Button} btn
817          * @param {Roo.EventObject} e
818          * @param {boolean} pressed (also available as button.pressed)
819          */
820         "toggle" : true
821     });
822 };
823
824 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
825     html: false,
826     active: false,
827     weight: '',
828     badge_weight: '',
829     outline : false,
830     size: '',
831     tag: 'button',
832     href: '',
833     disabled: false,
834     isClose: false,
835     glyphicon: '',
836     fa: '',
837     badge: '',
838     theme: 'default',
839     inverse: false,
840     
841     toggle: false,
842     ontext: 'ON',
843     offtext: 'OFF',
844     defaulton: true,
845     preventDefault: true,
846     removeClass: false,
847     name: false,
848     target: false,
849      
850     pressed : null,
851      
852     
853     getAutoCreate : function(){
854         
855         var cfg = {
856             tag : 'button',
857             cls : 'roo-button',
858             html: ''
859         };
860         
861         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
862             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
863             this.tag = 'button';
864         } else {
865             cfg.tag = this.tag;
866         }
867         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
868         
869         if (this.toggle == true) {
870             cfg={
871                 tag: 'div',
872                 cls: 'slider-frame roo-button',
873                 cn: [
874                     {
875                         tag: 'span',
876                         'data-on-text':'ON',
877                         'data-off-text':'OFF',
878                         cls: 'slider-button',
879                         html: this.offtext
880                     }
881                 ]
882             };
883             
884             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
885                 cfg.cls += ' '+this.weight;
886             }
887             
888             return cfg;
889         }
890         
891         if (this.isClose) {
892             cfg.cls += ' close';
893             
894             cfg["aria-hidden"] = true;
895             
896             cfg.html = "&times;";
897             
898             return cfg;
899         }
900         
901          
902         if (this.theme==='default') {
903             cfg.cls = 'btn roo-button';
904             
905             //if (this.parentType != 'Navbar') {
906             this.weight = this.weight.length ?  this.weight : 'default';
907             //}
908             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
909                 
910                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
911                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
912                 cfg.cls += ' btn-' + outline + weight;
913                 if (this.weight == 'default') {
914                     // BC
915                     cfg.cls += ' btn-' + this.weight;
916                 }
917             }
918         } else if (this.theme==='glow') {
919             
920             cfg.tag = 'a';
921             cfg.cls = 'btn-glow roo-button';
922             
923             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
924                 
925                 cfg.cls += ' ' + this.weight;
926             }
927         }
928    
929         
930         if (this.inverse) {
931             this.cls += ' inverse';
932         }
933         
934         
935         if (this.active || this.pressed === true) {
936             cfg.cls += ' active';
937         }
938         
939         if (this.disabled) {
940             cfg.disabled = 'disabled';
941         }
942         
943         if (this.items) {
944             Roo.log('changing to ul' );
945             cfg.tag = 'ul';
946             this.glyphicon = 'caret';
947             if (Roo.bootstrap.version == 4) {
948                 this.fa = 'caret-down';
949             }
950             
951         }
952         
953         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
954          
955         //gsRoo.log(this.parentType);
956         if (this.parentType === 'Navbar' && !this.parent().bar) {
957             Roo.log('changing to li?');
958             
959             cfg.tag = 'li';
960             
961             cfg.cls = '';
962             cfg.cn =  [{
963                 tag : 'a',
964                 cls : 'roo-button',
965                 html : this.html,
966                 href : this.href || '#'
967             }];
968             if (this.menu) {
969                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
970                 cfg.cls += ' dropdown';
971             }   
972             
973             delete cfg.html;
974             
975         }
976         
977        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
978         
979         if (this.glyphicon) {
980             cfg.html = ' ' + cfg.html;
981             
982             cfg.cn = [
983                 {
984                     tag: 'span',
985                     cls: 'glyphicon glyphicon-' + this.glyphicon
986                 }
987             ];
988         }
989         if (this.fa) {
990             cfg.html = ' ' + cfg.html;
991             
992             cfg.cn = [
993                 {
994                     tag: 'i',
995                     cls: 'fa fas fa-' + this.fa
996                 }
997             ];
998         }
999         
1000         if (this.badge) {
1001             cfg.html += ' ';
1002             
1003             cfg.tag = 'a';
1004             
1005 //            cfg.cls='btn roo-button';
1006             
1007             cfg.href=this.href;
1008             
1009             var value = cfg.html;
1010             
1011             if(this.glyphicon){
1012                 value = {
1013                     tag: 'span',
1014                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1015                     html: this.html
1016                 };
1017             }
1018             if(this.fa){
1019                 value = {
1020                     tag: 'i',
1021                     cls: 'fa fas fa-' + this.fa,
1022                     html: this.html
1023                 };
1024             }
1025             
1026             var bw = this.badge_weight.length ? this.badge_weight :
1027                 (this.weight.length ? this.weight : 'secondary');
1028             bw = bw == 'default' ? 'secondary' : bw;
1029             
1030             cfg.cn = [
1031                 value,
1032                 {
1033                     tag: 'span',
1034                     cls: 'badge badge-' + bw,
1035                     html: this.badge
1036                 }
1037             ];
1038             
1039             cfg.html='';
1040         }
1041         
1042         if (this.menu) {
1043             cfg.cls += ' dropdown';
1044             cfg.html = typeof(cfg.html) != 'undefined' ?
1045                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1046         }
1047         
1048         if (cfg.tag !== 'a' && this.href !== '') {
1049             throw "Tag must be a to set href.";
1050         } else if (this.href.length > 0) {
1051             cfg.href = this.href;
1052         }
1053         
1054         if(this.removeClass){
1055             cfg.cls = '';
1056         }
1057         
1058         if(this.target){
1059             cfg.target = this.target;
1060         }
1061         
1062         return cfg;
1063     },
1064     initEvents: function() {
1065        // Roo.log('init events?');
1066 //        Roo.log(this.el.dom);
1067         // add the menu...
1068         
1069         if (typeof (this.menu) != 'undefined') {
1070             this.menu.parentType = this.xtype;
1071             this.menu.triggerEl = this.el;
1072             this.addxtype(Roo.apply({}, this.menu));
1073         }
1074
1075
1076        if (this.el.hasClass('roo-button')) {
1077             this.el.on('click', this.onClick, this);
1078        } else {
1079             this.el.select('.roo-button').on('click', this.onClick, this);
1080        }
1081        
1082        if(this.removeClass){
1083            this.el.on('click', this.onClick, this);
1084        }
1085        
1086        this.el.enableDisplayMode();
1087         
1088     },
1089     onClick : function(e)
1090     {
1091         if (this.disabled) {
1092             return;
1093         }
1094         
1095         Roo.log('button on click ');
1096         if(this.preventDefault){
1097             e.preventDefault();
1098         }
1099         
1100         if (this.pressed === true || this.pressed === false) {
1101             this.toggleActive(e);
1102         }
1103         
1104         
1105         this.fireEvent('click', this, e);
1106     },
1107     
1108     /**
1109      * Enables this button
1110      */
1111     enable : function()
1112     {
1113         this.disabled = false;
1114         this.el.removeClass('disabled');
1115     },
1116     
1117     /**
1118      * Disable this button
1119      */
1120     disable : function()
1121     {
1122         this.disabled = true;
1123         this.el.addClass('disabled');
1124     },
1125      /**
1126      * sets the active state on/off, 
1127      * @param {Boolean} state (optional) Force a particular state
1128      */
1129     setActive : function(v) {
1130         
1131         this.el[v ? 'addClass' : 'removeClass']('active');
1132         this.pressed = v;
1133     },
1134      /**
1135      * toggles the current active state 
1136      */
1137     toggleActive : function(e)
1138     {
1139         this.setActive(!this.pressed);
1140         this.fireEvent('toggle', this, e, !this.pressed);
1141     },
1142      /**
1143      * get the current active state
1144      * @return {boolean} true if it's active
1145      */
1146     isActive : function()
1147     {
1148         return this.el.hasClass('active');
1149     },
1150     /**
1151      * set the text of the first selected button
1152      */
1153     setText : function(str)
1154     {
1155         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1156     },
1157     /**
1158      * get the text of the first selected button
1159      */
1160     getText : function()
1161     {
1162         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1163     },
1164     
1165     setWeight : function(str)
1166     {
1167         this.el.removeClass(this.weightClass);
1168         this.weight = str;
1169         var outline = this.outline ? 'outline-' : '';
1170         if (str == 'default') {
1171             this.el.addClass('btn-default btn-outline-secondary');        
1172             return;
1173         }
1174         this.el.addClass('btn-' + outline + str);        
1175     }
1176     
1177     
1178 });
1179
1180  /*
1181  * - LGPL
1182  *
1183  * column
1184  * 
1185  */
1186
1187 /**
1188  * @class Roo.bootstrap.Column
1189  * @extends Roo.bootstrap.Component
1190  * Bootstrap Column class
1191  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1192  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1193  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1194  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1195  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1196  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1197  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1198  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1199  *
1200  * 
1201  * @cfg {Boolean} hidden (true|false) hide the element
1202  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1203  * @cfg {String} fa (ban|check|...) font awesome icon
1204  * @cfg {Number} fasize (1|2|....) font awsome size
1205
1206  * @cfg {String} icon (info-sign|check|...) glyphicon name
1207
1208  * @cfg {String} html content of column.
1209  * 
1210  * @constructor
1211  * Create a new Column
1212  * @param {Object} config The config object
1213  */
1214
1215 Roo.bootstrap.Column = function(config){
1216     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1217 };
1218
1219 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1220     
1221     xs: false,
1222     sm: false,
1223     md: false,
1224     lg: false,
1225     xsoff: false,
1226     smoff: false,
1227     mdoff: false,
1228     lgoff: false,
1229     html: '',
1230     offset: 0,
1231     alert: false,
1232     fa: false,
1233     icon : false,
1234     hidden : false,
1235     fasize : 1,
1236     
1237     getAutoCreate : function(){
1238         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1239         
1240         cfg = {
1241             tag: 'div',
1242             cls: 'column'
1243         };
1244         
1245         var settings=this;
1246         var sizes =   ['xs','sm','md','lg'];
1247         sizes.map(function(size ,ix){
1248             //Roo.log( size + ':' + settings[size]);
1249             
1250             if (settings[size+'off'] !== false) {
1251                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1252             }
1253             
1254             if (settings[size] === false) {
1255                 return;
1256             }
1257             
1258             if (!settings[size]) { // 0 = hidden
1259                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1260                 // bootsrap4
1261                 for (var i = ix; i > -1; i--) {
1262                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1263                 }
1264                 
1265                 
1266                 return;
1267             }
1268             cfg.cls += ' col-' + size + '-' + settings[size] + (
1269                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1270             );
1271             
1272         });
1273         
1274         if (this.hidden) {
1275             cfg.cls += ' hidden';
1276         }
1277         
1278         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1279             cfg.cls +=' alert alert-' + this.alert;
1280         }
1281         
1282         
1283         if (this.html.length) {
1284             cfg.html = this.html;
1285         }
1286         if (this.fa) {
1287             var fasize = '';
1288             if (this.fasize > 1) {
1289                 fasize = ' fa-' + this.fasize + 'x';
1290             }
1291             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1292             
1293             
1294         }
1295         if (this.icon) {
1296             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1297         }
1298         
1299         return cfg;
1300     }
1301    
1302 });
1303
1304  
1305
1306  /*
1307  * - LGPL
1308  *
1309  * page container.
1310  * 
1311  */
1312
1313
1314 /**
1315  * @class Roo.bootstrap.Container
1316  * @extends Roo.bootstrap.Component
1317  * Bootstrap Container class
1318  * @cfg {Boolean} jumbotron is it a jumbotron element
1319  * @cfg {String} html content of element
1320  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1321  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1322  * @cfg {String} header content of header (for panel)
1323  * @cfg {String} footer content of footer (for panel)
1324  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1325  * @cfg {String} tag (header|aside|section) type of HTML tag.
1326  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1327  * @cfg {String} fa font awesome icon
1328  * @cfg {String} icon (info-sign|check|...) glyphicon name
1329  * @cfg {Boolean} hidden (true|false) hide the element
1330  * @cfg {Boolean} expandable (true|false) default false
1331  * @cfg {Boolean} expanded (true|false) default true
1332  * @cfg {String} rheader contet on the right of header
1333  * @cfg {Boolean} clickable (true|false) default false
1334
1335  *     
1336  * @constructor
1337  * Create a new Container
1338  * @param {Object} config The config object
1339  */
1340
1341 Roo.bootstrap.Container = function(config){
1342     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1343     
1344     this.addEvents({
1345         // raw events
1346          /**
1347          * @event expand
1348          * After the panel has been expand
1349          * 
1350          * @param {Roo.bootstrap.Container} this
1351          */
1352         "expand" : true,
1353         /**
1354          * @event collapse
1355          * After the panel has been collapsed
1356          * 
1357          * @param {Roo.bootstrap.Container} this
1358          */
1359         "collapse" : true,
1360         /**
1361          * @event click
1362          * When a element is chick
1363          * @param {Roo.bootstrap.Container} this
1364          * @param {Roo.EventObject} e
1365          */
1366         "click" : true
1367     });
1368 };
1369
1370 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1371     
1372     jumbotron : false,
1373     well: '',
1374     panel : '',
1375     header: '',
1376     footer : '',
1377     sticky: '',
1378     tag : false,
1379     alert : false,
1380     fa: false,
1381     icon : false,
1382     expandable : false,
1383     rheader : '',
1384     expanded : true,
1385     clickable: false,
1386   
1387      
1388     getChildContainer : function() {
1389         
1390         if(!this.el){
1391             return false;
1392         }
1393         
1394         if (this.panel.length) {
1395             return this.el.select('.panel-body',true).first();
1396         }
1397         
1398         return this.el;
1399     },
1400     
1401     
1402     getAutoCreate : function(){
1403         
1404         var cfg = {
1405             tag : this.tag || 'div',
1406             html : '',
1407             cls : ''
1408         };
1409         if (this.jumbotron) {
1410             cfg.cls = 'jumbotron';
1411         }
1412         
1413         
1414         
1415         // - this is applied by the parent..
1416         //if (this.cls) {
1417         //    cfg.cls = this.cls + '';
1418         //}
1419         
1420         if (this.sticky.length) {
1421             
1422             var bd = Roo.get(document.body);
1423             if (!bd.hasClass('bootstrap-sticky')) {
1424                 bd.addClass('bootstrap-sticky');
1425                 Roo.select('html',true).setStyle('height', '100%');
1426             }
1427              
1428             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1429         }
1430         
1431         
1432         if (this.well.length) {
1433             switch (this.well) {
1434                 case 'lg':
1435                 case 'sm':
1436                     cfg.cls +=' well well-' +this.well;
1437                     break;
1438                 default:
1439                     cfg.cls +=' well';
1440                     break;
1441             }
1442         }
1443         
1444         if (this.hidden) {
1445             cfg.cls += ' hidden';
1446         }
1447         
1448         
1449         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1450             cfg.cls +=' alert alert-' + this.alert;
1451         }
1452         
1453         var body = cfg;
1454         
1455         if (this.panel.length) {
1456             cfg.cls += ' panel panel-' + this.panel;
1457             cfg.cn = [];
1458             if (this.header.length) {
1459                 
1460                 var h = [];
1461                 
1462                 if(this.expandable){
1463                     
1464                     cfg.cls = cfg.cls + ' expandable';
1465                     
1466                     h.push({
1467                         tag: 'i',
1468                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1469                     });
1470                     
1471                 }
1472                 
1473                 h.push(
1474                     {
1475                         tag: 'span',
1476                         cls : 'panel-title',
1477                         html : (this.expandable ? '&nbsp;' : '') + this.header
1478                     },
1479                     {
1480                         tag: 'span',
1481                         cls: 'panel-header-right',
1482                         html: this.rheader
1483                     }
1484                 );
1485                 
1486                 cfg.cn.push({
1487                     cls : 'panel-heading',
1488                     style : this.expandable ? 'cursor: pointer' : '',
1489                     cn : h
1490                 });
1491                 
1492             }
1493             
1494             body = false;
1495             cfg.cn.push({
1496                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1497                 html : this.html
1498             });
1499             
1500             
1501             if (this.footer.length) {
1502                 cfg.cn.push({
1503                     cls : 'panel-footer',
1504                     html : this.footer
1505                     
1506                 });
1507             }
1508             
1509         }
1510         
1511         if (body) {
1512             body.html = this.html || cfg.html;
1513             // prefix with the icons..
1514             if (this.fa) {
1515                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1516             }
1517             if (this.icon) {
1518                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1519             }
1520             
1521             
1522         }
1523         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1524             cfg.cls =  'container';
1525         }
1526         
1527         return cfg;
1528     },
1529     
1530     initEvents: function() 
1531     {
1532         if(this.expandable){
1533             var headerEl = this.headerEl();
1534         
1535             if(headerEl){
1536                 headerEl.on('click', this.onToggleClick, this);
1537             }
1538         }
1539         
1540         if(this.clickable){
1541             this.el.on('click', this.onClick, this);
1542         }
1543         
1544     },
1545     
1546     onToggleClick : function()
1547     {
1548         var headerEl = this.headerEl();
1549         
1550         if(!headerEl){
1551             return;
1552         }
1553         
1554         if(this.expanded){
1555             this.collapse();
1556             return;
1557         }
1558         
1559         this.expand();
1560     },
1561     
1562     expand : function()
1563     {
1564         if(this.fireEvent('expand', this)) {
1565             
1566             this.expanded = true;
1567             
1568             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1569             
1570             this.el.select('.panel-body',true).first().removeClass('hide');
1571             
1572             var toggleEl = this.toggleEl();
1573
1574             if(!toggleEl){
1575                 return;
1576             }
1577
1578             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1579         }
1580         
1581     },
1582     
1583     collapse : function()
1584     {
1585         if(this.fireEvent('collapse', this)) {
1586             
1587             this.expanded = false;
1588             
1589             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1590             this.el.select('.panel-body',true).first().addClass('hide');
1591         
1592             var toggleEl = this.toggleEl();
1593
1594             if(!toggleEl){
1595                 return;
1596             }
1597
1598             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1599         }
1600     },
1601     
1602     toggleEl : function()
1603     {
1604         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1605             return;
1606         }
1607         
1608         return this.el.select('.panel-heading .fa',true).first();
1609     },
1610     
1611     headerEl : function()
1612     {
1613         if(!this.el || !this.panel.length || !this.header.length){
1614             return;
1615         }
1616         
1617         return this.el.select('.panel-heading',true).first()
1618     },
1619     
1620     bodyEl : function()
1621     {
1622         if(!this.el || !this.panel.length){
1623             return;
1624         }
1625         
1626         return this.el.select('.panel-body',true).first()
1627     },
1628     
1629     titleEl : function()
1630     {
1631         if(!this.el || !this.panel.length || !this.header.length){
1632             return;
1633         }
1634         
1635         return this.el.select('.panel-title',true).first();
1636     },
1637     
1638     setTitle : function(v)
1639     {
1640         var titleEl = this.titleEl();
1641         
1642         if(!titleEl){
1643             return;
1644         }
1645         
1646         titleEl.dom.innerHTML = v;
1647     },
1648     
1649     getTitle : function()
1650     {
1651         
1652         var titleEl = this.titleEl();
1653         
1654         if(!titleEl){
1655             return '';
1656         }
1657         
1658         return titleEl.dom.innerHTML;
1659     },
1660     
1661     setRightTitle : function(v)
1662     {
1663         var t = this.el.select('.panel-header-right',true).first();
1664         
1665         if(!t){
1666             return;
1667         }
1668         
1669         t.dom.innerHTML = v;
1670     },
1671     
1672     onClick : function(e)
1673     {
1674         e.preventDefault();
1675         
1676         this.fireEvent('click', this, e);
1677     }
1678 });
1679
1680  /*
1681  *  - LGPL
1682  *
1683  *  This is BS4's Card element.. - similar to our containers probably..
1684  * 
1685  */
1686 /**
1687  * @class Roo.bootstrap.Card
1688  * @extends Roo.bootstrap.Component
1689  * Bootstrap Card class
1690  *
1691  *
1692  * possible... may not be implemented..
1693  * @cfg {String} header_image  src url of image.
1694  * @cfg {String|Object} header
1695  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1696  * 
1697  * @cfg {String} title
1698  * @cfg {String} subtitle
1699  * @cfg {String} html -- html contents - or just use children..
1700  * @cfg {String} footer
1701  
1702  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1703  * 
1704  * @cfg {String} margin (0|1|2|3|4|5|auto)
1705  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1706  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1707  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1708  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1709  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1710  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1711  *
1712  * @cfg {String} padding (0|1|2|3|4|5)
1713  * @cfg {String} padding_top (0|1|2|3|4|5)
1714  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1715  * @cfg {String} padding_left (0|1|2|3|4|5)
1716  * @cfg {String} padding_right (0|1|2|3|4|5)
1717  * @cfg {String} padding_x (0|1|2|3|4|5)
1718  * @cfg {String} padding_y (0|1|2|3|4|5)
1719  *
1720  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1721  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1722  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1723  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1724  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1725  
1726  * @config {Boolean} dragable  if this card can be dragged.
1727  * @config {String} drag_group  group for drag
1728  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1729  * @config {String} drop_group  group for drag
1730  * 
1731  
1732  * @constructor
1733  * Create a new Container
1734  * @param {Object} config The config object
1735  */
1736
1737 Roo.bootstrap.Card = function(config){
1738     Roo.bootstrap.Card.superclass.constructor.call(this, config);
1739     
1740     this.addEvents({
1741         
1742     });
1743 };
1744
1745
1746 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
1747     
1748     
1749     weight : '',
1750     
1751     margin: '', /// may be better in component?
1752     margin_top: '', 
1753     margin_bottom: '', 
1754     margin_left: '',
1755     margin_right: '',
1756     margin_x: '',
1757     margin_y: '',
1758     
1759     padding : '',
1760     padding_top: '', 
1761     padding_bottom: '', 
1762     padding_left: '',
1763     padding_right: '',
1764     padding_x: '',
1765     padding_y: '',
1766     
1767     display: '', 
1768     display_xs: '', 
1769     display_sm: '', 
1770     display_lg: '',
1771     display_xl: '',
1772  
1773     header_image  : '',
1774     header : '',
1775     header_size : 0,
1776     title : '',
1777     subtitle : '',
1778     html : '',
1779     footer: '',
1780     
1781     dragable : false,
1782     drag_group : false,
1783     dropable : false,
1784     drop_group : false,
1785     childContainer : false,
1786
1787     layoutCls : function()
1788     {
1789         var cls = '';
1790         var t = this;
1791         Roo.log(this.margin_bottom.length);
1792         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
1793             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
1794             
1795             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
1796                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
1797             }
1798             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
1799                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
1800             }
1801         });
1802         
1803         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
1804             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
1805                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
1806             }
1807         });
1808         
1809         // more generic support?
1810         if (this.hidden) {
1811             cls += ' d-none';
1812         }
1813         
1814         return cls;
1815     },
1816  
1817        // Roo.log("Call onRender: " + this.xtype);
1818         /*  We are looking at something like this.
1819 <div class="card">
1820     <img src="..." class="card-img-top" alt="...">
1821     <div class="card-body">
1822         <h5 class="card-title">Card title</h5>
1823          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
1824
1825         >> this bit is really the body...
1826         <div> << we will ad dthis in hopefully it will not break shit.
1827         
1828         ** card text does not actually have any styling...
1829         
1830             <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>
1831         
1832         </div> <<
1833           <a href="#" class="card-link">Card link</a>
1834           
1835     </div>
1836     <div class="card-footer">
1837         <small class="text-muted">Last updated 3 mins ago</small>
1838     </div>
1839 </div>
1840          */
1841     getAutoCreate : function(){
1842         
1843         var cfg = {
1844             tag : 'div',
1845             cls : 'card',
1846             cn : [ ]
1847         };
1848         
1849         if (this.weight.length && this.weight != 'light') {
1850             cfg.cls += ' text-white';
1851         } else {
1852             cfg.cls += ' text-dark'; // need as it's nested..
1853         }
1854         if (this.weight.length) {
1855             cfg.cls += ' bg-' + this.weight;
1856         }
1857         
1858         cfg.cls += this.layoutCls(); 
1859         
1860         if (this.header.length) {
1861             cfg.cn.push({
1862                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
1863                 cls : 'card-header',
1864                 html : this.header // escape?
1865             });
1866         } else {
1867             cfg.cn.push({
1868                 tag : 'div',
1869                 cls : 'card-header d-none'
1870             });
1871         }
1872         if (this.header_image.length) {
1873             cfg.cn.push({
1874                 tag : 'img',
1875                 cls : 'card-img-top',
1876                 src: this.header_image // escape?
1877             });
1878         } else {
1879             cfg.cn.push({
1880                 tag : 'div',
1881                 cls : 'card-img-top d-none' 
1882             });
1883         }
1884         
1885         var body = {
1886             tag : 'div',
1887             cls : 'card-body',
1888             cn : []
1889         };
1890         cfg.cn.push(body);
1891         
1892         if (this.title.length) {
1893             body.cn.push({
1894                 tag : 'div',
1895                 cls : 'card-title',
1896                 src: this.title // escape?
1897             });
1898         }
1899         
1900         if (this.subtitle.length) {
1901             body.cn.push({
1902                 tag : 'div',
1903                 cls : 'card-title',
1904                 src: this.subtitle // escape?
1905             });
1906         }
1907         
1908         body.cn.push({
1909             tag : 'div',
1910             cls : 'roo-card-body-ctr'
1911         });
1912         
1913         if (this.html.length) {
1914             body.cn.push({
1915                 tag: 'div',
1916                 html : this.html
1917             });
1918         }
1919         // fixme ? handle objects?
1920         if (this.footer.length) {
1921             cfg.cn.push({
1922                 tag : 'div',
1923                 cls : 'card-footer',
1924                 html: this.footer // escape?
1925             });
1926         }
1927         // footer...
1928         
1929         return cfg;
1930     },
1931     
1932     
1933     getCardHeader : function()
1934     {
1935         var  ret = this.el.select('.card-header',true).first();
1936         if (ret.hasClass('d-none')) {
1937             ret.removeClass('d-none');
1938         }
1939         
1940         return ret;
1941     },
1942     
1943     getCardImageTop : function()
1944     {
1945         var  ret = this.el.select('.card-img-top',true).first();
1946         if (ret.hasClass('d-none')) {
1947             ret.removeClass('d-none');
1948         }
1949         
1950         return ret;
1951     },
1952     
1953     getChildContainer : function()
1954     {
1955         
1956         if(!this.el){
1957             return false;
1958         }
1959         return this.el.select('.roo-card-body-ctr',true).first();    
1960     },
1961     
1962     initEvents: function() 
1963     {
1964         if(this.dragable){
1965             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
1966                     containerScroll: true,
1967                     ddGroup: this.drag_group || 'default_card_drag_group'
1968             });
1969             this.dragZone.getDragData = this.getDragData.createDelegate(this);
1970         }
1971         if (this.dropable) {
1972             this.dropZone = new Roo.dd.DropZone(this.getChildContainer(), {
1973                     containerScroll: true,
1974                     ddGroup: his.drop_group || 'default_card_drag_group'
1975             });
1976             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
1977             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
1978             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
1979             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
1980             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
1981         }
1982         
1983         
1984     },
1985     getDragData : function(e) {
1986         var target = this.getEl();
1987         if (target) {
1988             //this.handleSelection(e);
1989             
1990             var dragData = {
1991                 source: this,
1992                 copy: false,
1993                 nodes: this.getEl(),
1994                 records: []
1995             };
1996             
1997             
1998             dragData.ddel = target.dom ;        // the div element
1999             Roo.log(target.getWidth( ));
2000              dragData.ddel.style.width = target.getWidth() + 'px';
2001             
2002             return dragData;
2003         }
2004         return false;
2005     }
2006     
2007 });
2008
2009 /*
2010  * - LGPL
2011  *
2012  * Card header - holder for the card header elements.
2013  * 
2014  */
2015
2016 /**
2017  * @class Roo.bootstrap.CardHeader
2018  * @extends Roo.bootstrap.Element
2019  * Bootstrap CardHeader class
2020  * @constructor
2021  * Create a new Card Header - that you can embed children into
2022  * @param {Object} config The config object
2023  */
2024
2025 Roo.bootstrap.CardHeader = function(config){
2026     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2027 };
2028
2029 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2030     
2031     
2032     container_method : 'getCardHeader' 
2033     
2034      
2035     
2036     
2037    
2038 });
2039
2040  
2041
2042  /*
2043  * - LGPL
2044  *
2045  * Card header - holder for the card header elements.
2046  * 
2047  */
2048
2049 /**
2050  * @class Roo.bootstrap.CardImageTop
2051  * @extends Roo.bootstrap.Element
2052  * Bootstrap CardImageTop class
2053  * @constructor
2054  * Create a new Card Image Top container
2055  * @param {Object} config The config object
2056  */
2057
2058 Roo.bootstrap.CardImageTop = function(config){
2059     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2060 };
2061
2062 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2063     
2064    
2065     container_method : 'getCardImageTop' 
2066     
2067      
2068     
2069    
2070 });
2071
2072  
2073
2074  /*
2075  * - LGPL
2076  *
2077  * image
2078  * 
2079  */
2080
2081
2082 /**
2083  * @class Roo.bootstrap.Img
2084  * @extends Roo.bootstrap.Component
2085  * Bootstrap Img class
2086  * @cfg {Boolean} imgResponsive false | true
2087  * @cfg {String} border rounded | circle | thumbnail
2088  * @cfg {String} src image source
2089  * @cfg {String} alt image alternative text
2090  * @cfg {String} href a tag href
2091  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2092  * @cfg {String} xsUrl xs image source
2093  * @cfg {String} smUrl sm image source
2094  * @cfg {String} mdUrl md image source
2095  * @cfg {String} lgUrl lg image source
2096  * 
2097  * @constructor
2098  * Create a new Input
2099  * @param {Object} config The config object
2100  */
2101
2102 Roo.bootstrap.Img = function(config){
2103     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2104     
2105     this.addEvents({
2106         // img events
2107         /**
2108          * @event click
2109          * The img click event for the img.
2110          * @param {Roo.EventObject} e
2111          */
2112         "click" : true
2113     });
2114 };
2115
2116 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2117     
2118     imgResponsive: true,
2119     border: '',
2120     src: 'about:blank',
2121     href: false,
2122     target: false,
2123     xsUrl: '',
2124     smUrl: '',
2125     mdUrl: '',
2126     lgUrl: '',
2127
2128     getAutoCreate : function()
2129     {   
2130         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2131             return this.createSingleImg();
2132         }
2133         
2134         var cfg = {
2135             tag: 'div',
2136             cls: 'roo-image-responsive-group',
2137             cn: []
2138         };
2139         var _this = this;
2140         
2141         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2142             
2143             if(!_this[size + 'Url']){
2144                 return;
2145             }
2146             
2147             var img = {
2148                 tag: 'img',
2149                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2150                 html: _this.html || cfg.html,
2151                 src: _this[size + 'Url']
2152             };
2153             
2154             img.cls += ' roo-image-responsive-' + size;
2155             
2156             var s = ['xs', 'sm', 'md', 'lg'];
2157             
2158             s.splice(s.indexOf(size), 1);
2159             
2160             Roo.each(s, function(ss){
2161                 img.cls += ' hidden-' + ss;
2162             });
2163             
2164             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2165                 cfg.cls += ' img-' + _this.border;
2166             }
2167             
2168             if(_this.alt){
2169                 cfg.alt = _this.alt;
2170             }
2171             
2172             if(_this.href){
2173                 var a = {
2174                     tag: 'a',
2175                     href: _this.href,
2176                     cn: [
2177                         img
2178                     ]
2179                 };
2180
2181                 if(this.target){
2182                     a.target = _this.target;
2183                 }
2184             }
2185             
2186             cfg.cn.push((_this.href) ? a : img);
2187             
2188         });
2189         
2190         return cfg;
2191     },
2192     
2193     createSingleImg : function()
2194     {
2195         var cfg = {
2196             tag: 'img',
2197             cls: (this.imgResponsive) ? 'img-responsive' : '',
2198             html : null,
2199             src : 'about:blank'  // just incase src get's set to undefined?!?
2200         };
2201         
2202         cfg.html = this.html || cfg.html;
2203         
2204         cfg.src = this.src || cfg.src;
2205         
2206         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2207             cfg.cls += ' img-' + this.border;
2208         }
2209         
2210         if(this.alt){
2211             cfg.alt = this.alt;
2212         }
2213         
2214         if(this.href){
2215             var a = {
2216                 tag: 'a',
2217                 href: this.href,
2218                 cn: [
2219                     cfg
2220                 ]
2221             };
2222             
2223             if(this.target){
2224                 a.target = this.target;
2225             }
2226             
2227         }
2228         
2229         return (this.href) ? a : cfg;
2230     },
2231     
2232     initEvents: function() 
2233     {
2234         if(!this.href){
2235             this.el.on('click', this.onClick, this);
2236         }
2237         
2238     },
2239     
2240     onClick : function(e)
2241     {
2242         Roo.log('img onclick');
2243         this.fireEvent('click', this, e);
2244     },
2245     /**
2246      * Sets the url of the image - used to update it
2247      * @param {String} url the url of the image
2248      */
2249     
2250     setSrc : function(url)
2251     {
2252         this.src =  url;
2253         
2254         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2255             this.el.dom.src =  url;
2256             return;
2257         }
2258         
2259         this.el.select('img', true).first().dom.src =  url;
2260     }
2261     
2262     
2263    
2264 });
2265
2266  /*
2267  * - LGPL
2268  *
2269  * image
2270  * 
2271  */
2272
2273
2274 /**
2275  * @class Roo.bootstrap.Link
2276  * @extends Roo.bootstrap.Component
2277  * Bootstrap Link Class
2278  * @cfg {String} alt image alternative text
2279  * @cfg {String} href a tag href
2280  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2281  * @cfg {String} html the content of the link.
2282  * @cfg {String} anchor name for the anchor link
2283  * @cfg {String} fa - favicon
2284
2285  * @cfg {Boolean} preventDefault (true | false) default false
2286
2287  * 
2288  * @constructor
2289  * Create a new Input
2290  * @param {Object} config The config object
2291  */
2292
2293 Roo.bootstrap.Link = function(config){
2294     Roo.bootstrap.Link.superclass.constructor.call(this, config);
2295     
2296     this.addEvents({
2297         // img events
2298         /**
2299          * @event click
2300          * The img click event for the img.
2301          * @param {Roo.EventObject} e
2302          */
2303         "click" : true
2304     });
2305 };
2306
2307 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
2308     
2309     href: false,
2310     target: false,
2311     preventDefault: false,
2312     anchor : false,
2313     alt : false,
2314     fa: false,
2315
2316
2317     getAutoCreate : function()
2318     {
2319         var html = this.html || '';
2320         
2321         if (this.fa !== false) {
2322             html = '<i class="fa fa-' + this.fa + '"></i>';
2323         }
2324         var cfg = {
2325             tag: 'a'
2326         };
2327         // anchor's do not require html/href...
2328         if (this.anchor === false) {
2329             cfg.html = html;
2330             cfg.href = this.href || '#';
2331         } else {
2332             cfg.name = this.anchor;
2333             if (this.html !== false || this.fa !== false) {
2334                 cfg.html = html;
2335             }
2336             if (this.href !== false) {
2337                 cfg.href = this.href;
2338             }
2339         }
2340         
2341         if(this.alt !== false){
2342             cfg.alt = this.alt;
2343         }
2344         
2345         
2346         if(this.target !== false) {
2347             cfg.target = this.target;
2348         }
2349         
2350         return cfg;
2351     },
2352     
2353     initEvents: function() {
2354         
2355         if(!this.href || this.preventDefault){
2356             this.el.on('click', this.onClick, this);
2357         }
2358     },
2359     
2360     onClick : function(e)
2361     {
2362         if(this.preventDefault){
2363             e.preventDefault();
2364         }
2365         //Roo.log('img onclick');
2366         this.fireEvent('click', this, e);
2367     }
2368    
2369 });
2370
2371  /*
2372  * - LGPL
2373  *
2374  * header
2375  * 
2376  */
2377
2378 /**
2379  * @class Roo.bootstrap.Header
2380  * @extends Roo.bootstrap.Component
2381  * Bootstrap Header class
2382  * @cfg {String} html content of header
2383  * @cfg {Number} level (1|2|3|4|5|6) default 1
2384  * 
2385  * @constructor
2386  * Create a new Header
2387  * @param {Object} config The config object
2388  */
2389
2390
2391 Roo.bootstrap.Header  = function(config){
2392     Roo.bootstrap.Header.superclass.constructor.call(this, config);
2393 };
2394
2395 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
2396     
2397     //href : false,
2398     html : false,
2399     level : 1,
2400     
2401     
2402     
2403     getAutoCreate : function(){
2404         
2405         
2406         
2407         var cfg = {
2408             tag: 'h' + (1 *this.level),
2409             html: this.html || ''
2410         } ;
2411         
2412         return cfg;
2413     }
2414    
2415 });
2416
2417  
2418
2419  /*
2420  * Based on:
2421  * Ext JS Library 1.1.1
2422  * Copyright(c) 2006-2007, Ext JS, LLC.
2423  *
2424  * Originally Released Under LGPL - original licence link has changed is not relivant.
2425  *
2426  * Fork - LGPL
2427  * <script type="text/javascript">
2428  */
2429  
2430 /**
2431  * @class Roo.bootstrap.MenuMgr
2432  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
2433  * @singleton
2434  */
2435 Roo.bootstrap.MenuMgr = function(){
2436    var menus, active, groups = {}, attached = false, lastShow = new Date();
2437
2438    // private - called when first menu is created
2439    function init(){
2440        menus = {};
2441        active = new Roo.util.MixedCollection();
2442        Roo.get(document).addKeyListener(27, function(){
2443            if(active.length > 0){
2444                hideAll();
2445            }
2446        });
2447    }
2448
2449    // private
2450    function hideAll(){
2451        if(active && active.length > 0){
2452            var c = active.clone();
2453            c.each(function(m){
2454                m.hide();
2455            });
2456        }
2457    }
2458
2459    // private
2460    function onHide(m){
2461        active.remove(m);
2462        if(active.length < 1){
2463            Roo.get(document).un("mouseup", onMouseDown);
2464             
2465            attached = false;
2466        }
2467    }
2468
2469    // private
2470    function onShow(m){
2471        var last = active.last();
2472        lastShow = new Date();
2473        active.add(m);
2474        if(!attached){
2475           Roo.get(document).on("mouseup", onMouseDown);
2476            
2477            attached = true;
2478        }
2479        if(m.parentMenu){
2480           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
2481           m.parentMenu.activeChild = m;
2482        }else if(last && last.isVisible()){
2483           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
2484        }
2485    }
2486
2487    // private
2488    function onBeforeHide(m){
2489        if(m.activeChild){
2490            m.activeChild.hide();
2491        }
2492        if(m.autoHideTimer){
2493            clearTimeout(m.autoHideTimer);
2494            delete m.autoHideTimer;
2495        }
2496    }
2497
2498    // private
2499    function onBeforeShow(m){
2500        var pm = m.parentMenu;
2501        if(!pm && !m.allowOtherMenus){
2502            hideAll();
2503        }else if(pm && pm.activeChild && active != m){
2504            pm.activeChild.hide();
2505        }
2506    }
2507
2508    // private this should really trigger on mouseup..
2509    function onMouseDown(e){
2510         Roo.log("on Mouse Up");
2511         
2512         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
2513             Roo.log("MenuManager hideAll");
2514             hideAll();
2515             e.stopEvent();
2516         }
2517         
2518         
2519    }
2520
2521    // private
2522    function onBeforeCheck(mi, state){
2523        if(state){
2524            var g = groups[mi.group];
2525            for(var i = 0, l = g.length; i < l; i++){
2526                if(g[i] != mi){
2527                    g[i].setChecked(false);
2528                }
2529            }
2530        }
2531    }
2532
2533    return {
2534
2535        /**
2536         * Hides all menus that are currently visible
2537         */
2538        hideAll : function(){
2539             hideAll();  
2540        },
2541
2542        // private
2543        register : function(menu){
2544            if(!menus){
2545                init();
2546            }
2547            menus[menu.id] = menu;
2548            menu.on("beforehide", onBeforeHide);
2549            menu.on("hide", onHide);
2550            menu.on("beforeshow", onBeforeShow);
2551            menu.on("show", onShow);
2552            var g = menu.group;
2553            if(g && menu.events["checkchange"]){
2554                if(!groups[g]){
2555                    groups[g] = [];
2556                }
2557                groups[g].push(menu);
2558                menu.on("checkchange", onCheck);
2559            }
2560        },
2561
2562         /**
2563          * Returns a {@link Roo.menu.Menu} object
2564          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
2565          * be used to generate and return a new Menu instance.
2566          */
2567        get : function(menu){
2568            if(typeof menu == "string"){ // menu id
2569                return menus[menu];
2570            }else if(menu.events){  // menu instance
2571                return menu;
2572            }
2573            /*else if(typeof menu.length == 'number'){ // array of menu items?
2574                return new Roo.bootstrap.Menu({items:menu});
2575            }else{ // otherwise, must be a config
2576                return new Roo.bootstrap.Menu(menu);
2577            }
2578            */
2579            return false;
2580        },
2581
2582        // private
2583        unregister : function(menu){
2584            delete menus[menu.id];
2585            menu.un("beforehide", onBeforeHide);
2586            menu.un("hide", onHide);
2587            menu.un("beforeshow", onBeforeShow);
2588            menu.un("show", onShow);
2589            var g = menu.group;
2590            if(g && menu.events["checkchange"]){
2591                groups[g].remove(menu);
2592                menu.un("checkchange", onCheck);
2593            }
2594        },
2595
2596        // private
2597        registerCheckable : function(menuItem){
2598            var g = menuItem.group;
2599            if(g){
2600                if(!groups[g]){
2601                    groups[g] = [];
2602                }
2603                groups[g].push(menuItem);
2604                menuItem.on("beforecheckchange", onBeforeCheck);
2605            }
2606        },
2607
2608        // private
2609        unregisterCheckable : function(menuItem){
2610            var g = menuItem.group;
2611            if(g){
2612                groups[g].remove(menuItem);
2613                menuItem.un("beforecheckchange", onBeforeCheck);
2614            }
2615        }
2616    };
2617 }();/*
2618  * - LGPL
2619  *
2620  * menu
2621  * 
2622  */
2623
2624 /**
2625  * @class Roo.bootstrap.Menu
2626  * @extends Roo.bootstrap.Component
2627  * Bootstrap Menu class - container for MenuItems
2628  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2629  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2630  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2631  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2632  * 
2633  * @constructor
2634  * Create a new Menu
2635  * @param {Object} config The config object
2636  */
2637
2638
2639 Roo.bootstrap.Menu = function(config){
2640     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2641     if (this.registerMenu && this.type != 'treeview')  {
2642         Roo.bootstrap.MenuMgr.register(this);
2643     }
2644     
2645     
2646     this.addEvents({
2647         /**
2648          * @event beforeshow
2649          * Fires before this menu is displayed (return false to block)
2650          * @param {Roo.menu.Menu} this
2651          */
2652         beforeshow : true,
2653         /**
2654          * @event beforehide
2655          * Fires before this menu is hidden (return false to block)
2656          * @param {Roo.menu.Menu} this
2657          */
2658         beforehide : true,
2659         /**
2660          * @event show
2661          * Fires after this menu is displayed
2662          * @param {Roo.menu.Menu} this
2663          */
2664         show : true,
2665         /**
2666          * @event hide
2667          * Fires after this menu is hidden
2668          * @param {Roo.menu.Menu} this
2669          */
2670         hide : true,
2671         /**
2672          * @event click
2673          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2674          * @param {Roo.menu.Menu} this
2675          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2676          * @param {Roo.EventObject} e
2677          */
2678         click : true,
2679         /**
2680          * @event mouseover
2681          * Fires when the mouse is hovering over this menu
2682          * @param {Roo.menu.Menu} this
2683          * @param {Roo.EventObject} e
2684          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2685          */
2686         mouseover : true,
2687         /**
2688          * @event mouseout
2689          * Fires when the mouse exits this menu
2690          * @param {Roo.menu.Menu} this
2691          * @param {Roo.EventObject} e
2692          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2693          */
2694         mouseout : true,
2695         /**
2696          * @event itemclick
2697          * Fires when a menu item contained in this menu is clicked
2698          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2699          * @param {Roo.EventObject} e
2700          */
2701         itemclick: true
2702     });
2703     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2704 };
2705
2706 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2707     
2708    /// html : false,
2709     //align : '',
2710     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2711     type: false,
2712     /**
2713      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2714      */
2715     registerMenu : true,
2716     
2717     menuItems :false, // stores the menu items..
2718     
2719     hidden:true,
2720         
2721     parentMenu : false,
2722     
2723     stopEvent : true,
2724     
2725     isLink : false,
2726     
2727     getChildContainer : function() {
2728         return this.el;  
2729     },
2730     
2731     getAutoCreate : function(){
2732          
2733         //if (['right'].indexOf(this.align)!==-1) {
2734         //    cfg.cn[1].cls += ' pull-right'
2735         //}
2736         
2737         
2738         var cfg = {
2739             tag : 'ul',
2740             cls : 'dropdown-menu' ,
2741             style : 'z-index:1000'
2742             
2743         };
2744         
2745         if (this.type === 'submenu') {
2746             cfg.cls = 'submenu active';
2747         }
2748         if (this.type === 'treeview') {
2749             cfg.cls = 'treeview-menu';
2750         }
2751         
2752         return cfg;
2753     },
2754     initEvents : function() {
2755         
2756        // Roo.log("ADD event");
2757        // Roo.log(this.triggerEl.dom);
2758         
2759         this.triggerEl.on('click', this.onTriggerClick, this);
2760         
2761         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2762         
2763         
2764         if (this.triggerEl.hasClass('nav-item')) {
2765             // dropdown toggle on the 'a' in BS4?
2766             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2767         } else {
2768             this.triggerEl.addClass('dropdown-toggle');
2769         }
2770         if (Roo.isTouch) {
2771             this.el.on('touchstart'  , this.onTouch, this);
2772         }
2773         this.el.on('click' , this.onClick, this);
2774
2775         this.el.on("mouseover", this.onMouseOver, this);
2776         this.el.on("mouseout", this.onMouseOut, this);
2777         
2778     },
2779     
2780     findTargetItem : function(e)
2781     {
2782         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2783         if(!t){
2784             return false;
2785         }
2786         //Roo.log(t);         Roo.log(t.id);
2787         if(t && t.id){
2788             //Roo.log(this.menuitems);
2789             return this.menuitems.get(t.id);
2790             
2791             //return this.items.get(t.menuItemId);
2792         }
2793         
2794         return false;
2795     },
2796     
2797     onTouch : function(e) 
2798     {
2799         Roo.log("menu.onTouch");
2800         //e.stopEvent(); this make the user popdown broken
2801         this.onClick(e);
2802     },
2803     
2804     onClick : function(e)
2805     {
2806         Roo.log("menu.onClick");
2807         
2808         var t = this.findTargetItem(e);
2809         if(!t || t.isContainer){
2810             return;
2811         }
2812         Roo.log(e);
2813         /*
2814         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2815             if(t == this.activeItem && t.shouldDeactivate(e)){
2816                 this.activeItem.deactivate();
2817                 delete this.activeItem;
2818                 return;
2819             }
2820             if(t.canActivate){
2821                 this.setActiveItem(t, true);
2822             }
2823             return;
2824             
2825             
2826         }
2827         */
2828        
2829         Roo.log('pass click event');
2830         
2831         t.onClick(e);
2832         
2833         this.fireEvent("click", this, t, e);
2834         
2835         var _this = this;
2836         
2837         if(!t.href.length || t.href == '#'){
2838             (function() { _this.hide(); }).defer(100);
2839         }
2840         
2841     },
2842     
2843     onMouseOver : function(e){
2844         var t  = this.findTargetItem(e);
2845         //Roo.log(t);
2846         //if(t){
2847         //    if(t.canActivate && !t.disabled){
2848         //        this.setActiveItem(t, true);
2849         //    }
2850         //}
2851         
2852         this.fireEvent("mouseover", this, e, t);
2853     },
2854     isVisible : function(){
2855         return !this.hidden;
2856     },
2857     onMouseOut : function(e){
2858         var t  = this.findTargetItem(e);
2859         
2860         //if(t ){
2861         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2862         //        this.activeItem.deactivate();
2863         //        delete this.activeItem;
2864         //    }
2865         //}
2866         this.fireEvent("mouseout", this, e, t);
2867     },
2868     
2869     
2870     /**
2871      * Displays this menu relative to another element
2872      * @param {String/HTMLElement/Roo.Element} element The element to align to
2873      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2874      * the element (defaults to this.defaultAlign)
2875      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2876      */
2877     show : function(el, pos, parentMenu)
2878     {
2879         if (false === this.fireEvent("beforeshow", this)) {
2880             Roo.log("show canceled");
2881             return;
2882         }
2883         this.parentMenu = parentMenu;
2884         if(!this.el){
2885             this.render();
2886         }
2887         
2888         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2889     },
2890      /**
2891      * Displays this menu at a specific xy position
2892      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2893      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2894      */
2895     showAt : function(xy, parentMenu, /* private: */_e){
2896         this.parentMenu = parentMenu;
2897         if(!this.el){
2898             this.render();
2899         }
2900         if(_e !== false){
2901             this.fireEvent("beforeshow", this);
2902             //xy = this.el.adjustForConstraints(xy);
2903         }
2904         
2905         //this.el.show();
2906         this.hideMenuItems();
2907         this.hidden = false;
2908         this.triggerEl.addClass('open');
2909         this.el.addClass('show');
2910         
2911         // reassign x when hitting right
2912         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2913             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2914         }
2915         
2916         // reassign y when hitting bottom
2917         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2918             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2919         }
2920         
2921         // but the list may align on trigger left or trigger top... should it be a properity?
2922         
2923         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2924             this.el.setXY(xy);
2925         }
2926         
2927         this.focus();
2928         this.fireEvent("show", this);
2929     },
2930     
2931     focus : function(){
2932         return;
2933         if(!this.hidden){
2934             this.doFocus.defer(50, this);
2935         }
2936     },
2937
2938     doFocus : function(){
2939         if(!this.hidden){
2940             this.focusEl.focus();
2941         }
2942     },
2943
2944     /**
2945      * Hides this menu and optionally all parent menus
2946      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2947      */
2948     hide : function(deep)
2949     {
2950         if (false === this.fireEvent("beforehide", this)) {
2951             Roo.log("hide canceled");
2952             return;
2953         }
2954         this.hideMenuItems();
2955         if(this.el && this.isVisible()){
2956            
2957             if(this.activeItem){
2958                 this.activeItem.deactivate();
2959                 this.activeItem = null;
2960             }
2961             this.triggerEl.removeClass('open');;
2962             this.el.removeClass('show');
2963             this.hidden = true;
2964             this.fireEvent("hide", this);
2965         }
2966         if(deep === true && this.parentMenu){
2967             this.parentMenu.hide(true);
2968         }
2969     },
2970     
2971     onTriggerClick : function(e)
2972     {
2973         Roo.log('trigger click');
2974         
2975         var target = e.getTarget();
2976         
2977         Roo.log(target.nodeName.toLowerCase());
2978         
2979         if(target.nodeName.toLowerCase() === 'i'){
2980             e.preventDefault();
2981         }
2982         
2983     },
2984     
2985     onTriggerPress  : function(e)
2986     {
2987         Roo.log('trigger press');
2988         //Roo.log(e.getTarget());
2989        // Roo.log(this.triggerEl.dom);
2990        
2991         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2992         var pel = Roo.get(e.getTarget());
2993         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2994             Roo.log('is treeview or dropdown?');
2995             return;
2996         }
2997         
2998         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2999             return;
3000         }
3001         
3002         if (this.isVisible()) {
3003             Roo.log('hide');
3004             this.hide();
3005         } else {
3006             Roo.log('show');
3007             this.show(this.triggerEl, '?', false);
3008         }
3009         
3010         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3011             e.stopEvent();
3012         }
3013         
3014     },
3015        
3016     
3017     hideMenuItems : function()
3018     {
3019         Roo.log("hide Menu Items");
3020         if (!this.el) { 
3021             return;
3022         }
3023         
3024         this.el.select('.open',true).each(function(aa) {
3025             
3026             aa.removeClass('open');
3027          
3028         });
3029     },
3030     addxtypeChild : function (tree, cntr) {
3031         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3032           
3033         this.menuitems.add(comp);
3034         return comp;
3035
3036     },
3037     getEl : function()
3038     {
3039         Roo.log(this.el);
3040         return this.el;
3041     },
3042     
3043     clear : function()
3044     {
3045         this.getEl().dom.innerHTML = '';
3046         this.menuitems.clear();
3047     }
3048 });
3049
3050  
3051  /*
3052  * - LGPL
3053  *
3054  * menu item
3055  * 
3056  */
3057
3058
3059 /**
3060  * @class Roo.bootstrap.MenuItem
3061  * @extends Roo.bootstrap.Component
3062  * Bootstrap MenuItem class
3063  * @cfg {String} html the menu label
3064  * @cfg {String} href the link
3065  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3066  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3067  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3068  * @cfg {String} fa favicon to show on left of menu item.
3069  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3070  * 
3071  * 
3072  * @constructor
3073  * Create a new MenuItem
3074  * @param {Object} config The config object
3075  */
3076
3077
3078 Roo.bootstrap.MenuItem = function(config){
3079     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3080     this.addEvents({
3081         // raw events
3082         /**
3083          * @event click
3084          * The raw click event for the entire grid.
3085          * @param {Roo.bootstrap.MenuItem} this
3086          * @param {Roo.EventObject} e
3087          */
3088         "click" : true
3089     });
3090 };
3091
3092 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3093     
3094     href : false,
3095     html : false,
3096     preventDefault: false,
3097     isContainer : false,
3098     active : false,
3099     fa: false,
3100     
3101     getAutoCreate : function(){
3102         
3103         if(this.isContainer){
3104             return {
3105                 tag: 'li',
3106                 cls: 'dropdown-menu-item '
3107             };
3108         }
3109         var ctag = {
3110             tag: 'span',
3111             html: 'Link'
3112         };
3113         
3114         var anc = {
3115             tag : 'a',
3116             cls : 'dropdown-item',
3117             href : '#',
3118             cn : [  ]
3119         };
3120         
3121         if (this.fa !== false) {
3122             anc.cn.push({
3123                 tag : 'i',
3124                 cls : 'fa fa-' + this.fa
3125             });
3126         }
3127         
3128         anc.cn.push(ctag);
3129         
3130         
3131         var cfg= {
3132             tag: 'li',
3133             cls: 'dropdown-menu-item',
3134             cn: [ anc ]
3135         };
3136         if (this.parent().type == 'treeview') {
3137             cfg.cls = 'treeview-menu';
3138         }
3139         if (this.active) {
3140             cfg.cls += ' active';
3141         }
3142         
3143         
3144         
3145         anc.href = this.href || cfg.cn[0].href ;
3146         ctag.html = this.html || cfg.cn[0].html ;
3147         return cfg;
3148     },
3149     
3150     initEvents: function()
3151     {
3152         if (this.parent().type == 'treeview') {
3153             this.el.select('a').on('click', this.onClick, this);
3154         }
3155         
3156         if (this.menu) {
3157             this.menu.parentType = this.xtype;
3158             this.menu.triggerEl = this.el;
3159             this.menu = this.addxtype(Roo.apply({}, this.menu));
3160         }
3161         
3162     },
3163     onClick : function(e)
3164     {
3165         Roo.log('item on click ');
3166         
3167         if(this.preventDefault){
3168             e.preventDefault();
3169         }
3170         //this.parent().hideMenuItems();
3171         
3172         this.fireEvent('click', this, e);
3173     },
3174     getEl : function()
3175     {
3176         return this.el;
3177     } 
3178 });
3179
3180  
3181
3182  /*
3183  * - LGPL
3184  *
3185  * menu separator
3186  * 
3187  */
3188
3189
3190 /**
3191  * @class Roo.bootstrap.MenuSeparator
3192  * @extends Roo.bootstrap.Component
3193  * Bootstrap MenuSeparator class
3194  * 
3195  * @constructor
3196  * Create a new MenuItem
3197  * @param {Object} config The config object
3198  */
3199
3200
3201 Roo.bootstrap.MenuSeparator = function(config){
3202     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3203 };
3204
3205 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3206     
3207     getAutoCreate : function(){
3208         var cfg = {
3209             cls: 'divider',
3210             tag : 'li'
3211         };
3212         
3213         return cfg;
3214     }
3215    
3216 });
3217
3218  
3219
3220  
3221 /*
3222 * Licence: LGPL
3223 */
3224
3225 /**
3226  * @class Roo.bootstrap.Modal
3227  * @extends Roo.bootstrap.Component
3228  * Bootstrap Modal class
3229  * @cfg {String} title Title of dialog
3230  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3231  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3232  * @cfg {Boolean} specificTitle default false
3233  * @cfg {Array} buttons Array of buttons or standard button set..
3234  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3235  * @cfg {Boolean} animate default true
3236  * @cfg {Boolean} allow_close default true
3237  * @cfg {Boolean} fitwindow default false
3238  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3239  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3240  * @cfg {String} size (sm|lg) default empty
3241  * @cfg {Number} max_width set the max width of modal
3242  *
3243  *
3244  * @constructor
3245  * Create a new Modal Dialog
3246  * @param {Object} config The config object
3247  */
3248
3249 Roo.bootstrap.Modal = function(config){
3250     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3251     this.addEvents({
3252         // raw events
3253         /**
3254          * @event btnclick
3255          * The raw btnclick event for the button
3256          * @param {Roo.EventObject} e
3257          */
3258         "btnclick" : true,
3259         /**
3260          * @event resize
3261          * Fire when dialog resize
3262          * @param {Roo.bootstrap.Modal} this
3263          * @param {Roo.EventObject} e
3264          */
3265         "resize" : true
3266     });
3267     this.buttons = this.buttons || [];
3268
3269     if (this.tmpl) {
3270         this.tmpl = Roo.factory(this.tmpl);
3271     }
3272
3273 };
3274
3275 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3276
3277     title : 'test dialog',
3278
3279     buttons : false,
3280
3281     // set on load...
3282
3283     html: false,
3284
3285     tmp: false,
3286
3287     specificTitle: false,
3288
3289     buttonPosition: 'right',
3290
3291     allow_close : true,
3292
3293     animate : true,
3294
3295     fitwindow: false,
3296     
3297      // private
3298     dialogEl: false,
3299     bodyEl:  false,
3300     footerEl:  false,
3301     titleEl:  false,
3302     closeEl:  false,
3303
3304     size: '',
3305     
3306     max_width: 0,
3307     
3308     max_height: 0,
3309     
3310     fit_content: false,
3311
3312     onRender : function(ct, position)
3313     {
3314         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3315
3316         if(!this.el){
3317             var cfg = Roo.apply({},  this.getAutoCreate());
3318             cfg.id = Roo.id();
3319             //if(!cfg.name){
3320             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3321             //}
3322             //if (!cfg.name.length) {
3323             //    delete cfg.name;
3324            // }
3325             if (this.cls) {
3326                 cfg.cls += ' ' + this.cls;
3327             }
3328             if (this.style) {
3329                 cfg.style = this.style;
3330             }
3331             this.el = Roo.get(document.body).createChild(cfg, position);
3332         }
3333         //var type = this.el.dom.type;
3334
3335
3336         if(this.tabIndex !== undefined){
3337             this.el.dom.setAttribute('tabIndex', this.tabIndex);
3338         }
3339
3340         this.dialogEl = this.el.select('.modal-dialog',true).first();
3341         this.bodyEl = this.el.select('.modal-body',true).first();
3342         this.closeEl = this.el.select('.modal-header .close', true).first();
3343         this.headerEl = this.el.select('.modal-header',true).first();
3344         this.titleEl = this.el.select('.modal-title',true).first();
3345         this.footerEl = this.el.select('.modal-footer',true).first();
3346
3347         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
3348         
3349         //this.el.addClass("x-dlg-modal");
3350
3351         if (this.buttons.length) {
3352             Roo.each(this.buttons, function(bb) {
3353                 var b = Roo.apply({}, bb);
3354                 b.xns = b.xns || Roo.bootstrap;
3355                 b.xtype = b.xtype || 'Button';
3356                 if (typeof(b.listeners) == 'undefined') {
3357                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
3358                 }
3359
3360                 var btn = Roo.factory(b);
3361
3362                 btn.render(this.getButtonContainer());
3363
3364             },this);
3365         }
3366         // render the children.
3367         var nitems = [];
3368
3369         if(typeof(this.items) != 'undefined'){
3370             var items = this.items;
3371             delete this.items;
3372
3373             for(var i =0;i < items.length;i++) {
3374                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
3375             }
3376         }
3377
3378         this.items = nitems;
3379
3380         // where are these used - they used to be body/close/footer
3381
3382
3383         this.initEvents();
3384         //this.el.addClass([this.fieldClass, this.cls]);
3385
3386     },
3387
3388     getAutoCreate : function()
3389     {
3390         // we will default to modal-body-overflow - might need to remove or make optional later.
3391         var bdy = {
3392                 cls : 'modal-body enable-modal-body-overflow ', 
3393                 html : this.html || ''
3394         };
3395
3396         var title = {
3397             tag: 'h4',
3398             cls : 'modal-title',
3399             html : this.title
3400         };
3401
3402         if(this.specificTitle){
3403             title = this.title;
3404
3405         }
3406
3407         var header = [];
3408         if (this.allow_close && Roo.bootstrap.version == 3) {
3409             header.push({
3410                 tag: 'button',
3411                 cls : 'close',
3412                 html : '&times'
3413             });
3414         }
3415
3416         header.push(title);
3417
3418         if (this.allow_close && Roo.bootstrap.version == 4) {
3419             header.push({
3420                 tag: 'button',
3421                 cls : 'close',
3422                 html : '&times'
3423             });
3424         }
3425         
3426         var size = '';
3427
3428         if(this.size.length){
3429             size = 'modal-' + this.size;
3430         }
3431         
3432         var footer = Roo.bootstrap.version == 3 ?
3433             {
3434                 cls : 'modal-footer',
3435                 cn : [
3436                     {
3437                         tag: 'div',
3438                         cls: 'btn-' + this.buttonPosition
3439                     }
3440                 ]
3441
3442             } :
3443             {  // BS4 uses mr-auto on left buttons....
3444                 cls : 'modal-footer'
3445             };
3446
3447             
3448
3449         
3450         
3451         var modal = {
3452             cls: "modal",
3453              cn : [
3454                 {
3455                     cls: "modal-dialog " + size,
3456                     cn : [
3457                         {
3458                             cls : "modal-content",
3459                             cn : [
3460                                 {
3461                                     cls : 'modal-header',
3462                                     cn : header
3463                                 },
3464                                 bdy,
3465                                 footer
3466                             ]
3467
3468                         }
3469                     ]
3470
3471                 }
3472             ]
3473         };
3474
3475         if(this.animate){
3476             modal.cls += ' fade';
3477         }
3478
3479         return modal;
3480
3481     },
3482     getChildContainer : function() {
3483
3484          return this.bodyEl;
3485
3486     },
3487     getButtonContainer : function() {
3488         
3489          return Roo.bootstrap.version == 4 ?
3490             this.el.select('.modal-footer',true).first()
3491             : this.el.select('.modal-footer div',true).first();
3492
3493     },
3494     initEvents : function()
3495     {
3496         if (this.allow_close) {
3497             this.closeEl.on('click', this.hide, this);
3498         }
3499         Roo.EventManager.onWindowResize(this.resize, this, true);
3500
3501
3502     },
3503   
3504
3505     resize : function()
3506     {
3507         this.maskEl.setSize(
3508             Roo.lib.Dom.getViewWidth(true),
3509             Roo.lib.Dom.getViewHeight(true)
3510         );
3511         
3512         if (this.fitwindow) {
3513             
3514            
3515             this.setSize(
3516                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
3517                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
3518             );
3519             return;
3520         }
3521         
3522         if(this.max_width !== 0) {
3523             
3524             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
3525             
3526             if(this.height) {
3527                 this.setSize(w, this.height);
3528                 return;
3529             }
3530             
3531             if(this.max_height) {
3532                 this.setSize(w,Math.min(
3533                     this.max_height,
3534                     Roo.lib.Dom.getViewportHeight(true) - 60
3535                 ));
3536                 
3537                 return;
3538             }
3539             
3540             if(!this.fit_content) {
3541                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
3542                 return;
3543             }
3544             
3545             this.setSize(w, Math.min(
3546                 60 +
3547                 this.headerEl.getHeight() + 
3548                 this.footerEl.getHeight() + 
3549                 this.getChildHeight(this.bodyEl.dom.childNodes),
3550                 Roo.lib.Dom.getViewportHeight(true) - 60)
3551             );
3552         }
3553         
3554     },
3555
3556     setSize : function(w,h)
3557     {
3558         if (!w && !h) {
3559             return;
3560         }
3561         
3562         this.resizeTo(w,h);
3563     },
3564
3565     show : function() {
3566
3567         if (!this.rendered) {
3568             this.render();
3569         }
3570
3571         //this.el.setStyle('display', 'block');
3572         this.el.removeClass('hideing');
3573         this.el.dom.style.display='block';
3574         
3575         Roo.get(document.body).addClass('modal-open');
3576  
3577         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
3578             
3579             (function(){
3580                 this.el.addClass('show');
3581                 this.el.addClass('in');
3582             }).defer(50, this);
3583         }else{
3584             this.el.addClass('show');
3585             this.el.addClass('in');
3586         }
3587
3588         // not sure how we can show data in here..
3589         //if (this.tmpl) {
3590         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3591         //}
3592
3593         Roo.get(document.body).addClass("x-body-masked");
3594         
3595         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3596         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3597         this.maskEl.dom.style.display = 'block';
3598         this.maskEl.addClass('show');
3599         
3600         
3601         this.resize();
3602         
3603         this.fireEvent('show', this);
3604
3605         // set zindex here - otherwise it appears to be ignored...
3606         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3607
3608         (function () {
3609             this.items.forEach( function(e) {
3610                 e.layout ? e.layout() : false;
3611
3612             });
3613         }).defer(100,this);
3614
3615     },
3616     hide : function()
3617     {
3618         if(this.fireEvent("beforehide", this) !== false){
3619             
3620             this.maskEl.removeClass('show');
3621             
3622             this.maskEl.dom.style.display = '';
3623             Roo.get(document.body).removeClass("x-body-masked");
3624             this.el.removeClass('in');
3625             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3626
3627             if(this.animate){ // why
3628                 this.el.addClass('hideing');
3629                 this.el.removeClass('show');
3630                 (function(){
3631                     if (!this.el.hasClass('hideing')) {
3632                         return; // it's been shown again...
3633                     }
3634                     
3635                     this.el.dom.style.display='';
3636
3637                     Roo.get(document.body).removeClass('modal-open');
3638                     this.el.removeClass('hideing');
3639                 }).defer(150,this);
3640                 
3641             }else{
3642                 this.el.removeClass('show');
3643                 this.el.dom.style.display='';
3644                 Roo.get(document.body).removeClass('modal-open');
3645
3646             }
3647             this.fireEvent('hide', this);
3648         }
3649     },
3650     isVisible : function()
3651     {
3652         
3653         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3654         
3655     },
3656
3657     addButton : function(str, cb)
3658     {
3659
3660
3661         var b = Roo.apply({}, { html : str } );
3662         b.xns = b.xns || Roo.bootstrap;
3663         b.xtype = b.xtype || 'Button';
3664         if (typeof(b.listeners) == 'undefined') {
3665             b.listeners = { click : cb.createDelegate(this)  };
3666         }
3667
3668         var btn = Roo.factory(b);
3669
3670         btn.render(this.getButtonContainer());
3671
3672         return btn;
3673
3674     },
3675
3676     setDefaultButton : function(btn)
3677     {
3678         //this.el.select('.modal-footer').()
3679     },
3680
3681     resizeTo: function(w,h)
3682     {
3683         this.dialogEl.setWidth(w);
3684         
3685         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
3686
3687         this.bodyEl.setHeight(h - diff);
3688         
3689         this.fireEvent('resize', this);
3690     },
3691     
3692     setContentSize  : function(w, h)
3693     {
3694
3695     },
3696     onButtonClick: function(btn,e)
3697     {
3698         //Roo.log([a,b,c]);
3699         this.fireEvent('btnclick', btn.name, e);
3700     },
3701      /**
3702      * Set the title of the Dialog
3703      * @param {String} str new Title
3704      */
3705     setTitle: function(str) {
3706         this.titleEl.dom.innerHTML = str;
3707     },
3708     /**
3709      * Set the body of the Dialog
3710      * @param {String} str new Title
3711      */
3712     setBody: function(str) {
3713         this.bodyEl.dom.innerHTML = str;
3714     },
3715     /**
3716      * Set the body of the Dialog using the template
3717      * @param {Obj} data - apply this data to the template and replace the body contents.
3718      */
3719     applyBody: function(obj)
3720     {
3721         if (!this.tmpl) {
3722             Roo.log("Error - using apply Body without a template");
3723             //code
3724         }
3725         this.tmpl.overwrite(this.bodyEl, obj);
3726     },
3727     
3728     getChildHeight : function(child_nodes)
3729     {
3730         if(
3731             !child_nodes ||
3732             child_nodes.length == 0
3733         ) {
3734             return;
3735         }
3736         
3737         var child_height = 0;
3738         
3739         for(var i = 0; i < child_nodes.length; i++) {
3740             
3741             /*
3742             * for modal with tabs...
3743             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3744                 
3745                 var layout_childs = child_nodes[i].childNodes;
3746                 
3747                 for(var j = 0; j < layout_childs.length; j++) {
3748                     
3749                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3750                         
3751                         var layout_body_childs = layout_childs[j].childNodes;
3752                         
3753                         for(var k = 0; k < layout_body_childs.length; k++) {
3754                             
3755                             if(layout_body_childs[k].classList.contains('navbar')) {
3756                                 child_height += layout_body_childs[k].offsetHeight;
3757                                 continue;
3758                             }
3759                             
3760                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3761                                 
3762                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3763                                 
3764                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3765                                     
3766                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3767                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3768                                         continue;
3769                                     }
3770                                     
3771                                 }
3772                                 
3773                             }
3774                             
3775                         }
3776                     }
3777                 }
3778                 continue;
3779             }
3780             */
3781             
3782             child_height += child_nodes[i].offsetHeight;
3783             // Roo.log(child_nodes[i].offsetHeight);
3784         }
3785         
3786         return child_height;
3787     }
3788
3789 });
3790
3791
3792 Roo.apply(Roo.bootstrap.Modal,  {
3793     /**
3794          * Button config that displays a single OK button
3795          * @type Object
3796          */
3797         OK :  [{
3798             name : 'ok',
3799             weight : 'primary',
3800             html : 'OK'
3801         }],
3802         /**
3803          * Button config that displays Yes and No buttons
3804          * @type Object
3805          */
3806         YESNO : [
3807             {
3808                 name  : 'no',
3809                 html : 'No'
3810             },
3811             {
3812                 name  :'yes',
3813                 weight : 'primary',
3814                 html : 'Yes'
3815             }
3816         ],
3817
3818         /**
3819          * Button config that displays OK and Cancel buttons
3820          * @type Object
3821          */
3822         OKCANCEL : [
3823             {
3824                name : 'cancel',
3825                 html : 'Cancel'
3826             },
3827             {
3828                 name : 'ok',
3829                 weight : 'primary',
3830                 html : 'OK'
3831             }
3832         ],
3833         /**
3834          * Button config that displays Yes, No and Cancel buttons
3835          * @type Object
3836          */
3837         YESNOCANCEL : [
3838             {
3839                 name : 'yes',
3840                 weight : 'primary',
3841                 html : 'Yes'
3842             },
3843             {
3844                 name : 'no',
3845                 html : 'No'
3846             },
3847             {
3848                 name : 'cancel',
3849                 html : 'Cancel'
3850             }
3851         ],
3852         
3853         zIndex : 10001
3854 });
3855 /*
3856  * - LGPL
3857  *
3858  * messagebox - can be used as a replace
3859  * 
3860  */
3861 /**
3862  * @class Roo.MessageBox
3863  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3864  * Example usage:
3865  *<pre><code>
3866 // Basic alert:
3867 Roo.Msg.alert('Status', 'Changes saved successfully.');
3868
3869 // Prompt for user data:
3870 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3871     if (btn == 'ok'){
3872         // process text value...
3873     }
3874 });
3875
3876 // Show a dialog using config options:
3877 Roo.Msg.show({
3878    title:'Save Changes?',
3879    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3880    buttons: Roo.Msg.YESNOCANCEL,
3881    fn: processResult,
3882    animEl: 'elId'
3883 });
3884 </code></pre>
3885  * @singleton
3886  */
3887 Roo.bootstrap.MessageBox = function(){
3888     var dlg, opt, mask, waitTimer;
3889     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3890     var buttons, activeTextEl, bwidth;
3891
3892     
3893     // private
3894     var handleButton = function(button){
3895         dlg.hide();
3896         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3897     };
3898
3899     // private
3900     var handleHide = function(){
3901         if(opt && opt.cls){
3902             dlg.el.removeClass(opt.cls);
3903         }
3904         //if(waitTimer){
3905         //    Roo.TaskMgr.stop(waitTimer);
3906         //    waitTimer = null;
3907         //}
3908     };
3909
3910     // private
3911     var updateButtons = function(b){
3912         var width = 0;
3913         if(!b){
3914             buttons["ok"].hide();
3915             buttons["cancel"].hide();
3916             buttons["yes"].hide();
3917             buttons["no"].hide();
3918             dlg.footerEl.hide();
3919             
3920             return width;
3921         }
3922         dlg.footerEl.show();
3923         for(var k in buttons){
3924             if(typeof buttons[k] != "function"){
3925                 if(b[k]){
3926                     buttons[k].show();
3927                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3928                     width += buttons[k].el.getWidth()+15;
3929                 }else{
3930                     buttons[k].hide();
3931                 }
3932             }
3933         }
3934         return width;
3935     };
3936
3937     // private
3938     var handleEsc = function(d, k, e){
3939         if(opt && opt.closable !== false){
3940             dlg.hide();
3941         }
3942         if(e){
3943             e.stopEvent();
3944         }
3945     };
3946
3947     return {
3948         /**
3949          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3950          * @return {Roo.BasicDialog} The BasicDialog element
3951          */
3952         getDialog : function(){
3953            if(!dlg){
3954                 dlg = new Roo.bootstrap.Modal( {
3955                     //draggable: true,
3956                     //resizable:false,
3957                     //constraintoviewport:false,
3958                     //fixedcenter:true,
3959                     //collapsible : false,
3960                     //shim:true,
3961                     //modal: true,
3962                 //    width: 'auto',
3963                   //  height:100,
3964                     //buttonAlign:"center",
3965                     closeClick : function(){
3966                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3967                             handleButton("no");
3968                         }else{
3969                             handleButton("cancel");
3970                         }
3971                     }
3972                 });
3973                 dlg.render();
3974                 dlg.on("hide", handleHide);
3975                 mask = dlg.mask;
3976                 //dlg.addKeyListener(27, handleEsc);
3977                 buttons = {};
3978                 this.buttons = buttons;
3979                 var bt = this.buttonText;
3980                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3981                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3982                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3983                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3984                 //Roo.log(buttons);
3985                 bodyEl = dlg.bodyEl.createChild({
3986
3987                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3988                         '<textarea class="roo-mb-textarea"></textarea>' +
3989                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3990                 });
3991                 msgEl = bodyEl.dom.firstChild;
3992                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3993                 textboxEl.enableDisplayMode();
3994                 textboxEl.addKeyListener([10,13], function(){
3995                     if(dlg.isVisible() && opt && opt.buttons){
3996                         if(opt.buttons.ok){
3997                             handleButton("ok");
3998                         }else if(opt.buttons.yes){
3999                             handleButton("yes");
4000                         }
4001                     }
4002                 });
4003                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4004                 textareaEl.enableDisplayMode();
4005                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4006                 progressEl.enableDisplayMode();
4007                 
4008                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4009                 var pf = progressEl.dom.firstChild;
4010                 if (pf) {
4011                     pp = Roo.get(pf.firstChild);
4012                     pp.setHeight(pf.offsetHeight);
4013                 }
4014                 
4015             }
4016             return dlg;
4017         },
4018
4019         /**
4020          * Updates the message box body text
4021          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4022          * the XHTML-compliant non-breaking space character '&amp;#160;')
4023          * @return {Roo.MessageBox} This message box
4024          */
4025         updateText : function(text)
4026         {
4027             if(!dlg.isVisible() && !opt.width){
4028                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4029                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4030             }
4031             msgEl.innerHTML = text || '&#160;';
4032       
4033             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4034             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4035             var w = Math.max(
4036                     Math.min(opt.width || cw , this.maxWidth), 
4037                     Math.max(opt.minWidth || this.minWidth, bwidth)
4038             );
4039             if(opt.prompt){
4040                 activeTextEl.setWidth(w);
4041             }
4042             if(dlg.isVisible()){
4043                 dlg.fixedcenter = false;
4044             }
4045             // to big, make it scroll. = But as usual stupid IE does not support
4046             // !important..
4047             
4048             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4049                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4050                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4051             } else {
4052                 bodyEl.dom.style.height = '';
4053                 bodyEl.dom.style.overflowY = '';
4054             }
4055             if (cw > w) {
4056                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4057             } else {
4058                 bodyEl.dom.style.overflowX = '';
4059             }
4060             
4061             dlg.setContentSize(w, bodyEl.getHeight());
4062             if(dlg.isVisible()){
4063                 dlg.fixedcenter = true;
4064             }
4065             return this;
4066         },
4067
4068         /**
4069          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4070          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4071          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4072          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4073          * @return {Roo.MessageBox} This message box
4074          */
4075         updateProgress : function(value, text){
4076             if(text){
4077                 this.updateText(text);
4078             }
4079             
4080             if (pp) { // weird bug on my firefox - for some reason this is not defined
4081                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4082                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4083             }
4084             return this;
4085         },        
4086
4087         /**
4088          * Returns true if the message box is currently displayed
4089          * @return {Boolean} True if the message box is visible, else false
4090          */
4091         isVisible : function(){
4092             return dlg && dlg.isVisible();  
4093         },
4094
4095         /**
4096          * Hides the message box if it is displayed
4097          */
4098         hide : function(){
4099             if(this.isVisible()){
4100                 dlg.hide();
4101             }  
4102         },
4103
4104         /**
4105          * Displays a new message box, or reinitializes an existing message box, based on the config options
4106          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4107          * The following config object properties are supported:
4108          * <pre>
4109 Property    Type             Description
4110 ----------  ---------------  ------------------------------------------------------------------------------------
4111 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4112                                    closes (defaults to undefined)
4113 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4114                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4115 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4116                                    progress and wait dialogs will ignore this property and always hide the
4117                                    close button as they can only be closed programmatically.
4118 cls               String           A custom CSS class to apply to the message box element
4119 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4120                                    displayed (defaults to 75)
4121 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4122                                    function will be btn (the name of the button that was clicked, if applicable,
4123                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4124                                    Progress and wait dialogs will ignore this option since they do not respond to
4125                                    user actions and can only be closed programmatically, so any required function
4126                                    should be called by the same code after it closes the dialog.
4127 icon              String           A CSS class that provides a background image to be used as an icon for
4128                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4129 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4130 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4131 modal             Boolean          False to allow user interaction with the page while the message box is
4132                                    displayed (defaults to true)
4133 msg               String           A string that will replace the existing message box body text (defaults
4134                                    to the XHTML-compliant non-breaking space character '&#160;')
4135 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4136 progress          Boolean          True to display a progress bar (defaults to false)
4137 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4138 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4139 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4140 title             String           The title text
4141 value             String           The string value to set into the active textbox element if displayed
4142 wait              Boolean          True to display a progress bar (defaults to false)
4143 width             Number           The width of the dialog in pixels
4144 </pre>
4145          *
4146          * Example usage:
4147          * <pre><code>
4148 Roo.Msg.show({
4149    title: 'Address',
4150    msg: 'Please enter your address:',
4151    width: 300,
4152    buttons: Roo.MessageBox.OKCANCEL,
4153    multiline: true,
4154    fn: saveAddress,
4155    animEl: 'addAddressBtn'
4156 });
4157 </code></pre>
4158          * @param {Object} config Configuration options
4159          * @return {Roo.MessageBox} This message box
4160          */
4161         show : function(options)
4162         {
4163             
4164             // this causes nightmares if you show one dialog after another
4165             // especially on callbacks..
4166              
4167             if(this.isVisible()){
4168                 
4169                 this.hide();
4170                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4171                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4172                 Roo.log("New Dialog Message:" +  options.msg )
4173                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4174                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4175                 
4176             }
4177             var d = this.getDialog();
4178             opt = options;
4179             d.setTitle(opt.title || "&#160;");
4180             d.closeEl.setDisplayed(opt.closable !== false);
4181             activeTextEl = textboxEl;
4182             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4183             if(opt.prompt){
4184                 if(opt.multiline){
4185                     textboxEl.hide();
4186                     textareaEl.show();
4187                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4188                         opt.multiline : this.defaultTextHeight);
4189                     activeTextEl = textareaEl;
4190                 }else{
4191                     textboxEl.show();
4192                     textareaEl.hide();
4193                 }
4194             }else{
4195                 textboxEl.hide();
4196                 textareaEl.hide();
4197             }
4198             progressEl.setDisplayed(opt.progress === true);
4199             if (opt.progress) {
4200                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4201             }
4202             this.updateProgress(0);
4203             activeTextEl.dom.value = opt.value || "";
4204             if(opt.prompt){
4205                 dlg.setDefaultButton(activeTextEl);
4206             }else{
4207                 var bs = opt.buttons;
4208                 var db = null;
4209                 if(bs && bs.ok){
4210                     db = buttons["ok"];
4211                 }else if(bs && bs.yes){
4212                     db = buttons["yes"];
4213                 }
4214                 dlg.setDefaultButton(db);
4215             }
4216             bwidth = updateButtons(opt.buttons);
4217             this.updateText(opt.msg);
4218             if(opt.cls){
4219                 d.el.addClass(opt.cls);
4220             }
4221             d.proxyDrag = opt.proxyDrag === true;
4222             d.modal = opt.modal !== false;
4223             d.mask = opt.modal !== false ? mask : false;
4224             if(!d.isVisible()){
4225                 // force it to the end of the z-index stack so it gets a cursor in FF
4226                 document.body.appendChild(dlg.el.dom);
4227                 d.animateTarget = null;
4228                 d.show(options.animEl);
4229             }
4230             return this;
4231         },
4232
4233         /**
4234          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
4235          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
4236          * and closing the message box when the process is complete.
4237          * @param {String} title The title bar text
4238          * @param {String} msg The message box body text
4239          * @return {Roo.MessageBox} This message box
4240          */
4241         progress : function(title, msg){
4242             this.show({
4243                 title : title,
4244                 msg : msg,
4245                 buttons: false,
4246                 progress:true,
4247                 closable:false,
4248                 minWidth: this.minProgressWidth,
4249                 modal : true
4250             });
4251             return this;
4252         },
4253
4254         /**
4255          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
4256          * If a callback function is passed it will be called after the user clicks the button, and the
4257          * id of the button that was clicked will be passed as the only parameter to the callback
4258          * (could also be the top-right close button).
4259          * @param {String} title The title bar text
4260          * @param {String} msg The message box body text
4261          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4262          * @param {Object} scope (optional) The scope of the callback function
4263          * @return {Roo.MessageBox} This message box
4264          */
4265         alert : function(title, msg, fn, scope)
4266         {
4267             this.show({
4268                 title : title,
4269                 msg : msg,
4270                 buttons: this.OK,
4271                 fn: fn,
4272                 closable : false,
4273                 scope : scope,
4274                 modal : true
4275             });
4276             return this;
4277         },
4278
4279         /**
4280          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
4281          * interaction while waiting for a long-running process to complete that does not have defined intervals.
4282          * You are responsible for closing the message box when the process is complete.
4283          * @param {String} msg The message box body text
4284          * @param {String} title (optional) The title bar text
4285          * @return {Roo.MessageBox} This message box
4286          */
4287         wait : function(msg, title){
4288             this.show({
4289                 title : title,
4290                 msg : msg,
4291                 buttons: false,
4292                 closable:false,
4293                 progress:true,
4294                 modal:true,
4295                 width:300,
4296                 wait:true
4297             });
4298             waitTimer = Roo.TaskMgr.start({
4299                 run: function(i){
4300                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
4301                 },
4302                 interval: 1000
4303             });
4304             return this;
4305         },
4306
4307         /**
4308          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
4309          * If a callback function is passed it will be called after the user clicks either button, and the id of the
4310          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
4311          * @param {String} title The title bar text
4312          * @param {String} msg The message box body text
4313          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4314          * @param {Object} scope (optional) The scope of the callback function
4315          * @return {Roo.MessageBox} This message box
4316          */
4317         confirm : function(title, msg, fn, scope){
4318             this.show({
4319                 title : title,
4320                 msg : msg,
4321                 buttons: this.YESNO,
4322                 fn: fn,
4323                 scope : scope,
4324                 modal : true
4325             });
4326             return this;
4327         },
4328
4329         /**
4330          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
4331          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
4332          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
4333          * (could also be the top-right close button) and the text that was entered will be passed as the two
4334          * parameters to the callback.
4335          * @param {String} title The title bar text
4336          * @param {String} msg The message box body text
4337          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4338          * @param {Object} scope (optional) The scope of the callback function
4339          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
4340          * property, or the height in pixels to create the textbox (defaults to false / single-line)
4341          * @return {Roo.MessageBox} This message box
4342          */
4343         prompt : function(title, msg, fn, scope, multiline){
4344             this.show({
4345                 title : title,
4346                 msg : msg,
4347                 buttons: this.OKCANCEL,
4348                 fn: fn,
4349                 minWidth:250,
4350                 scope : scope,
4351                 prompt:true,
4352                 multiline: multiline,
4353                 modal : true
4354             });
4355             return this;
4356         },
4357
4358         /**
4359          * Button config that displays a single OK button
4360          * @type Object
4361          */
4362         OK : {ok:true},
4363         /**
4364          * Button config that displays Yes and No buttons
4365          * @type Object
4366          */
4367         YESNO : {yes:true, no:true},
4368         /**
4369          * Button config that displays OK and Cancel buttons
4370          * @type Object
4371          */
4372         OKCANCEL : {ok:true, cancel:true},
4373         /**
4374          * Button config that displays Yes, No and Cancel buttons
4375          * @type Object
4376          */
4377         YESNOCANCEL : {yes:true, no:true, cancel:true},
4378
4379         /**
4380          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
4381          * @type Number
4382          */
4383         defaultTextHeight : 75,
4384         /**
4385          * The maximum width in pixels of the message box (defaults to 600)
4386          * @type Number
4387          */
4388         maxWidth : 600,
4389         /**
4390          * The minimum width in pixels of the message box (defaults to 100)
4391          * @type Number
4392          */
4393         minWidth : 100,
4394         /**
4395          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
4396          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
4397          * @type Number
4398          */
4399         minProgressWidth : 250,
4400         /**
4401          * An object containing the default button text strings that can be overriden for localized language support.
4402          * Supported properties are: ok, cancel, yes and no.
4403          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
4404          * @type Object
4405          */
4406         buttonText : {
4407             ok : "OK",
4408             cancel : "Cancel",
4409             yes : "Yes",
4410             no : "No"
4411         }
4412     };
4413 }();
4414
4415 /**
4416  * Shorthand for {@link Roo.MessageBox}
4417  */
4418 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
4419 Roo.Msg = Roo.Msg || Roo.MessageBox;
4420 /*
4421  * - LGPL
4422  *
4423  * navbar
4424  * 
4425  */
4426
4427 /**
4428  * @class Roo.bootstrap.Navbar
4429  * @extends Roo.bootstrap.Component
4430  * Bootstrap Navbar class
4431
4432  * @constructor
4433  * Create a new Navbar
4434  * @param {Object} config The config object
4435  */
4436
4437
4438 Roo.bootstrap.Navbar = function(config){
4439     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
4440     this.addEvents({
4441         // raw events
4442         /**
4443          * @event beforetoggle
4444          * Fire before toggle the menu
4445          * @param {Roo.EventObject} e
4446          */
4447         "beforetoggle" : true
4448     });
4449 };
4450
4451 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
4452     
4453     
4454    
4455     // private
4456     navItems : false,
4457     loadMask : false,
4458     
4459     
4460     getAutoCreate : function(){
4461         
4462         
4463         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
4464         
4465     },
4466     
4467     initEvents :function ()
4468     {
4469         //Roo.log(this.el.select('.navbar-toggle',true));
4470         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
4471         
4472         var mark = {
4473             tag: "div",
4474             cls:"x-dlg-mask"
4475         };
4476         
4477         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
4478         
4479         var size = this.el.getSize();
4480         this.maskEl.setSize(size.width, size.height);
4481         this.maskEl.enableDisplayMode("block");
4482         this.maskEl.hide();
4483         
4484         if(this.loadMask){
4485             this.maskEl.show();
4486         }
4487     },
4488     
4489     
4490     getChildContainer : function()
4491     {
4492         if (this.el && this.el.select('.collapse').getCount()) {
4493             return this.el.select('.collapse',true).first();
4494         }
4495         
4496         return this.el;
4497     },
4498     
4499     mask : function()
4500     {
4501         this.maskEl.show();
4502     },
4503     
4504     unmask : function()
4505     {
4506         this.maskEl.hide();
4507     },
4508     onToggle : function()
4509     {
4510         
4511         if(this.fireEvent('beforetoggle', this) === false){
4512             return;
4513         }
4514         var ce = this.el.select('.navbar-collapse',true).first();
4515       
4516         if (!ce.hasClass('show')) {
4517            this.expand();
4518         } else {
4519             this.collapse();
4520         }
4521         
4522         
4523     
4524     },
4525     /**
4526      * Expand the navbar pulldown 
4527      */
4528     expand : function ()
4529     {
4530        
4531         var ce = this.el.select('.navbar-collapse',true).first();
4532         if (ce.hasClass('collapsing')) {
4533             return;
4534         }
4535         ce.dom.style.height = '';
4536                // show it...
4537         ce.addClass('in'); // old...
4538         ce.removeClass('collapse');
4539         ce.addClass('show');
4540         var h = ce.getHeight();
4541         Roo.log(h);
4542         ce.removeClass('show');
4543         // at this point we should be able to see it..
4544         ce.addClass('collapsing');
4545         
4546         ce.setHeight(0); // resize it ...
4547         ce.on('transitionend', function() {
4548             //Roo.log('done transition');
4549             ce.removeClass('collapsing');
4550             ce.addClass('show');
4551             ce.removeClass('collapse');
4552
4553             ce.dom.style.height = '';
4554         }, this, { single: true} );
4555         ce.setHeight(h);
4556         ce.dom.scrollTop = 0;
4557     },
4558     /**
4559      * Collapse the navbar pulldown 
4560      */
4561     collapse : function()
4562     {
4563          var ce = this.el.select('.navbar-collapse',true).first();
4564        
4565         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
4566             // it's collapsed or collapsing..
4567             return;
4568         }
4569         ce.removeClass('in'); // old...
4570         ce.setHeight(ce.getHeight());
4571         ce.removeClass('show');
4572         ce.addClass('collapsing');
4573         
4574         ce.on('transitionend', function() {
4575             ce.dom.style.height = '';
4576             ce.removeClass('collapsing');
4577             ce.addClass('collapse');
4578         }, this, { single: true} );
4579         ce.setHeight(0);
4580     }
4581     
4582     
4583     
4584 });
4585
4586
4587
4588  
4589
4590  /*
4591  * - LGPL
4592  *
4593  * navbar
4594  * 
4595  */
4596
4597 /**
4598  * @class Roo.bootstrap.NavSimplebar
4599  * @extends Roo.bootstrap.Navbar
4600  * Bootstrap Sidebar class
4601  *
4602  * @cfg {Boolean} inverse is inverted color
4603  * 
4604  * @cfg {String} type (nav | pills | tabs)
4605  * @cfg {Boolean} arrangement stacked | justified
4606  * @cfg {String} align (left | right) alignment
4607  * 
4608  * @cfg {Boolean} main (true|false) main nav bar? default false
4609  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4610  * 
4611  * @cfg {String} tag (header|footer|nav|div) default is nav 
4612
4613  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4614  * 
4615  * 
4616  * @constructor
4617  * Create a new Sidebar
4618  * @param {Object} config The config object
4619  */
4620
4621
4622 Roo.bootstrap.NavSimplebar = function(config){
4623     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4624 };
4625
4626 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4627     
4628     inverse: false,
4629     
4630     type: false,
4631     arrangement: '',
4632     align : false,
4633     
4634     weight : 'light',
4635     
4636     main : false,
4637     
4638     
4639     tag : false,
4640     
4641     
4642     getAutoCreate : function(){
4643         
4644         
4645         var cfg = {
4646             tag : this.tag || 'div',
4647             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
4648         };
4649         if (['light','white'].indexOf(this.weight) > -1) {
4650             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4651         }
4652         cfg.cls += ' bg-' + this.weight;
4653         
4654         if (this.inverse) {
4655             cfg.cls += ' navbar-inverse';
4656             
4657         }
4658         
4659         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4660         
4661         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
4662             return cfg;
4663         }
4664         
4665         
4666     
4667         
4668         cfg.cn = [
4669             {
4670                 cls: 'nav nav-' + this.xtype,
4671                 tag : 'ul'
4672             }
4673         ];
4674         
4675          
4676         this.type = this.type || 'nav';
4677         if (['tabs','pills'].indexOf(this.type) != -1) {
4678             cfg.cn[0].cls += ' nav-' + this.type
4679         
4680         
4681         } else {
4682             if (this.type!=='nav') {
4683                 Roo.log('nav type must be nav/tabs/pills')
4684             }
4685             cfg.cn[0].cls += ' navbar-nav'
4686         }
4687         
4688         
4689         
4690         
4691         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4692             cfg.cn[0].cls += ' nav-' + this.arrangement;
4693         }
4694         
4695         
4696         if (this.align === 'right') {
4697             cfg.cn[0].cls += ' navbar-right';
4698         }
4699         
4700         
4701         
4702         
4703         return cfg;
4704     
4705         
4706     }
4707     
4708     
4709     
4710 });
4711
4712
4713
4714  
4715
4716  
4717        /*
4718  * - LGPL
4719  *
4720  * navbar
4721  * navbar-fixed-top
4722  * navbar-expand-md  fixed-top 
4723  */
4724
4725 /**
4726  * @class Roo.bootstrap.NavHeaderbar
4727  * @extends Roo.bootstrap.NavSimplebar
4728  * Bootstrap Sidebar class
4729  *
4730  * @cfg {String} brand what is brand
4731  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4732  * @cfg {String} brand_href href of the brand
4733  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4734  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4735  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4736  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4737  * 
4738  * @constructor
4739  * Create a new Sidebar
4740  * @param {Object} config The config object
4741  */
4742
4743
4744 Roo.bootstrap.NavHeaderbar = function(config){
4745     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4746       
4747 };
4748
4749 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4750     
4751     position: '',
4752     brand: '',
4753     brand_href: false,
4754     srButton : true,
4755     autohide : false,
4756     desktopCenter : false,
4757    
4758     
4759     getAutoCreate : function(){
4760         
4761         var   cfg = {
4762             tag: this.nav || 'nav',
4763             cls: 'navbar navbar-expand-md',
4764             role: 'navigation',
4765             cn: []
4766         };
4767         
4768         var cn = cfg.cn;
4769         if (this.desktopCenter) {
4770             cn.push({cls : 'container', cn : []});
4771             cn = cn[0].cn;
4772         }
4773         
4774         if(this.srButton){
4775             var btn = {
4776                 tag: 'button',
4777                 type: 'button',
4778                 cls: 'navbar-toggle navbar-toggler',
4779                 'data-toggle': 'collapse',
4780                 cn: [
4781                     {
4782                         tag: 'span',
4783                         cls: 'sr-only',
4784                         html: 'Toggle navigation'
4785                     },
4786                     {
4787                         tag: 'span',
4788                         cls: 'icon-bar navbar-toggler-icon'
4789                     },
4790                     {
4791                         tag: 'span',
4792                         cls: 'icon-bar'
4793                     },
4794                     {
4795                         tag: 'span',
4796                         cls: 'icon-bar'
4797                     }
4798                 ]
4799             };
4800             
4801             cn.push( Roo.bootstrap.version == 4 ? btn : {
4802                 tag: 'div',
4803                 cls: 'navbar-header',
4804                 cn: [
4805                     btn
4806                 ]
4807             });
4808         }
4809         
4810         cn.push({
4811             tag: 'div',
4812             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
4813             cn : []
4814         });
4815         
4816         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4817         
4818         if (['light','white'].indexOf(this.weight) > -1) {
4819             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4820         }
4821         cfg.cls += ' bg-' + this.weight;
4822         
4823         
4824         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4825             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4826             
4827             // tag can override this..
4828             
4829             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4830         }
4831         
4832         if (this.brand !== '') {
4833             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4834             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4835                 tag: 'a',
4836                 href: this.brand_href ? this.brand_href : '#',
4837                 cls: 'navbar-brand',
4838                 cn: [
4839                 this.brand
4840                 ]
4841             });
4842         }
4843         
4844         if(this.main){
4845             cfg.cls += ' main-nav';
4846         }
4847         
4848         
4849         return cfg;
4850
4851         
4852     },
4853     getHeaderChildContainer : function()
4854     {
4855         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4856             return this.el.select('.navbar-header',true).first();
4857         }
4858         
4859         return this.getChildContainer();
4860     },
4861     
4862     getChildContainer : function()
4863     {
4864          
4865         return this.el.select('.roo-navbar-collapse',true).first();
4866          
4867         
4868     },
4869     
4870     initEvents : function()
4871     {
4872         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4873         
4874         if (this.autohide) {
4875             
4876             var prevScroll = 0;
4877             var ft = this.el;
4878             
4879             Roo.get(document).on('scroll',function(e) {
4880                 var ns = Roo.get(document).getScroll().top;
4881                 var os = prevScroll;
4882                 prevScroll = ns;
4883                 
4884                 if(ns > os){
4885                     ft.removeClass('slideDown');
4886                     ft.addClass('slideUp');
4887                     return;
4888                 }
4889                 ft.removeClass('slideUp');
4890                 ft.addClass('slideDown');
4891                  
4892               
4893           },this);
4894         }
4895     }    
4896     
4897 });
4898
4899
4900
4901  
4902
4903  /*
4904  * - LGPL
4905  *
4906  * navbar
4907  * 
4908  */
4909
4910 /**
4911  * @class Roo.bootstrap.NavSidebar
4912  * @extends Roo.bootstrap.Navbar
4913  * Bootstrap Sidebar class
4914  * 
4915  * @constructor
4916  * Create a new Sidebar
4917  * @param {Object} config The config object
4918  */
4919
4920
4921 Roo.bootstrap.NavSidebar = function(config){
4922     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4923 };
4924
4925 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4926     
4927     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4928     
4929     getAutoCreate : function(){
4930         
4931         
4932         return  {
4933             tag: 'div',
4934             cls: 'sidebar sidebar-nav'
4935         };
4936     
4937         
4938     }
4939     
4940     
4941     
4942 });
4943
4944
4945
4946  
4947
4948  /*
4949  * - LGPL
4950  *
4951  * nav group
4952  * 
4953  */
4954
4955 /**
4956  * @class Roo.bootstrap.NavGroup
4957  * @extends Roo.bootstrap.Component
4958  * Bootstrap NavGroup class
4959  * @cfg {String} align (left|right)
4960  * @cfg {Boolean} inverse
4961  * @cfg {String} type (nav|pills|tab) default nav
4962  * @cfg {String} navId - reference Id for navbar.
4963
4964  * 
4965  * @constructor
4966  * Create a new nav group
4967  * @param {Object} config The config object
4968  */
4969
4970 Roo.bootstrap.NavGroup = function(config){
4971     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4972     this.navItems = [];
4973    
4974     Roo.bootstrap.NavGroup.register(this);
4975      this.addEvents({
4976         /**
4977              * @event changed
4978              * Fires when the active item changes
4979              * @param {Roo.bootstrap.NavGroup} this
4980              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4981              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4982          */
4983         'changed': true
4984      });
4985     
4986 };
4987
4988 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4989     
4990     align: '',
4991     inverse: false,
4992     form: false,
4993     type: 'nav',
4994     navId : '',
4995     // private
4996     
4997     navItems : false, 
4998     
4999     getAutoCreate : function()
5000     {
5001         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5002         
5003         cfg = {
5004             tag : 'ul',
5005             cls: 'nav' 
5006         };
5007         if (Roo.bootstrap.version == 4) {
5008             if (['tabs','pills'].indexOf(this.type) != -1) {
5009                 cfg.cls += ' nav-' + this.type; 
5010             } else {
5011                 // trying to remove so header bar can right align top?
5012                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5013                     // do not use on header bar... 
5014                     cfg.cls += ' navbar-nav';
5015                 }
5016             }
5017             
5018         } else {
5019             if (['tabs','pills'].indexOf(this.type) != -1) {
5020                 cfg.cls += ' nav-' + this.type
5021             } else {
5022                 if (this.type !== 'nav') {
5023                     Roo.log('nav type must be nav/tabs/pills')
5024                 }
5025                 cfg.cls += ' navbar-nav'
5026             }
5027         }
5028         
5029         if (this.parent() && this.parent().sidebar) {
5030             cfg = {
5031                 tag: 'ul',
5032                 cls: 'dashboard-menu sidebar-menu'
5033             };
5034             
5035             return cfg;
5036         }
5037         
5038         if (this.form === true) {
5039             cfg = {
5040                 tag: 'form',
5041                 cls: 'navbar-form form-inline'
5042             };
5043             //nav navbar-right ml-md-auto
5044             if (this.align === 'right') {
5045                 cfg.cls += ' navbar-right ml-md-auto';
5046             } else {
5047                 cfg.cls += ' navbar-left';
5048             }
5049         }
5050         
5051         if (this.align === 'right') {
5052             cfg.cls += ' navbar-right ml-md-auto';
5053         } else {
5054             cfg.cls += ' mr-auto';
5055         }
5056         
5057         if (this.inverse) {
5058             cfg.cls += ' navbar-inverse';
5059             
5060         }
5061         
5062         
5063         return cfg;
5064     },
5065     /**
5066     * sets the active Navigation item
5067     * @param {Roo.bootstrap.NavItem} the new current navitem
5068     */
5069     setActiveItem : function(item)
5070     {
5071         var prev = false;
5072         Roo.each(this.navItems, function(v){
5073             if (v == item) {
5074                 return ;
5075             }
5076             if (v.isActive()) {
5077                 v.setActive(false, true);
5078                 prev = v;
5079                 
5080             }
5081             
5082         });
5083
5084         item.setActive(true, true);
5085         this.fireEvent('changed', this, item, prev);
5086         
5087         
5088     },
5089     /**
5090     * gets the active Navigation item
5091     * @return {Roo.bootstrap.NavItem} the current navitem
5092     */
5093     getActive : function()
5094     {
5095         
5096         var prev = false;
5097         Roo.each(this.navItems, function(v){
5098             
5099             if (v.isActive()) {
5100                 prev = v;
5101                 
5102             }
5103             
5104         });
5105         return prev;
5106     },
5107     
5108     indexOfNav : function()
5109     {
5110         
5111         var prev = false;
5112         Roo.each(this.navItems, function(v,i){
5113             
5114             if (v.isActive()) {
5115                 prev = i;
5116                 
5117             }
5118             
5119         });
5120         return prev;
5121     },
5122     /**
5123     * adds a Navigation item
5124     * @param {Roo.bootstrap.NavItem} the navitem to add
5125     */
5126     addItem : function(cfg)
5127     {
5128         if (this.form && Roo.bootstrap.version == 4) {
5129             cfg.tag = 'div';
5130         }
5131         var cn = new Roo.bootstrap.NavItem(cfg);
5132         this.register(cn);
5133         cn.parentId = this.id;
5134         cn.onRender(this.el, null);
5135         return cn;
5136     },
5137     /**
5138     * register a Navigation item
5139     * @param {Roo.bootstrap.NavItem} the navitem to add
5140     */
5141     register : function(item)
5142     {
5143         this.navItems.push( item);
5144         item.navId = this.navId;
5145     
5146     },
5147     
5148     /**
5149     * clear all the Navigation item
5150     */
5151    
5152     clearAll : function()
5153     {
5154         this.navItems = [];
5155         this.el.dom.innerHTML = '';
5156     },
5157     
5158     getNavItem: function(tabId)
5159     {
5160         var ret = false;
5161         Roo.each(this.navItems, function(e) {
5162             if (e.tabId == tabId) {
5163                ret =  e;
5164                return false;
5165             }
5166             return true;
5167             
5168         });
5169         return ret;
5170     },
5171     
5172     setActiveNext : function()
5173     {
5174         var i = this.indexOfNav(this.getActive());
5175         if (i > this.navItems.length) {
5176             return;
5177         }
5178         this.setActiveItem(this.navItems[i+1]);
5179     },
5180     setActivePrev : function()
5181     {
5182         var i = this.indexOfNav(this.getActive());
5183         if (i  < 1) {
5184             return;
5185         }
5186         this.setActiveItem(this.navItems[i-1]);
5187     },
5188     clearWasActive : function(except) {
5189         Roo.each(this.navItems, function(e) {
5190             if (e.tabId != except.tabId && e.was_active) {
5191                e.was_active = false;
5192                return false;
5193             }
5194             return true;
5195             
5196         });
5197     },
5198     getWasActive : function ()
5199     {
5200         var r = false;
5201         Roo.each(this.navItems, function(e) {
5202             if (e.was_active) {
5203                r = e;
5204                return false;
5205             }
5206             return true;
5207             
5208         });
5209         return r;
5210     }
5211     
5212     
5213 });
5214
5215  
5216 Roo.apply(Roo.bootstrap.NavGroup, {
5217     
5218     groups: {},
5219      /**
5220     * register a Navigation Group
5221     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5222     */
5223     register : function(navgrp)
5224     {
5225         this.groups[navgrp.navId] = navgrp;
5226         
5227     },
5228     /**
5229     * fetch a Navigation Group based on the navigation ID
5230     * @param {string} the navgroup to add
5231     * @returns {Roo.bootstrap.NavGroup} the navgroup 
5232     */
5233     get: function(navId) {
5234         if (typeof(this.groups[navId]) == 'undefined') {
5235             return false;
5236             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
5237         }
5238         return this.groups[navId] ;
5239     }
5240     
5241     
5242     
5243 });
5244
5245  /*
5246  * - LGPL
5247  *
5248  * row
5249  * 
5250  */
5251
5252 /**
5253  * @class Roo.bootstrap.NavItem
5254  * @extends Roo.bootstrap.Component
5255  * Bootstrap Navbar.NavItem class
5256  * @cfg {String} href  link to
5257  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
5258
5259  * @cfg {String} html content of button
5260  * @cfg {String} badge text inside badge
5261  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
5262  * @cfg {String} glyphicon DEPRICATED - use fa
5263  * @cfg {String} icon DEPRICATED - use fa
5264  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
5265  * @cfg {Boolean} active Is item active
5266  * @cfg {Boolean} disabled Is item disabled
5267  
5268  * @cfg {Boolean} preventDefault (true | false) default false
5269  * @cfg {String} tabId the tab that this item activates.
5270  * @cfg {String} tagtype (a|span) render as a href or span?
5271  * @cfg {Boolean} animateRef (true|false) link to element default false  
5272   
5273  * @constructor
5274  * Create a new Navbar Item
5275  * @param {Object} config The config object
5276  */
5277 Roo.bootstrap.NavItem = function(config){
5278     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
5279     this.addEvents({
5280         // raw events
5281         /**
5282          * @event click
5283          * The raw click event for the entire grid.
5284          * @param {Roo.EventObject} e
5285          */
5286         "click" : true,
5287          /**
5288             * @event changed
5289             * Fires when the active item active state changes
5290             * @param {Roo.bootstrap.NavItem} this
5291             * @param {boolean} state the new state
5292              
5293          */
5294         'changed': true,
5295         /**
5296             * @event scrollto
5297             * Fires when scroll to element
5298             * @param {Roo.bootstrap.NavItem} this
5299             * @param {Object} options
5300             * @param {Roo.EventObject} e
5301              
5302          */
5303         'scrollto': true
5304     });
5305    
5306 };
5307
5308 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
5309     
5310     href: false,
5311     html: '',
5312     badge: '',
5313     icon: false,
5314     fa : false,
5315     glyphicon: false,
5316     active: false,
5317     preventDefault : false,
5318     tabId : false,
5319     tagtype : 'a',
5320     tag: 'li',
5321     disabled : false,
5322     animateRef : false,
5323     was_active : false,
5324     button_weight : '',
5325     button_outline : false,
5326     
5327     navLink: false,
5328     
5329     getAutoCreate : function(){
5330          
5331         var cfg = {
5332             tag: this.tag,
5333             cls: 'nav-item'
5334         };
5335         
5336         if (this.active) {
5337             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
5338         }
5339         if (this.disabled) {
5340             cfg.cls += ' disabled';
5341         }
5342         
5343         // BS4 only?
5344         if (this.button_weight.length) {
5345             cfg.tag = this.href ? 'a' : 'button';
5346             cfg.html = this.html || '';
5347             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
5348             if (this.href) {
5349                 cfg.href = this.href;
5350             }
5351             if (this.fa) {
5352                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
5353             }
5354             
5355             // menu .. should add dropdown-menu class - so no need for carat..
5356             
5357             if (this.badge !== '') {
5358                  
5359                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5360             }
5361             return cfg;
5362         }
5363         
5364         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
5365             cfg.cn = [
5366                 {
5367                     tag: this.tagtype,
5368                     href : this.href || "#",
5369                     html: this.html || ''
5370                 }
5371             ];
5372             if (this.tagtype == 'a') {
5373                 cfg.cn[0].cls = 'nav-link';
5374             }
5375             if (this.icon) {
5376                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
5377             }
5378             if (this.fa) {
5379                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
5380             }
5381             if(this.glyphicon) {
5382                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
5383             }
5384             
5385             if (this.menu) {
5386                 
5387                 cfg.cn[0].html += " <span class='caret'></span>";
5388              
5389             }
5390             
5391             if (this.badge !== '') {
5392                  
5393                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5394             }
5395         }
5396         
5397         
5398         
5399         return cfg;
5400     },
5401     onRender : function(ct, position)
5402     {
5403        // Roo.log("Call onRender: " + this.xtype);
5404         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
5405             this.tag = 'div';
5406         }
5407         
5408         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
5409         this.navLink = this.el.select('.nav-link',true).first();
5410         return ret;
5411     },
5412       
5413     
5414     initEvents: function() 
5415     {
5416         if (typeof (this.menu) != 'undefined') {
5417             this.menu.parentType = this.xtype;
5418             this.menu.triggerEl = this.el;
5419             this.menu = this.addxtype(Roo.apply({}, this.menu));
5420         }
5421         
5422         this.el.select('a',true).on('click', this.onClick, this);
5423         
5424         if(this.tagtype == 'span'){
5425             this.el.select('span',true).on('click', this.onClick, this);
5426         }
5427        
5428         // at this point parent should be available..
5429         this.parent().register(this);
5430     },
5431     
5432     onClick : function(e)
5433     {
5434         if (e.getTarget('.dropdown-menu-item')) {
5435             // did you click on a menu itemm.... - then don't trigger onclick..
5436             return;
5437         }
5438         
5439         if(
5440                 this.preventDefault || 
5441                 this.href == '#' 
5442         ){
5443             Roo.log("NavItem - prevent Default?");
5444             e.preventDefault();
5445         }
5446         
5447         if (this.disabled) {
5448             return;
5449         }
5450         
5451         var tg = Roo.bootstrap.TabGroup.get(this.navId);
5452         if (tg && tg.transition) {
5453             Roo.log("waiting for the transitionend");
5454             return;
5455         }
5456         
5457         
5458         
5459         //Roo.log("fire event clicked");
5460         if(this.fireEvent('click', this, e) === false){
5461             return;
5462         };
5463         
5464         if(this.tagtype == 'span'){
5465             return;
5466         }
5467         
5468         //Roo.log(this.href);
5469         var ael = this.el.select('a',true).first();
5470         //Roo.log(ael);
5471         
5472         if(ael && this.animateRef && this.href.indexOf('#') > -1){
5473             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
5474             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
5475                 return; // ignore... - it's a 'hash' to another page.
5476             }
5477             Roo.log("NavItem - prevent Default?");
5478             e.preventDefault();
5479             this.scrollToElement(e);
5480         }
5481         
5482         
5483         var p =  this.parent();
5484    
5485         if (['tabs','pills'].indexOf(p.type)!==-1) {
5486             if (typeof(p.setActiveItem) !== 'undefined') {
5487                 p.setActiveItem(this);
5488             }
5489         }
5490         
5491         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
5492         if (p.parentType == 'NavHeaderbar' && !this.menu) {
5493             // remove the collapsed menu expand...
5494             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
5495         }
5496     },
5497     
5498     isActive: function () {
5499         return this.active
5500     },
5501     setActive : function(state, fire, is_was_active)
5502     {
5503         if (this.active && !state && this.navId) {
5504             this.was_active = true;
5505             var nv = Roo.bootstrap.NavGroup.get(this.navId);
5506             if (nv) {
5507                 nv.clearWasActive(this);
5508             }
5509             
5510         }
5511         this.active = state;
5512         
5513         if (!state ) {
5514             this.el.removeClass('active');
5515             this.navLink ? this.navLink.removeClass('active') : false;
5516         } else if (!this.el.hasClass('active')) {
5517             
5518             this.el.addClass('active');
5519             if (Roo.bootstrap.version == 4 && this.navLink ) {
5520                 this.navLink.addClass('active');
5521             }
5522             
5523         }
5524         if (fire) {
5525             this.fireEvent('changed', this, state);
5526         }
5527         
5528         // show a panel if it's registered and related..
5529         
5530         if (!this.navId || !this.tabId || !state || is_was_active) {
5531             return;
5532         }
5533         
5534         var tg = Roo.bootstrap.TabGroup.get(this.navId);
5535         if (!tg) {
5536             return;
5537         }
5538         var pan = tg.getPanelByName(this.tabId);
5539         if (!pan) {
5540             return;
5541         }
5542         // if we can not flip to new panel - go back to old nav highlight..
5543         if (false == tg.showPanel(pan)) {
5544             var nv = Roo.bootstrap.NavGroup.get(this.navId);
5545             if (nv) {
5546                 var onav = nv.getWasActive();
5547                 if (onav) {
5548                     onav.setActive(true, false, true);
5549                 }
5550             }
5551             
5552         }
5553         
5554         
5555         
5556     },
5557      // this should not be here...
5558     setDisabled : function(state)
5559     {
5560         this.disabled = state;
5561         if (!state ) {
5562             this.el.removeClass('disabled');
5563         } else if (!this.el.hasClass('disabled')) {
5564             this.el.addClass('disabled');
5565         }
5566         
5567     },
5568     
5569     /**
5570      * Fetch the element to display the tooltip on.
5571      * @return {Roo.Element} defaults to this.el
5572      */
5573     tooltipEl : function()
5574     {
5575         return this.el.select('' + this.tagtype + '', true).first();
5576     },
5577     
5578     scrollToElement : function(e)
5579     {
5580         var c = document.body;
5581         
5582         /*
5583          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
5584          */
5585         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
5586             c = document.documentElement;
5587         }
5588         
5589         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
5590         
5591         if(!target){
5592             return;
5593         }
5594
5595         var o = target.calcOffsetsTo(c);
5596         
5597         var options = {
5598             target : target,
5599             value : o[1]
5600         };
5601         
5602         this.fireEvent('scrollto', this, options, e);
5603         
5604         Roo.get(c).scrollTo('top', options.value, true);
5605         
5606         return;
5607     }
5608 });
5609  
5610
5611  /*
5612  * - LGPL
5613  *
5614  * sidebar item
5615  *
5616  *  li
5617  *    <span> icon </span>
5618  *    <span> text </span>
5619  *    <span>badge </span>
5620  */
5621
5622 /**
5623  * @class Roo.bootstrap.NavSidebarItem
5624  * @extends Roo.bootstrap.NavItem
5625  * Bootstrap Navbar.NavSidebarItem class
5626  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5627  * {Boolean} open is the menu open
5628  * {Boolean} buttonView use button as the tigger el rather that a (default false)
5629  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5630  * {String} buttonSize (sm|md|lg)the extra classes for the button
5631  * {Boolean} showArrow show arrow next to the text (default true)
5632  * @constructor
5633  * Create a new Navbar Button
5634  * @param {Object} config The config object
5635  */
5636 Roo.bootstrap.NavSidebarItem = function(config){
5637     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5638     this.addEvents({
5639         // raw events
5640         /**
5641          * @event click
5642          * The raw click event for the entire grid.
5643          * @param {Roo.EventObject} e
5644          */
5645         "click" : true,
5646          /**
5647             * @event changed
5648             * Fires when the active item active state changes
5649             * @param {Roo.bootstrap.NavSidebarItem} this
5650             * @param {boolean} state the new state
5651              
5652          */
5653         'changed': true
5654     });
5655    
5656 };
5657
5658 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
5659     
5660     badgeWeight : 'default',
5661     
5662     open: false,
5663     
5664     buttonView : false,
5665     
5666     buttonWeight : 'default',
5667     
5668     buttonSize : 'md',
5669     
5670     showArrow : true,
5671     
5672     getAutoCreate : function(){
5673         
5674         
5675         var a = {
5676                 tag: 'a',
5677                 href : this.href || '#',
5678                 cls: '',
5679                 html : '',
5680                 cn : []
5681         };
5682         
5683         if(this.buttonView){
5684             a = {
5685                 tag: 'button',
5686                 href : this.href || '#',
5687                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5688                 html : this.html,
5689                 cn : []
5690             };
5691         }
5692         
5693         var cfg = {
5694             tag: 'li',
5695             cls: '',
5696             cn: [ a ]
5697         };
5698         
5699         if (this.active) {
5700             cfg.cls += ' active';
5701         }
5702         
5703         if (this.disabled) {
5704             cfg.cls += ' disabled';
5705         }
5706         if (this.open) {
5707             cfg.cls += ' open x-open';
5708         }
5709         // left icon..
5710         if (this.glyphicon || this.icon) {
5711             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5712             a.cn.push({ tag : 'i', cls : c }) ;
5713         }
5714         
5715         if(!this.buttonView){
5716             var span = {
5717                 tag: 'span',
5718                 html : this.html || ''
5719             };
5720
5721             a.cn.push(span);
5722             
5723         }
5724         
5725         if (this.badge !== '') {
5726             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5727         }
5728         
5729         if (this.menu) {
5730             
5731             if(this.showArrow){
5732                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5733             }
5734             
5735             a.cls += ' dropdown-toggle treeview' ;
5736         }
5737         
5738         return cfg;
5739     },
5740     
5741     initEvents : function()
5742     { 
5743         if (typeof (this.menu) != 'undefined') {
5744             this.menu.parentType = this.xtype;
5745             this.menu.triggerEl = this.el;
5746             this.menu = this.addxtype(Roo.apply({}, this.menu));
5747         }
5748         
5749         this.el.on('click', this.onClick, this);
5750         
5751         if(this.badge !== ''){
5752             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5753         }
5754         
5755     },
5756     
5757     onClick : function(e)
5758     {
5759         if(this.disabled){
5760             e.preventDefault();
5761             return;
5762         }
5763         
5764         if(this.preventDefault){
5765             e.preventDefault();
5766         }
5767         
5768         this.fireEvent('click', this, e);
5769     },
5770     
5771     disable : function()
5772     {
5773         this.setDisabled(true);
5774     },
5775     
5776     enable : function()
5777     {
5778         this.setDisabled(false);
5779     },
5780     
5781     setDisabled : function(state)
5782     {
5783         if(this.disabled == state){
5784             return;
5785         }
5786         
5787         this.disabled = state;
5788         
5789         if (state) {
5790             this.el.addClass('disabled');
5791             return;
5792         }
5793         
5794         this.el.removeClass('disabled');
5795         
5796         return;
5797     },
5798     
5799     setActive : function(state)
5800     {
5801         if(this.active == state){
5802             return;
5803         }
5804         
5805         this.active = state;
5806         
5807         if (state) {
5808             this.el.addClass('active');
5809             return;
5810         }
5811         
5812         this.el.removeClass('active');
5813         
5814         return;
5815     },
5816     
5817     isActive: function () 
5818     {
5819         return this.active;
5820     },
5821     
5822     setBadge : function(str)
5823     {
5824         if(!this.badgeEl){
5825             return;
5826         }
5827         
5828         this.badgeEl.dom.innerHTML = str;
5829     }
5830     
5831    
5832      
5833  
5834 });
5835  
5836
5837  /*
5838  * - LGPL
5839  *
5840  * row
5841  * 
5842  */
5843
5844 /**
5845  * @class Roo.bootstrap.Row
5846  * @extends Roo.bootstrap.Component
5847  * Bootstrap Row class (contains columns...)
5848  * 
5849  * @constructor
5850  * Create a new Row
5851  * @param {Object} config The config object
5852  */
5853
5854 Roo.bootstrap.Row = function(config){
5855     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5856 };
5857
5858 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5859     
5860     getAutoCreate : function(){
5861        return {
5862             cls: 'row clearfix'
5863        };
5864     }
5865     
5866     
5867 });
5868
5869  
5870
5871  /*
5872  * - LGPL
5873  *
5874  * pagination
5875  * 
5876  */
5877
5878 /**
5879  * @class Roo.bootstrap.Pagination
5880  * @extends Roo.bootstrap.Component
5881  * Bootstrap Pagination class
5882  * @cfg {String} size xs | sm | md | lg
5883  * @cfg {Boolean} inverse false | true
5884  * 
5885  * @constructor
5886  * Create a new Pagination
5887  * @param {Object} config The config object
5888  */
5889
5890 Roo.bootstrap.Pagination = function(config){
5891     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5892 };
5893
5894 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5895     
5896     cls: false,
5897     size: false,
5898     inverse: false,
5899     
5900     getAutoCreate : function(){
5901         var cfg = {
5902             tag: 'ul',
5903                 cls: 'pagination'
5904         };
5905         if (this.inverse) {
5906             cfg.cls += ' inverse';
5907         }
5908         if (this.html) {
5909             cfg.html=this.html;
5910         }
5911         if (this.cls) {
5912             cfg.cls += " " + this.cls;
5913         }
5914         return cfg;
5915     }
5916    
5917 });
5918
5919  
5920
5921  /*
5922  * - LGPL
5923  *
5924  * Pagination item
5925  * 
5926  */
5927
5928
5929 /**
5930  * @class Roo.bootstrap.PaginationItem
5931  * @extends Roo.bootstrap.Component
5932  * Bootstrap PaginationItem class
5933  * @cfg {String} html text
5934  * @cfg {String} href the link
5935  * @cfg {Boolean} preventDefault (true | false) default true
5936  * @cfg {Boolean} active (true | false) default false
5937  * @cfg {Boolean} disabled default false
5938  * 
5939  * 
5940  * @constructor
5941  * Create a new PaginationItem
5942  * @param {Object} config The config object
5943  */
5944
5945
5946 Roo.bootstrap.PaginationItem = function(config){
5947     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5948     this.addEvents({
5949         // raw events
5950         /**
5951          * @event click
5952          * The raw click event for the entire grid.
5953          * @param {Roo.EventObject} e
5954          */
5955         "click" : true
5956     });
5957 };
5958
5959 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5960     
5961     href : false,
5962     html : false,
5963     preventDefault: true,
5964     active : false,
5965     cls : false,
5966     disabled: false,
5967     
5968     getAutoCreate : function(){
5969         var cfg= {
5970             tag: 'li',
5971             cn: [
5972                 {
5973                     tag : 'a',
5974                     href : this.href ? this.href : '#',
5975                     html : this.html ? this.html : ''
5976                 }
5977             ]
5978         };
5979         
5980         if(this.cls){
5981             cfg.cls = this.cls;
5982         }
5983         
5984         if(this.disabled){
5985             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5986         }
5987         
5988         if(this.active){
5989             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5990         }
5991         
5992         return cfg;
5993     },
5994     
5995     initEvents: function() {
5996         
5997         this.el.on('click', this.onClick, this);
5998         
5999     },
6000     onClick : function(e)
6001     {
6002         Roo.log('PaginationItem on click ');
6003         if(this.preventDefault){
6004             e.preventDefault();
6005         }
6006         
6007         if(this.disabled){
6008             return;
6009         }
6010         
6011         this.fireEvent('click', this, e);
6012     }
6013    
6014 });
6015
6016  
6017
6018  /*
6019  * - LGPL
6020  *
6021  * slider
6022  * 
6023  */
6024
6025
6026 /**
6027  * @class Roo.bootstrap.Slider
6028  * @extends Roo.bootstrap.Component
6029  * Bootstrap Slider class
6030  *    
6031  * @constructor
6032  * Create a new Slider
6033  * @param {Object} config The config object
6034  */
6035
6036 Roo.bootstrap.Slider = function(config){
6037     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6038 };
6039
6040 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6041     
6042     getAutoCreate : function(){
6043         
6044         var cfg = {
6045             tag: 'div',
6046             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6047             cn: [
6048                 {
6049                     tag: 'a',
6050                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6051                 }
6052             ]
6053         };
6054         
6055         return cfg;
6056     }
6057    
6058 });
6059
6060  /*
6061  * Based on:
6062  * Ext JS Library 1.1.1
6063  * Copyright(c) 2006-2007, Ext JS, LLC.
6064  *
6065  * Originally Released Under LGPL - original licence link has changed is not relivant.
6066  *
6067  * Fork - LGPL
6068  * <script type="text/javascript">
6069  */
6070  
6071
6072 /**
6073  * @class Roo.grid.ColumnModel
6074  * @extends Roo.util.Observable
6075  * This is the default implementation of a ColumnModel used by the Grid. It defines
6076  * the columns in the grid.
6077  * <br>Usage:<br>
6078  <pre><code>
6079  var colModel = new Roo.grid.ColumnModel([
6080         {header: "Ticker", width: 60, sortable: true, locked: true},
6081         {header: "Company Name", width: 150, sortable: true},
6082         {header: "Market Cap.", width: 100, sortable: true},
6083         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6084         {header: "Employees", width: 100, sortable: true, resizable: false}
6085  ]);
6086  </code></pre>
6087  * <p>
6088  
6089  * The config options listed for this class are options which may appear in each
6090  * individual column definition.
6091  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6092  * @constructor
6093  * @param {Object} config An Array of column config objects. See this class's
6094  * config objects for details.
6095 */
6096 Roo.grid.ColumnModel = function(config){
6097         /**
6098      * The config passed into the constructor
6099      */
6100     this.config = config;
6101     this.lookup = {};
6102
6103     // if no id, create one
6104     // if the column does not have a dataIndex mapping,
6105     // map it to the order it is in the config
6106     for(var i = 0, len = config.length; i < len; i++){
6107         var c = config[i];
6108         if(typeof c.dataIndex == "undefined"){
6109             c.dataIndex = i;
6110         }
6111         if(typeof c.renderer == "string"){
6112             c.renderer = Roo.util.Format[c.renderer];
6113         }
6114         if(typeof c.id == "undefined"){
6115             c.id = Roo.id();
6116         }
6117         if(c.editor && c.editor.xtype){
6118             c.editor  = Roo.factory(c.editor, Roo.grid);
6119         }
6120         if(c.editor && c.editor.isFormField){
6121             c.editor = new Roo.grid.GridEditor(c.editor);
6122         }
6123         this.lookup[c.id] = c;
6124     }
6125
6126     /**
6127      * The width of columns which have no width specified (defaults to 100)
6128      * @type Number
6129      */
6130     this.defaultWidth = 100;
6131
6132     /**
6133      * Default sortable of columns which have no sortable specified (defaults to false)
6134      * @type Boolean
6135      */
6136     this.defaultSortable = false;
6137
6138     this.addEvents({
6139         /**
6140              * @event widthchange
6141              * Fires when the width of a column changes.
6142              * @param {ColumnModel} this
6143              * @param {Number} columnIndex The column index
6144              * @param {Number} newWidth The new width
6145              */
6146             "widthchange": true,
6147         /**
6148              * @event headerchange
6149              * Fires when the text of a header changes.
6150              * @param {ColumnModel} this
6151              * @param {Number} columnIndex The column index
6152              * @param {Number} newText The new header text
6153              */
6154             "headerchange": true,
6155         /**
6156              * @event hiddenchange
6157              * Fires when a column is hidden or "unhidden".
6158              * @param {ColumnModel} this
6159              * @param {Number} columnIndex The column index
6160              * @param {Boolean} hidden true if hidden, false otherwise
6161              */
6162             "hiddenchange": true,
6163             /**
6164          * @event columnmoved
6165          * Fires when a column is moved.
6166          * @param {ColumnModel} this
6167          * @param {Number} oldIndex
6168          * @param {Number} newIndex
6169          */
6170         "columnmoved" : true,
6171         /**
6172          * @event columlockchange
6173          * Fires when a column's locked state is changed
6174          * @param {ColumnModel} this
6175          * @param {Number} colIndex
6176          * @param {Boolean} locked true if locked
6177          */
6178         "columnlockchange" : true
6179     });
6180     Roo.grid.ColumnModel.superclass.constructor.call(this);
6181 };
6182 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6183     /**
6184      * @cfg {String} header The header text to display in the Grid view.
6185      */
6186     /**
6187      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6188      * {@link Roo.data.Record} definition from which to draw the column's value. If not
6189      * specified, the column's index is used as an index into the Record's data Array.
6190      */
6191     /**
6192      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6193      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6194      */
6195     /**
6196      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6197      * Defaults to the value of the {@link #defaultSortable} property.
6198      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6199      */
6200     /**
6201      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
6202      */
6203     /**
6204      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
6205      */
6206     /**
6207      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6208      */
6209     /**
6210      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6211      */
6212     /**
6213      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6214      * given the cell's data value. See {@link #setRenderer}. If not specified, the
6215      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6216      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6217      */
6218        /**
6219      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
6220      */
6221     /**
6222      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
6223      */
6224     /**
6225      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
6226      */
6227     /**
6228      * @cfg {String} cursor (Optional)
6229      */
6230     /**
6231      * @cfg {String} tooltip (Optional)
6232      */
6233     /**
6234      * @cfg {Number} xs (Optional)
6235      */
6236     /**
6237      * @cfg {Number} sm (Optional)
6238      */
6239     /**
6240      * @cfg {Number} md (Optional)
6241      */
6242     /**
6243      * @cfg {Number} lg (Optional)
6244      */
6245     /**
6246      * Returns the id of the column at the specified index.
6247      * @param {Number} index The column index
6248      * @return {String} the id
6249      */
6250     getColumnId : function(index){
6251         return this.config[index].id;
6252     },
6253
6254     /**
6255      * Returns the column for a specified id.
6256      * @param {String} id The column id
6257      * @return {Object} the column
6258      */
6259     getColumnById : function(id){
6260         return this.lookup[id];
6261     },
6262
6263     
6264     /**
6265      * Returns the column for a specified dataIndex.
6266      * @param {String} dataIndex The column dataIndex
6267      * @return {Object|Boolean} the column or false if not found
6268      */
6269     getColumnByDataIndex: function(dataIndex){
6270         var index = this.findColumnIndex(dataIndex);
6271         return index > -1 ? this.config[index] : false;
6272     },
6273     
6274     /**
6275      * Returns the index for a specified column id.
6276      * @param {String} id The column id
6277      * @return {Number} the index, or -1 if not found
6278      */
6279     getIndexById : function(id){
6280         for(var i = 0, len = this.config.length; i < len; i++){
6281             if(this.config[i].id == id){
6282                 return i;
6283             }
6284         }
6285         return -1;
6286     },
6287     
6288     /**
6289      * Returns the index for a specified column dataIndex.
6290      * @param {String} dataIndex The column dataIndex
6291      * @return {Number} the index, or -1 if not found
6292      */
6293     
6294     findColumnIndex : function(dataIndex){
6295         for(var i = 0, len = this.config.length; i < len; i++){
6296             if(this.config[i].dataIndex == dataIndex){
6297                 return i;
6298             }
6299         }
6300         return -1;
6301     },
6302     
6303     
6304     moveColumn : function(oldIndex, newIndex){
6305         var c = this.config[oldIndex];
6306         this.config.splice(oldIndex, 1);
6307         this.config.splice(newIndex, 0, c);
6308         this.dataMap = null;
6309         this.fireEvent("columnmoved", this, oldIndex, newIndex);
6310     },
6311
6312     isLocked : function(colIndex){
6313         return this.config[colIndex].locked === true;
6314     },
6315
6316     setLocked : function(colIndex, value, suppressEvent){
6317         if(this.isLocked(colIndex) == value){
6318             return;
6319         }
6320         this.config[colIndex].locked = value;
6321         if(!suppressEvent){
6322             this.fireEvent("columnlockchange", this, colIndex, value);
6323         }
6324     },
6325
6326     getTotalLockedWidth : function(){
6327         var totalWidth = 0;
6328         for(var i = 0; i < this.config.length; i++){
6329             if(this.isLocked(i) && !this.isHidden(i)){
6330                 this.totalWidth += this.getColumnWidth(i);
6331             }
6332         }
6333         return totalWidth;
6334     },
6335
6336     getLockedCount : function(){
6337         for(var i = 0, len = this.config.length; i < len; i++){
6338             if(!this.isLocked(i)){
6339                 return i;
6340             }
6341         }
6342         
6343         return this.config.length;
6344     },
6345
6346     /**
6347      * Returns the number of columns.
6348      * @return {Number}
6349      */
6350     getColumnCount : function(visibleOnly){
6351         if(visibleOnly === true){
6352             var c = 0;
6353             for(var i = 0, len = this.config.length; i < len; i++){
6354                 if(!this.isHidden(i)){
6355                     c++;
6356                 }
6357             }
6358             return c;
6359         }
6360         return this.config.length;
6361     },
6362
6363     /**
6364      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
6365      * @param {Function} fn
6366      * @param {Object} scope (optional)
6367      * @return {Array} result
6368      */
6369     getColumnsBy : function(fn, scope){
6370         var r = [];
6371         for(var i = 0, len = this.config.length; i < len; i++){
6372             var c = this.config[i];
6373             if(fn.call(scope||this, c, i) === true){
6374                 r[r.length] = c;
6375             }
6376         }
6377         return r;
6378     },
6379
6380     /**
6381      * Returns true if the specified column is sortable.
6382      * @param {Number} col The column index
6383      * @return {Boolean}
6384      */
6385     isSortable : function(col){
6386         if(typeof this.config[col].sortable == "undefined"){
6387             return this.defaultSortable;
6388         }
6389         return this.config[col].sortable;
6390     },
6391
6392     /**
6393      * Returns the rendering (formatting) function defined for the column.
6394      * @param {Number} col The column index.
6395      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
6396      */
6397     getRenderer : function(col){
6398         if(!this.config[col].renderer){
6399             return Roo.grid.ColumnModel.defaultRenderer;
6400         }
6401         return this.config[col].renderer;
6402     },
6403
6404     /**
6405      * Sets the rendering (formatting) function for a column.
6406      * @param {Number} col The column index
6407      * @param {Function} fn The function to use to process the cell's raw data
6408      * to return HTML markup for the grid view. The render function is called with
6409      * the following parameters:<ul>
6410      * <li>Data value.</li>
6411      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
6412      * <li>css A CSS style string to apply to the table cell.</li>
6413      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
6414      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
6415      * <li>Row index</li>
6416      * <li>Column index</li>
6417      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
6418      */
6419     setRenderer : function(col, fn){
6420         this.config[col].renderer = fn;
6421     },
6422
6423     /**
6424      * Returns the width for the specified column.
6425      * @param {Number} col The column index
6426      * @return {Number}
6427      */
6428     getColumnWidth : function(col){
6429         return this.config[col].width * 1 || this.defaultWidth;
6430     },
6431
6432     /**
6433      * Sets the width for a column.
6434      * @param {Number} col The column index
6435      * @param {Number} width The new width
6436      */
6437     setColumnWidth : function(col, width, suppressEvent){
6438         this.config[col].width = width;
6439         this.totalWidth = null;
6440         if(!suppressEvent){
6441              this.fireEvent("widthchange", this, col, width);
6442         }
6443     },
6444
6445     /**
6446      * Returns the total width of all columns.
6447      * @param {Boolean} includeHidden True to include hidden column widths
6448      * @return {Number}
6449      */
6450     getTotalWidth : function(includeHidden){
6451         if(!this.totalWidth){
6452             this.totalWidth = 0;
6453             for(var i = 0, len = this.config.length; i < len; i++){
6454                 if(includeHidden || !this.isHidden(i)){
6455                     this.totalWidth += this.getColumnWidth(i);
6456                 }
6457             }
6458         }
6459         return this.totalWidth;
6460     },
6461
6462     /**
6463      * Returns the header for the specified column.
6464      * @param {Number} col The column index
6465      * @return {String}
6466      */
6467     getColumnHeader : function(col){
6468         return this.config[col].header;
6469     },
6470
6471     /**
6472      * Sets the header for a column.
6473      * @param {Number} col The column index
6474      * @param {String} header The new header
6475      */
6476     setColumnHeader : function(col, header){
6477         this.config[col].header = header;
6478         this.fireEvent("headerchange", this, col, header);
6479     },
6480
6481     /**
6482      * Returns the tooltip for the specified column.
6483      * @param {Number} col The column index
6484      * @return {String}
6485      */
6486     getColumnTooltip : function(col){
6487             return this.config[col].tooltip;
6488     },
6489     /**
6490      * Sets the tooltip for a column.
6491      * @param {Number} col The column index
6492      * @param {String} tooltip The new tooltip
6493      */
6494     setColumnTooltip : function(col, tooltip){
6495             this.config[col].tooltip = tooltip;
6496     },
6497
6498     /**
6499      * Returns the dataIndex for the specified column.
6500      * @param {Number} col The column index
6501      * @return {Number}
6502      */
6503     getDataIndex : function(col){
6504         return this.config[col].dataIndex;
6505     },
6506
6507     /**
6508      * Sets the dataIndex for a column.
6509      * @param {Number} col The column index
6510      * @param {Number} dataIndex The new dataIndex
6511      */
6512     setDataIndex : function(col, dataIndex){
6513         this.config[col].dataIndex = dataIndex;
6514     },
6515
6516     
6517     
6518     /**
6519      * Returns true if the cell is editable.
6520      * @param {Number} colIndex The column index
6521      * @param {Number} rowIndex The row index - this is nto actually used..?
6522      * @return {Boolean}
6523      */
6524     isCellEditable : function(colIndex, rowIndex){
6525         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6526     },
6527
6528     /**
6529      * Returns the editor defined for the cell/column.
6530      * return false or null to disable editing.
6531      * @param {Number} colIndex The column index
6532      * @param {Number} rowIndex The row index
6533      * @return {Object}
6534      */
6535     getCellEditor : function(colIndex, rowIndex){
6536         return this.config[colIndex].editor;
6537     },
6538
6539     /**
6540      * Sets if a column is editable.
6541      * @param {Number} col The column index
6542      * @param {Boolean} editable True if the column is editable
6543      */
6544     setEditable : function(col, editable){
6545         this.config[col].editable = editable;
6546     },
6547
6548
6549     /**
6550      * Returns true if the column is hidden.
6551      * @param {Number} colIndex The column index
6552      * @return {Boolean}
6553      */
6554     isHidden : function(colIndex){
6555         return this.config[colIndex].hidden;
6556     },
6557
6558
6559     /**
6560      * Returns true if the column width cannot be changed
6561      */
6562     isFixed : function(colIndex){
6563         return this.config[colIndex].fixed;
6564     },
6565
6566     /**
6567      * Returns true if the column can be resized
6568      * @return {Boolean}
6569      */
6570     isResizable : function(colIndex){
6571         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6572     },
6573     /**
6574      * Sets if a column is hidden.
6575      * @param {Number} colIndex The column index
6576      * @param {Boolean} hidden True if the column is hidden
6577      */
6578     setHidden : function(colIndex, hidden){
6579         this.config[colIndex].hidden = hidden;
6580         this.totalWidth = null;
6581         this.fireEvent("hiddenchange", this, colIndex, hidden);
6582     },
6583
6584     /**
6585      * Sets the editor for a column.
6586      * @param {Number} col The column index
6587      * @param {Object} editor The editor object
6588      */
6589     setEditor : function(col, editor){
6590         this.config[col].editor = editor;
6591     }
6592 });
6593
6594 Roo.grid.ColumnModel.defaultRenderer = function(value)
6595 {
6596     if(typeof value == "object") {
6597         return value;
6598     }
6599         if(typeof value == "string" && value.length < 1){
6600             return "&#160;";
6601         }
6602     
6603         return String.format("{0}", value);
6604 };
6605
6606 // Alias for backwards compatibility
6607 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6608 /*
6609  * Based on:
6610  * Ext JS Library 1.1.1
6611  * Copyright(c) 2006-2007, Ext JS, LLC.
6612  *
6613  * Originally Released Under LGPL - original licence link has changed is not relivant.
6614  *
6615  * Fork - LGPL
6616  * <script type="text/javascript">
6617  */
6618  
6619 /**
6620  * @class Roo.LoadMask
6621  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6622  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6623  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6624  * element's UpdateManager load indicator and will be destroyed after the initial load.
6625  * @constructor
6626  * Create a new LoadMask
6627  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6628  * @param {Object} config The config object
6629  */
6630 Roo.LoadMask = function(el, config){
6631     this.el = Roo.get(el);
6632     Roo.apply(this, config);
6633     if(this.store){
6634         this.store.on('beforeload', this.onBeforeLoad, this);
6635         this.store.on('load', this.onLoad, this);
6636         this.store.on('loadexception', this.onLoadException, this);
6637         this.removeMask = false;
6638     }else{
6639         var um = this.el.getUpdateManager();
6640         um.showLoadIndicator = false; // disable the default indicator
6641         um.on('beforeupdate', this.onBeforeLoad, this);
6642         um.on('update', this.onLoad, this);
6643         um.on('failure', this.onLoad, this);
6644         this.removeMask = true;
6645     }
6646 };
6647
6648 Roo.LoadMask.prototype = {
6649     /**
6650      * @cfg {Boolean} removeMask
6651      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6652      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6653      */
6654     /**
6655      * @cfg {String} msg
6656      * The text to display in a centered loading message box (defaults to 'Loading...')
6657      */
6658     msg : 'Loading...',
6659     /**
6660      * @cfg {String} msgCls
6661      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6662      */
6663     msgCls : 'x-mask-loading',
6664
6665     /**
6666      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6667      * @type Boolean
6668      */
6669     disabled: false,
6670
6671     /**
6672      * Disables the mask to prevent it from being displayed
6673      */
6674     disable : function(){
6675        this.disabled = true;
6676     },
6677
6678     /**
6679      * Enables the mask so that it can be displayed
6680      */
6681     enable : function(){
6682         this.disabled = false;
6683     },
6684     
6685     onLoadException : function()
6686     {
6687         Roo.log(arguments);
6688         
6689         if (typeof(arguments[3]) != 'undefined') {
6690             Roo.MessageBox.alert("Error loading",arguments[3]);
6691         } 
6692         /*
6693         try {
6694             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6695                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6696             }   
6697         } catch(e) {
6698             
6699         }
6700         */
6701     
6702         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6703     },
6704     // private
6705     onLoad : function()
6706     {
6707         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6708     },
6709
6710     // private
6711     onBeforeLoad : function(){
6712         if(!this.disabled){
6713             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6714         }
6715     },
6716
6717     // private
6718     destroy : function(){
6719         if(this.store){
6720             this.store.un('beforeload', this.onBeforeLoad, this);
6721             this.store.un('load', this.onLoad, this);
6722             this.store.un('loadexception', this.onLoadException, this);
6723         }else{
6724             var um = this.el.getUpdateManager();
6725             um.un('beforeupdate', this.onBeforeLoad, this);
6726             um.un('update', this.onLoad, this);
6727             um.un('failure', this.onLoad, this);
6728         }
6729     }
6730 };/*
6731  * - LGPL
6732  *
6733  * table
6734  * 
6735  */
6736
6737 /**
6738  * @class Roo.bootstrap.Table
6739  * @extends Roo.bootstrap.Component
6740  * Bootstrap Table class
6741  * @cfg {String} cls table class
6742  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6743  * @cfg {String} bgcolor Specifies the background color for a table
6744  * @cfg {Number} border Specifies whether the table cells should have borders or not
6745  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6746  * @cfg {Number} cellspacing Specifies the space between cells
6747  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6748  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6749  * @cfg {String} sortable Specifies that the table should be sortable
6750  * @cfg {String} summary Specifies a summary of the content of a table
6751  * @cfg {Number} width Specifies the width of a table
6752  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6753  * 
6754  * @cfg {boolean} striped Should the rows be alternative striped
6755  * @cfg {boolean} bordered Add borders to the table
6756  * @cfg {boolean} hover Add hover highlighting
6757  * @cfg {boolean} condensed Format condensed
6758  * @cfg {boolean} responsive Format condensed
6759  * @cfg {Boolean} loadMask (true|false) default false
6760  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6761  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6762  * @cfg {Boolean} rowSelection (true|false) default false
6763  * @cfg {Boolean} cellSelection (true|false) default false
6764  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6765  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6766  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6767  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6768  
6769  * 
6770  * @constructor
6771  * Create a new Table
6772  * @param {Object} config The config object
6773  */
6774
6775 Roo.bootstrap.Table = function(config){
6776     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6777     
6778   
6779     
6780     // BC...
6781     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6782     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6783     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6784     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6785     
6786     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6787     if (this.sm) {
6788         this.sm.grid = this;
6789         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6790         this.sm = this.selModel;
6791         this.sm.xmodule = this.xmodule || false;
6792     }
6793     
6794     if (this.cm && typeof(this.cm.config) == 'undefined') {
6795         this.colModel = new Roo.grid.ColumnModel(this.cm);
6796         this.cm = this.colModel;
6797         this.cm.xmodule = this.xmodule || false;
6798     }
6799     if (this.store) {
6800         this.store= Roo.factory(this.store, Roo.data);
6801         this.ds = this.store;
6802         this.ds.xmodule = this.xmodule || false;
6803          
6804     }
6805     if (this.footer && this.store) {
6806         this.footer.dataSource = this.ds;
6807         this.footer = Roo.factory(this.footer);
6808     }
6809     
6810     /** @private */
6811     this.addEvents({
6812         /**
6813          * @event cellclick
6814          * Fires when a cell is clicked
6815          * @param {Roo.bootstrap.Table} this
6816          * @param {Roo.Element} el
6817          * @param {Number} rowIndex
6818          * @param {Number} columnIndex
6819          * @param {Roo.EventObject} e
6820          */
6821         "cellclick" : true,
6822         /**
6823          * @event celldblclick
6824          * Fires when a cell is double clicked
6825          * @param {Roo.bootstrap.Table} this
6826          * @param {Roo.Element} el
6827          * @param {Number} rowIndex
6828          * @param {Number} columnIndex
6829          * @param {Roo.EventObject} e
6830          */
6831         "celldblclick" : true,
6832         /**
6833          * @event rowclick
6834          * Fires when a row is clicked
6835          * @param {Roo.bootstrap.Table} this
6836          * @param {Roo.Element} el
6837          * @param {Number} rowIndex
6838          * @param {Roo.EventObject} e
6839          */
6840         "rowclick" : true,
6841         /**
6842          * @event rowdblclick
6843          * Fires when a row is double clicked
6844          * @param {Roo.bootstrap.Table} this
6845          * @param {Roo.Element} el
6846          * @param {Number} rowIndex
6847          * @param {Roo.EventObject} e
6848          */
6849         "rowdblclick" : true,
6850         /**
6851          * @event mouseover
6852          * Fires when a mouseover occur
6853          * @param {Roo.bootstrap.Table} this
6854          * @param {Roo.Element} el
6855          * @param {Number} rowIndex
6856          * @param {Number} columnIndex
6857          * @param {Roo.EventObject} e
6858          */
6859         "mouseover" : true,
6860         /**
6861          * @event mouseout
6862          * Fires when a mouseout occur
6863          * @param {Roo.bootstrap.Table} this
6864          * @param {Roo.Element} el
6865          * @param {Number} rowIndex
6866          * @param {Number} columnIndex
6867          * @param {Roo.EventObject} e
6868          */
6869         "mouseout" : true,
6870         /**
6871          * @event rowclass
6872          * Fires when a row is rendered, so you can change add a style to it.
6873          * @param {Roo.bootstrap.Table} this
6874          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6875          */
6876         'rowclass' : true,
6877           /**
6878          * @event rowsrendered
6879          * Fires when all the  rows have been rendered
6880          * @param {Roo.bootstrap.Table} this
6881          */
6882         'rowsrendered' : true,
6883         /**
6884          * @event contextmenu
6885          * The raw contextmenu event for the entire grid.
6886          * @param {Roo.EventObject} e
6887          */
6888         "contextmenu" : true,
6889         /**
6890          * @event rowcontextmenu
6891          * Fires when a row is right clicked
6892          * @param {Roo.bootstrap.Table} this
6893          * @param {Number} rowIndex
6894          * @param {Roo.EventObject} e
6895          */
6896         "rowcontextmenu" : true,
6897         /**
6898          * @event cellcontextmenu
6899          * Fires when a cell is right clicked
6900          * @param {Roo.bootstrap.Table} this
6901          * @param {Number} rowIndex
6902          * @param {Number} cellIndex
6903          * @param {Roo.EventObject} e
6904          */
6905          "cellcontextmenu" : true,
6906          /**
6907          * @event headercontextmenu
6908          * Fires when a header is right clicked
6909          * @param {Roo.bootstrap.Table} this
6910          * @param {Number} columnIndex
6911          * @param {Roo.EventObject} e
6912          */
6913         "headercontextmenu" : true
6914     });
6915 };
6916
6917 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6918     
6919     cls: false,
6920     align: false,
6921     bgcolor: false,
6922     border: false,
6923     cellpadding: false,
6924     cellspacing: false,
6925     frame: false,
6926     rules: false,
6927     sortable: false,
6928     summary: false,
6929     width: false,
6930     striped : false,
6931     scrollBody : false,
6932     bordered: false,
6933     hover:  false,
6934     condensed : false,
6935     responsive : false,
6936     sm : false,
6937     cm : false,
6938     store : false,
6939     loadMask : false,
6940     footerShow : true,
6941     headerShow : true,
6942   
6943     rowSelection : false,
6944     cellSelection : false,
6945     layout : false,
6946     
6947     // Roo.Element - the tbody
6948     mainBody: false,
6949     // Roo.Element - thead element
6950     mainHead: false,
6951     
6952     container: false, // used by gridpanel...
6953     
6954     lazyLoad : false,
6955     
6956     CSS : Roo.util.CSS,
6957     
6958     auto_hide_footer : false,
6959     
6960     getAutoCreate : function()
6961     {
6962         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6963         
6964         cfg = {
6965             tag: 'table',
6966             cls : 'table',
6967             cn : []
6968         };
6969         if (this.scrollBody) {
6970             cfg.cls += ' table-body-fixed';
6971         }    
6972         if (this.striped) {
6973             cfg.cls += ' table-striped';
6974         }
6975         
6976         if (this.hover) {
6977             cfg.cls += ' table-hover';
6978         }
6979         if (this.bordered) {
6980             cfg.cls += ' table-bordered';
6981         }
6982         if (this.condensed) {
6983             cfg.cls += ' table-condensed';
6984         }
6985         if (this.responsive) {
6986             cfg.cls += ' table-responsive';
6987         }
6988         
6989         if (this.cls) {
6990             cfg.cls+=  ' ' +this.cls;
6991         }
6992         
6993         // this lot should be simplifed...
6994         var _t = this;
6995         var cp = [
6996             'align',
6997             'bgcolor',
6998             'border',
6999             'cellpadding',
7000             'cellspacing',
7001             'frame',
7002             'rules',
7003             'sortable',
7004             'summary',
7005             'width'
7006         ].forEach(function(k) {
7007             if (_t[k]) {
7008                 cfg[k] = _t[k];
7009             }
7010         });
7011         
7012         
7013         if (this.layout) {
7014             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7015         }
7016         
7017         if(this.store || this.cm){
7018             if(this.headerShow){
7019                 cfg.cn.push(this.renderHeader());
7020             }
7021             
7022             cfg.cn.push(this.renderBody());
7023             
7024             if(this.footerShow){
7025                 cfg.cn.push(this.renderFooter());
7026             }
7027             // where does this come from?
7028             //cfg.cls+=  ' TableGrid';
7029         }
7030         
7031         return { cn : [ cfg ] };
7032     },
7033     
7034     initEvents : function()
7035     {   
7036         if(!this.store || !this.cm){
7037             return;
7038         }
7039         if (this.selModel) {
7040             this.selModel.initEvents();
7041         }
7042         
7043         
7044         //Roo.log('initEvents with ds!!!!');
7045         
7046         this.mainBody = this.el.select('tbody', true).first();
7047         this.mainHead = this.el.select('thead', true).first();
7048         this.mainFoot = this.el.select('tfoot', true).first();
7049         
7050         
7051         
7052         var _this = this;
7053         
7054         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7055             e.on('click', _this.sort, _this);
7056         });
7057         
7058         this.mainBody.on("click", this.onClick, this);
7059         this.mainBody.on("dblclick", this.onDblClick, this);
7060         
7061         // why is this done????? = it breaks dialogs??
7062         //this.parent().el.setStyle('position', 'relative');
7063         
7064         
7065         if (this.footer) {
7066             this.footer.parentId = this.id;
7067             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7068             
7069             if(this.lazyLoad){
7070                 this.el.select('tfoot tr td').first().addClass('hide');
7071             }
7072         } 
7073         
7074         if(this.loadMask) {
7075             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7076         }
7077         
7078         this.store.on('load', this.onLoad, this);
7079         this.store.on('beforeload', this.onBeforeLoad, this);
7080         this.store.on('update', this.onUpdate, this);
7081         this.store.on('add', this.onAdd, this);
7082         this.store.on("clear", this.clear, this);
7083         
7084         this.el.on("contextmenu", this.onContextMenu, this);
7085         
7086         this.mainBody.on('scroll', this.onBodyScroll, this);
7087         
7088         this.cm.on("headerchange", this.onHeaderChange, this);
7089         
7090         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7091         
7092     },
7093     
7094     onContextMenu : function(e, t)
7095     {
7096         this.processEvent("contextmenu", e);
7097     },
7098     
7099     processEvent : function(name, e)
7100     {
7101         if (name != 'touchstart' ) {
7102             this.fireEvent(name, e);    
7103         }
7104         
7105         var t = e.getTarget();
7106         
7107         var cell = Roo.get(t);
7108         
7109         if(!cell){
7110             return;
7111         }
7112         
7113         if(cell.findParent('tfoot', false, true)){
7114             return;
7115         }
7116         
7117         if(cell.findParent('thead', false, true)){
7118             
7119             if(e.getTarget().nodeName.toLowerCase() != 'th'){
7120                 cell = Roo.get(t).findParent('th', false, true);
7121                 if (!cell) {
7122                     Roo.log("failed to find th in thead?");
7123                     Roo.log(e.getTarget());
7124                     return;
7125                 }
7126             }
7127             
7128             var cellIndex = cell.dom.cellIndex;
7129             
7130             var ename = name == 'touchstart' ? 'click' : name;
7131             this.fireEvent("header" + ename, this, cellIndex, e);
7132             
7133             return;
7134         }
7135         
7136         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7137             cell = Roo.get(t).findParent('td', false, true);
7138             if (!cell) {
7139                 Roo.log("failed to find th in tbody?");
7140                 Roo.log(e.getTarget());
7141                 return;
7142             }
7143         }
7144         
7145         var row = cell.findParent('tr', false, true);
7146         var cellIndex = cell.dom.cellIndex;
7147         var rowIndex = row.dom.rowIndex - 1;
7148         
7149         if(row !== false){
7150             
7151             this.fireEvent("row" + name, this, rowIndex, e);
7152             
7153             if(cell !== false){
7154             
7155                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7156             }
7157         }
7158         
7159     },
7160     
7161     onMouseover : function(e, el)
7162     {
7163         var cell = Roo.get(el);
7164         
7165         if(!cell){
7166             return;
7167         }
7168         
7169         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7170             cell = cell.findParent('td', false, true);
7171         }
7172         
7173         var row = cell.findParent('tr', false, true);
7174         var cellIndex = cell.dom.cellIndex;
7175         var rowIndex = row.dom.rowIndex - 1; // start from 0
7176         
7177         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7178         
7179     },
7180     
7181     onMouseout : function(e, el)
7182     {
7183         var cell = Roo.get(el);
7184         
7185         if(!cell){
7186             return;
7187         }
7188         
7189         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7190             cell = cell.findParent('td', false, true);
7191         }
7192         
7193         var row = cell.findParent('tr', false, true);
7194         var cellIndex = cell.dom.cellIndex;
7195         var rowIndex = row.dom.rowIndex - 1; // start from 0
7196         
7197         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7198         
7199     },
7200     
7201     onClick : function(e, el)
7202     {
7203         var cell = Roo.get(el);
7204         
7205         if(!cell || (!this.cellSelection && !this.rowSelection)){
7206             return;
7207         }
7208         
7209         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7210             cell = cell.findParent('td', false, true);
7211         }
7212         
7213         if(!cell || typeof(cell) == 'undefined'){
7214             return;
7215         }
7216         
7217         var row = cell.findParent('tr', false, true);
7218         
7219         if(!row || typeof(row) == 'undefined'){
7220             return;
7221         }
7222         
7223         var cellIndex = cell.dom.cellIndex;
7224         var rowIndex = this.getRowIndex(row);
7225         
7226         // why??? - should these not be based on SelectionModel?
7227         if(this.cellSelection){
7228             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7229         }
7230         
7231         if(this.rowSelection){
7232             this.fireEvent('rowclick', this, row, rowIndex, e);
7233         }
7234         
7235         
7236     },
7237         
7238     onDblClick : function(e,el)
7239     {
7240         var cell = Roo.get(el);
7241         
7242         if(!cell || (!this.cellSelection && !this.rowSelection)){
7243             return;
7244         }
7245         
7246         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7247             cell = cell.findParent('td', false, true);
7248         }
7249         
7250         if(!cell || typeof(cell) == 'undefined'){
7251             return;
7252         }
7253         
7254         var row = cell.findParent('tr', false, true);
7255         
7256         if(!row || typeof(row) == 'undefined'){
7257             return;
7258         }
7259         
7260         var cellIndex = cell.dom.cellIndex;
7261         var rowIndex = this.getRowIndex(row);
7262         
7263         if(this.cellSelection){
7264             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7265         }
7266         
7267         if(this.rowSelection){
7268             this.fireEvent('rowdblclick', this, row, rowIndex, e);
7269         }
7270     },
7271     
7272     sort : function(e,el)
7273     {
7274         var col = Roo.get(el);
7275         
7276         if(!col.hasClass('sortable')){
7277             return;
7278         }
7279         
7280         var sort = col.attr('sort');
7281         var dir = 'ASC';
7282         
7283         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7284             dir = 'DESC';
7285         }
7286         
7287         this.store.sortInfo = {field : sort, direction : dir};
7288         
7289         if (this.footer) {
7290             Roo.log("calling footer first");
7291             this.footer.onClick('first');
7292         } else {
7293         
7294             this.store.load({ params : { start : 0 } });
7295         }
7296     },
7297     
7298     renderHeader : function()
7299     {
7300         var header = {
7301             tag: 'thead',
7302             cn : []
7303         };
7304         
7305         var cm = this.cm;
7306         this.totalWidth = 0;
7307         
7308         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7309             
7310             var config = cm.config[i];
7311             
7312             var c = {
7313                 tag: 'th',
7314                 cls : 'x-hcol-' + i,
7315                 style : '',
7316                 html: cm.getColumnHeader(i)
7317             };
7318             
7319             var hh = '';
7320             
7321             if(typeof(config.sortable) != 'undefined' && config.sortable){
7322                 c.cls = 'sortable';
7323                 c.html = '<i class="glyphicon"></i>' + c.html;
7324             }
7325             
7326             // could use BS4 hidden-..-down 
7327             
7328             if(typeof(config.lgHeader) != 'undefined'){
7329                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
7330             }
7331             
7332             if(typeof(config.mdHeader) != 'undefined'){
7333                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
7334             }
7335             
7336             if(typeof(config.smHeader) != 'undefined'){
7337                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
7338             }
7339             
7340             if(typeof(config.xsHeader) != 'undefined'){
7341                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
7342             }
7343             
7344             if(hh.length){
7345                 c.html = hh;
7346             }
7347             
7348             if(typeof(config.tooltip) != 'undefined'){
7349                 c.tooltip = config.tooltip;
7350             }
7351             
7352             if(typeof(config.colspan) != 'undefined'){
7353                 c.colspan = config.colspan;
7354             }
7355             
7356             if(typeof(config.hidden) != 'undefined' && config.hidden){
7357                 c.style += ' display:none;';
7358             }
7359             
7360             if(typeof(config.dataIndex) != 'undefined'){
7361                 c.sort = config.dataIndex;
7362             }
7363             
7364            
7365             
7366             if(typeof(config.align) != 'undefined' && config.align.length){
7367                 c.style += ' text-align:' + config.align + ';';
7368             }
7369             
7370             if(typeof(config.width) != 'undefined'){
7371                 c.style += ' width:' + config.width + 'px;';
7372                 this.totalWidth += config.width;
7373             } else {
7374                 this.totalWidth += 100; // assume minimum of 100 per column?
7375             }
7376             
7377             if(typeof(config.cls) != 'undefined'){
7378                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
7379             }
7380             
7381             ['xs','sm','md','lg'].map(function(size){
7382                 
7383                 if(typeof(config[size]) == 'undefined'){
7384                     return;
7385                 }
7386                  
7387                 if (!config[size]) { // 0 = hidden
7388                     // BS 4 '0' is treated as hide that column and below.
7389                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
7390                     return;
7391                 }
7392                 
7393                 c.cls += ' col-' + size + '-' + config[size] + (
7394                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7395                 );
7396                 
7397                 
7398             });
7399             
7400             header.cn.push(c)
7401         }
7402         
7403         return header;
7404     },
7405     
7406     renderBody : function()
7407     {
7408         var body = {
7409             tag: 'tbody',
7410             cn : [
7411                 {
7412                     tag: 'tr',
7413                     cn : [
7414                         {
7415                             tag : 'td',
7416                             colspan :  this.cm.getColumnCount()
7417                         }
7418                     ]
7419                 }
7420             ]
7421         };
7422         
7423         return body;
7424     },
7425     
7426     renderFooter : function()
7427     {
7428         var footer = {
7429             tag: 'tfoot',
7430             cn : [
7431                 {
7432                     tag: 'tr',
7433                     cn : [
7434                         {
7435                             tag : 'td',
7436                             colspan :  this.cm.getColumnCount()
7437                         }
7438                     ]
7439                 }
7440             ]
7441         };
7442         
7443         return footer;
7444     },
7445     
7446     
7447     
7448     onLoad : function()
7449     {
7450 //        Roo.log('ds onload');
7451         this.clear();
7452         
7453         var _this = this;
7454         var cm = this.cm;
7455         var ds = this.store;
7456         
7457         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7458             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
7459             if (_this.store.sortInfo) {
7460                     
7461                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
7462                     e.select('i', true).addClass(['glyphicon-arrow-up']);
7463                 }
7464                 
7465                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
7466                     e.select('i', true).addClass(['glyphicon-arrow-down']);
7467                 }
7468             }
7469         });
7470         
7471         var tbody =  this.mainBody;
7472               
7473         if(ds.getCount() > 0){
7474             ds.data.each(function(d,rowIndex){
7475                 var row =  this.renderRow(cm, ds, rowIndex);
7476                 
7477                 tbody.createChild(row);
7478                 
7479                 var _this = this;
7480                 
7481                 if(row.cellObjects.length){
7482                     Roo.each(row.cellObjects, function(r){
7483                         _this.renderCellObject(r);
7484                     })
7485                 }
7486                 
7487             }, this);
7488         }
7489         
7490         var tfoot = this.el.select('tfoot', true).first();
7491         
7492         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
7493             
7494             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
7495             
7496             var total = this.ds.getTotalCount();
7497             
7498             if(this.footer.pageSize < total){
7499                 this.mainFoot.show();
7500             }
7501         }
7502         
7503         Roo.each(this.el.select('tbody td', true).elements, function(e){
7504             e.on('mouseover', _this.onMouseover, _this);
7505         });
7506         
7507         Roo.each(this.el.select('tbody td', true).elements, function(e){
7508             e.on('mouseout', _this.onMouseout, _this);
7509         });
7510         this.fireEvent('rowsrendered', this);
7511         
7512         this.autoSize();
7513     },
7514     
7515     
7516     onUpdate : function(ds,record)
7517     {
7518         this.refreshRow(record);
7519         this.autoSize();
7520     },
7521     
7522     onRemove : function(ds, record, index, isUpdate){
7523         if(isUpdate !== true){
7524             this.fireEvent("beforerowremoved", this, index, record);
7525         }
7526         var bt = this.mainBody.dom;
7527         
7528         var rows = this.el.select('tbody > tr', true).elements;
7529         
7530         if(typeof(rows[index]) != 'undefined'){
7531             bt.removeChild(rows[index].dom);
7532         }
7533         
7534 //        if(bt.rows[index]){
7535 //            bt.removeChild(bt.rows[index]);
7536 //        }
7537         
7538         if(isUpdate !== true){
7539             //this.stripeRows(index);
7540             //this.syncRowHeights(index, index);
7541             //this.layout();
7542             this.fireEvent("rowremoved", this, index, record);
7543         }
7544     },
7545     
7546     onAdd : function(ds, records, rowIndex)
7547     {
7548         //Roo.log('on Add called');
7549         // - note this does not handle multiple adding very well..
7550         var bt = this.mainBody.dom;
7551         for (var i =0 ; i < records.length;i++) {
7552             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7553             //Roo.log(records[i]);
7554             //Roo.log(this.store.getAt(rowIndex+i));
7555             this.insertRow(this.store, rowIndex + i, false);
7556             return;
7557         }
7558         
7559     },
7560     
7561     
7562     refreshRow : function(record){
7563         var ds = this.store, index;
7564         if(typeof record == 'number'){
7565             index = record;
7566             record = ds.getAt(index);
7567         }else{
7568             index = ds.indexOf(record);
7569         }
7570         this.insertRow(ds, index, true);
7571         this.autoSize();
7572         this.onRemove(ds, record, index+1, true);
7573         this.autoSize();
7574         //this.syncRowHeights(index, index);
7575         //this.layout();
7576         this.fireEvent("rowupdated", this, index, record);
7577     },
7578     
7579     insertRow : function(dm, rowIndex, isUpdate){
7580         
7581         if(!isUpdate){
7582             this.fireEvent("beforerowsinserted", this, rowIndex);
7583         }
7584             //var s = this.getScrollState();
7585         var row = this.renderRow(this.cm, this.store, rowIndex);
7586         // insert before rowIndex..
7587         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7588         
7589         var _this = this;
7590                 
7591         if(row.cellObjects.length){
7592             Roo.each(row.cellObjects, function(r){
7593                 _this.renderCellObject(r);
7594             })
7595         }
7596             
7597         if(!isUpdate){
7598             this.fireEvent("rowsinserted", this, rowIndex);
7599             //this.syncRowHeights(firstRow, lastRow);
7600             //this.stripeRows(firstRow);
7601             //this.layout();
7602         }
7603         
7604     },
7605     
7606     
7607     getRowDom : function(rowIndex)
7608     {
7609         var rows = this.el.select('tbody > tr', true).elements;
7610         
7611         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7612         
7613     },
7614     // returns the object tree for a tr..
7615   
7616     
7617     renderRow : function(cm, ds, rowIndex) 
7618     {
7619         var d = ds.getAt(rowIndex);
7620         
7621         var row = {
7622             tag : 'tr',
7623             cls : 'x-row-' + rowIndex,
7624             cn : []
7625         };
7626             
7627         var cellObjects = [];
7628         
7629         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7630             var config = cm.config[i];
7631             
7632             var renderer = cm.getRenderer(i);
7633             var value = '';
7634             var id = false;
7635             
7636             if(typeof(renderer) !== 'undefined'){
7637                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7638             }
7639             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7640             // and are rendered into the cells after the row is rendered - using the id for the element.
7641             
7642             if(typeof(value) === 'object'){
7643                 id = Roo.id();
7644                 cellObjects.push({
7645                     container : id,
7646                     cfg : value 
7647                 })
7648             }
7649             
7650             var rowcfg = {
7651                 record: d,
7652                 rowIndex : rowIndex,
7653                 colIndex : i,
7654                 rowClass : ''
7655             };
7656
7657             this.fireEvent('rowclass', this, rowcfg);
7658             
7659             var td = {
7660                 tag: 'td',
7661                 cls : rowcfg.rowClass + ' x-col-' + i,
7662                 style: '',
7663                 html: (typeof(value) === 'object') ? '' : value
7664             };
7665             
7666             if (id) {
7667                 td.id = id;
7668             }
7669             
7670             if(typeof(config.colspan) != 'undefined'){
7671                 td.colspan = config.colspan;
7672             }
7673             
7674             if(typeof(config.hidden) != 'undefined' && config.hidden){
7675                 td.style += ' display:none;';
7676             }
7677             
7678             if(typeof(config.align) != 'undefined' && config.align.length){
7679                 td.style += ' text-align:' + config.align + ';';
7680             }
7681             if(typeof(config.valign) != 'undefined' && config.valign.length){
7682                 td.style += ' vertical-align:' + config.valign + ';';
7683             }
7684             
7685             if(typeof(config.width) != 'undefined'){
7686                 td.style += ' width:' +  config.width + 'px;';
7687             }
7688             
7689             if(typeof(config.cursor) != 'undefined'){
7690                 td.style += ' cursor:' +  config.cursor + ';';
7691             }
7692             
7693             if(typeof(config.cls) != 'undefined'){
7694                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7695             }
7696             
7697             ['xs','sm','md','lg'].map(function(size){
7698                 
7699                 if(typeof(config[size]) == 'undefined'){
7700                     return;
7701                 }
7702                 
7703                 
7704                   
7705                 if (!config[size]) { // 0 = hidden
7706                     // BS 4 '0' is treated as hide that column and below.
7707                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
7708                     return;
7709                 }
7710                 
7711                 td.cls += ' col-' + size + '-' + config[size] + (
7712                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
7713                 );
7714                  
7715
7716             });
7717             
7718             row.cn.push(td);
7719            
7720         }
7721         
7722         row.cellObjects = cellObjects;
7723         
7724         return row;
7725           
7726     },
7727     
7728     
7729     
7730     onBeforeLoad : function()
7731     {
7732         
7733     },
7734      /**
7735      * Remove all rows
7736      */
7737     clear : function()
7738     {
7739         this.el.select('tbody', true).first().dom.innerHTML = '';
7740     },
7741     /**
7742      * Show or hide a row.
7743      * @param {Number} rowIndex to show or hide
7744      * @param {Boolean} state hide
7745      */
7746     setRowVisibility : function(rowIndex, state)
7747     {
7748         var bt = this.mainBody.dom;
7749         
7750         var rows = this.el.select('tbody > tr', true).elements;
7751         
7752         if(typeof(rows[rowIndex]) == 'undefined'){
7753             return;
7754         }
7755         rows[rowIndex].dom.style.display = state ? '' : 'none';
7756     },
7757     
7758     
7759     getSelectionModel : function(){
7760         if(!this.selModel){
7761             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7762         }
7763         return this.selModel;
7764     },
7765     /*
7766      * Render the Roo.bootstrap object from renderder
7767      */
7768     renderCellObject : function(r)
7769     {
7770         var _this = this;
7771         
7772         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7773         
7774         var t = r.cfg.render(r.container);
7775         
7776         if(r.cfg.cn){
7777             Roo.each(r.cfg.cn, function(c){
7778                 var child = {
7779                     container: t.getChildContainer(),
7780                     cfg: c
7781                 };
7782                 _this.renderCellObject(child);
7783             })
7784         }
7785     },
7786     
7787     getRowIndex : function(row)
7788     {
7789         var rowIndex = -1;
7790         
7791         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7792             if(el != row){
7793                 return;
7794             }
7795             
7796             rowIndex = index;
7797         });
7798         
7799         return rowIndex;
7800     },
7801      /**
7802      * Returns the grid's underlying element = used by panel.Grid
7803      * @return {Element} The element
7804      */
7805     getGridEl : function(){
7806         return this.el;
7807     },
7808      /**
7809      * Forces a resize - used by panel.Grid
7810      * @return {Element} The element
7811      */
7812     autoSize : function()
7813     {
7814         //var ctr = Roo.get(this.container.dom.parentElement);
7815         var ctr = Roo.get(this.el.dom);
7816         
7817         var thd = this.getGridEl().select('thead',true).first();
7818         var tbd = this.getGridEl().select('tbody', true).first();
7819         var tfd = this.getGridEl().select('tfoot', true).first();
7820         
7821         var cw = ctr.getWidth();
7822         
7823         if (tbd) {
7824             
7825             tbd.setWidth(ctr.getWidth());
7826             // if the body has a max height - and then scrolls - we should perhaps set up the height here
7827             // this needs fixing for various usage - currently only hydra job advers I think..
7828             //tdb.setHeight(
7829             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7830             //); 
7831             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7832             cw -= barsize;
7833         }
7834         cw = Math.max(cw, this.totalWidth);
7835         this.getGridEl().select('tr',true).setWidth(cw);
7836         // resize 'expandable coloumn?
7837         
7838         return; // we doe not have a view in this design..
7839         
7840     },
7841     onBodyScroll: function()
7842     {
7843         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7844         if(this.mainHead){
7845             this.mainHead.setStyle({
7846                 'position' : 'relative',
7847                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7848             });
7849         }
7850         
7851         if(this.lazyLoad){
7852             
7853             var scrollHeight = this.mainBody.dom.scrollHeight;
7854             
7855             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7856             
7857             var height = this.mainBody.getHeight();
7858             
7859             if(scrollHeight - height == scrollTop) {
7860                 
7861                 var total = this.ds.getTotalCount();
7862                 
7863                 if(this.footer.cursor + this.footer.pageSize < total){
7864                     
7865                     this.footer.ds.load({
7866                         params : {
7867                             start : this.footer.cursor + this.footer.pageSize,
7868                             limit : this.footer.pageSize
7869                         },
7870                         add : true
7871                     });
7872                 }
7873             }
7874             
7875         }
7876     },
7877     
7878     onHeaderChange : function()
7879     {
7880         var header = this.renderHeader();
7881         var table = this.el.select('table', true).first();
7882         
7883         this.mainHead.remove();
7884         this.mainHead = table.createChild(header, this.mainBody, false);
7885     },
7886     
7887     onHiddenChange : function(colModel, colIndex, hidden)
7888     {
7889         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7890         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7891         
7892         this.CSS.updateRule(thSelector, "display", "");
7893         this.CSS.updateRule(tdSelector, "display", "");
7894         
7895         if(hidden){
7896             this.CSS.updateRule(thSelector, "display", "none");
7897             this.CSS.updateRule(tdSelector, "display", "none");
7898         }
7899         
7900         this.onHeaderChange();
7901         this.onLoad();
7902     },
7903     
7904     setColumnWidth: function(col_index, width)
7905     {
7906         // width = "md-2 xs-2..."
7907         if(!this.colModel.config[col_index]) {
7908             return;
7909         }
7910         
7911         var w = width.split(" ");
7912         
7913         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7914         
7915         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7916         
7917         
7918         for(var j = 0; j < w.length; j++) {
7919             
7920             if(!w[j]) {
7921                 continue;
7922             }
7923             
7924             var size_cls = w[j].split("-");
7925             
7926             if(!Number.isInteger(size_cls[1] * 1)) {
7927                 continue;
7928             }
7929             
7930             if(!this.colModel.config[col_index][size_cls[0]]) {
7931                 continue;
7932             }
7933             
7934             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7935                 continue;
7936             }
7937             
7938             h_row[0].classList.replace(
7939                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7940                 "col-"+size_cls[0]+"-"+size_cls[1]
7941             );
7942             
7943             for(var i = 0; i < rows.length; i++) {
7944                 
7945                 var size_cls = w[j].split("-");
7946                 
7947                 if(!Number.isInteger(size_cls[1] * 1)) {
7948                     continue;
7949                 }
7950                 
7951                 if(!this.colModel.config[col_index][size_cls[0]]) {
7952                     continue;
7953                 }
7954                 
7955                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7956                     continue;
7957                 }
7958                 
7959                 rows[i].classList.replace(
7960                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7961                     "col-"+size_cls[0]+"-"+size_cls[1]
7962                 );
7963             }
7964             
7965             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7966         }
7967     }
7968 });
7969
7970  
7971
7972  /*
7973  * - LGPL
7974  *
7975  * table cell
7976  * 
7977  */
7978
7979 /**
7980  * @class Roo.bootstrap.TableCell
7981  * @extends Roo.bootstrap.Component
7982  * Bootstrap TableCell class
7983  * @cfg {String} html cell contain text
7984  * @cfg {String} cls cell class
7985  * @cfg {String} tag cell tag (td|th) default td
7986  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7987  * @cfg {String} align Aligns the content in a cell
7988  * @cfg {String} axis Categorizes cells
7989  * @cfg {String} bgcolor Specifies the background color of a cell
7990  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7991  * @cfg {Number} colspan Specifies the number of columns a cell should span
7992  * @cfg {String} headers Specifies one or more header cells a cell is related to
7993  * @cfg {Number} height Sets the height of a cell
7994  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7995  * @cfg {Number} rowspan Sets the number of rows a cell should span
7996  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7997  * @cfg {String} valign Vertical aligns the content in a cell
7998  * @cfg {Number} width Specifies the width of a cell
7999  * 
8000  * @constructor
8001  * Create a new TableCell
8002  * @param {Object} config The config object
8003  */
8004
8005 Roo.bootstrap.TableCell = function(config){
8006     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8007 };
8008
8009 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8010     
8011     html: false,
8012     cls: false,
8013     tag: false,
8014     abbr: false,
8015     align: false,
8016     axis: false,
8017     bgcolor: false,
8018     charoff: false,
8019     colspan: false,
8020     headers: false,
8021     height: false,
8022     nowrap: false,
8023     rowspan: false,
8024     scope: false,
8025     valign: false,
8026     width: false,
8027     
8028     
8029     getAutoCreate : function(){
8030         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8031         
8032         cfg = {
8033             tag: 'td'
8034         };
8035         
8036         if(this.tag){
8037             cfg.tag = this.tag;
8038         }
8039         
8040         if (this.html) {
8041             cfg.html=this.html
8042         }
8043         if (this.cls) {
8044             cfg.cls=this.cls
8045         }
8046         if (this.abbr) {
8047             cfg.abbr=this.abbr
8048         }
8049         if (this.align) {
8050             cfg.align=this.align
8051         }
8052         if (this.axis) {
8053             cfg.axis=this.axis
8054         }
8055         if (this.bgcolor) {
8056             cfg.bgcolor=this.bgcolor
8057         }
8058         if (this.charoff) {
8059             cfg.charoff=this.charoff
8060         }
8061         if (this.colspan) {
8062             cfg.colspan=this.colspan
8063         }
8064         if (this.headers) {
8065             cfg.headers=this.headers
8066         }
8067         if (this.height) {
8068             cfg.height=this.height
8069         }
8070         if (this.nowrap) {
8071             cfg.nowrap=this.nowrap
8072         }
8073         if (this.rowspan) {
8074             cfg.rowspan=this.rowspan
8075         }
8076         if (this.scope) {
8077             cfg.scope=this.scope
8078         }
8079         if (this.valign) {
8080             cfg.valign=this.valign
8081         }
8082         if (this.width) {
8083             cfg.width=this.width
8084         }
8085         
8086         
8087         return cfg;
8088     }
8089    
8090 });
8091
8092  
8093
8094  /*
8095  * - LGPL
8096  *
8097  * table row
8098  * 
8099  */
8100
8101 /**
8102  * @class Roo.bootstrap.TableRow
8103  * @extends Roo.bootstrap.Component
8104  * Bootstrap TableRow class
8105  * @cfg {String} cls row class
8106  * @cfg {String} align Aligns the content in a table row
8107  * @cfg {String} bgcolor Specifies a background color for a table row
8108  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8109  * @cfg {String} valign Vertical aligns the content in a table row
8110  * 
8111  * @constructor
8112  * Create a new TableRow
8113  * @param {Object} config The config object
8114  */
8115
8116 Roo.bootstrap.TableRow = function(config){
8117     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8118 };
8119
8120 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
8121     
8122     cls: false,
8123     align: false,
8124     bgcolor: false,
8125     charoff: false,
8126     valign: false,
8127     
8128     getAutoCreate : function(){
8129         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8130         
8131         cfg = {
8132             tag: 'tr'
8133         };
8134             
8135         if(this.cls){
8136             cfg.cls = this.cls;
8137         }
8138         if(this.align){
8139             cfg.align = this.align;
8140         }
8141         if(this.bgcolor){
8142             cfg.bgcolor = this.bgcolor;
8143         }
8144         if(this.charoff){
8145             cfg.charoff = this.charoff;
8146         }
8147         if(this.valign){
8148             cfg.valign = this.valign;
8149         }
8150         
8151         return cfg;
8152     }
8153    
8154 });
8155
8156  
8157
8158  /*
8159  * - LGPL
8160  *
8161  * table body
8162  * 
8163  */
8164
8165 /**
8166  * @class Roo.bootstrap.TableBody
8167  * @extends Roo.bootstrap.Component
8168  * Bootstrap TableBody class
8169  * @cfg {String} cls element class
8170  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8171  * @cfg {String} align Aligns the content inside the element
8172  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8173  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8174  * 
8175  * @constructor
8176  * Create a new TableBody
8177  * @param {Object} config The config object
8178  */
8179
8180 Roo.bootstrap.TableBody = function(config){
8181     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8182 };
8183
8184 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
8185     
8186     cls: false,
8187     tag: false,
8188     align: false,
8189     charoff: false,
8190     valign: false,
8191     
8192     getAutoCreate : function(){
8193         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8194         
8195         cfg = {
8196             tag: 'tbody'
8197         };
8198             
8199         if (this.cls) {
8200             cfg.cls=this.cls
8201         }
8202         if(this.tag){
8203             cfg.tag = this.tag;
8204         }
8205         
8206         if(this.align){
8207             cfg.align = this.align;
8208         }
8209         if(this.charoff){
8210             cfg.charoff = this.charoff;
8211         }
8212         if(this.valign){
8213             cfg.valign = this.valign;
8214         }
8215         
8216         return cfg;
8217     }
8218     
8219     
8220 //    initEvents : function()
8221 //    {
8222 //        
8223 //        if(!this.store){
8224 //            return;
8225 //        }
8226 //        
8227 //        this.store = Roo.factory(this.store, Roo.data);
8228 //        this.store.on('load', this.onLoad, this);
8229 //        
8230 //        this.store.load();
8231 //        
8232 //    },
8233 //    
8234 //    onLoad: function () 
8235 //    {   
8236 //        this.fireEvent('load', this);
8237 //    }
8238 //    
8239 //   
8240 });
8241
8242  
8243
8244  /*
8245  * Based on:
8246  * Ext JS Library 1.1.1
8247  * Copyright(c) 2006-2007, Ext JS, LLC.
8248  *
8249  * Originally Released Under LGPL - original licence link has changed is not relivant.
8250  *
8251  * Fork - LGPL
8252  * <script type="text/javascript">
8253  */
8254
8255 // as we use this in bootstrap.
8256 Roo.namespace('Roo.form');
8257  /**
8258  * @class Roo.form.Action
8259  * Internal Class used to handle form actions
8260  * @constructor
8261  * @param {Roo.form.BasicForm} el The form element or its id
8262  * @param {Object} config Configuration options
8263  */
8264
8265  
8266  
8267 // define the action interface
8268 Roo.form.Action = function(form, options){
8269     this.form = form;
8270     this.options = options || {};
8271 };
8272 /**
8273  * Client Validation Failed
8274  * @const 
8275  */
8276 Roo.form.Action.CLIENT_INVALID = 'client';
8277 /**
8278  * Server Validation Failed
8279  * @const 
8280  */
8281 Roo.form.Action.SERVER_INVALID = 'server';
8282  /**
8283  * Connect to Server Failed
8284  * @const 
8285  */
8286 Roo.form.Action.CONNECT_FAILURE = 'connect';
8287 /**
8288  * Reading Data from Server Failed
8289  * @const 
8290  */
8291 Roo.form.Action.LOAD_FAILURE = 'load';
8292
8293 Roo.form.Action.prototype = {
8294     type : 'default',
8295     failureType : undefined,
8296     response : undefined,
8297     result : undefined,
8298
8299     // interface method
8300     run : function(options){
8301
8302     },
8303
8304     // interface method
8305     success : function(response){
8306
8307     },
8308
8309     // interface method
8310     handleResponse : function(response){
8311
8312     },
8313
8314     // default connection failure
8315     failure : function(response){
8316         
8317         this.response = response;
8318         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8319         this.form.afterAction(this, false);
8320     },
8321
8322     processResponse : function(response){
8323         this.response = response;
8324         if(!response.responseText){
8325             return true;
8326         }
8327         this.result = this.handleResponse(response);
8328         return this.result;
8329     },
8330
8331     // utility functions used internally
8332     getUrl : function(appendParams){
8333         var url = this.options.url || this.form.url || this.form.el.dom.action;
8334         if(appendParams){
8335             var p = this.getParams();
8336             if(p){
8337                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
8338             }
8339         }
8340         return url;
8341     },
8342
8343     getMethod : function(){
8344         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
8345     },
8346
8347     getParams : function(){
8348         var bp = this.form.baseParams;
8349         var p = this.options.params;
8350         if(p){
8351             if(typeof p == "object"){
8352                 p = Roo.urlEncode(Roo.applyIf(p, bp));
8353             }else if(typeof p == 'string' && bp){
8354                 p += '&' + Roo.urlEncode(bp);
8355             }
8356         }else if(bp){
8357             p = Roo.urlEncode(bp);
8358         }
8359         return p;
8360     },
8361
8362     createCallback : function(){
8363         return {
8364             success: this.success,
8365             failure: this.failure,
8366             scope: this,
8367             timeout: (this.form.timeout*1000),
8368             upload: this.form.fileUpload ? this.success : undefined
8369         };
8370     }
8371 };
8372
8373 Roo.form.Action.Submit = function(form, options){
8374     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
8375 };
8376
8377 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
8378     type : 'submit',
8379
8380     haveProgress : false,
8381     uploadComplete : false,
8382     
8383     // uploadProgress indicator.
8384     uploadProgress : function()
8385     {
8386         if (!this.form.progressUrl) {
8387             return;
8388         }
8389         
8390         if (!this.haveProgress) {
8391             Roo.MessageBox.progress("Uploading", "Uploading");
8392         }
8393         if (this.uploadComplete) {
8394            Roo.MessageBox.hide();
8395            return;
8396         }
8397         
8398         this.haveProgress = true;
8399    
8400         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
8401         
8402         var c = new Roo.data.Connection();
8403         c.request({
8404             url : this.form.progressUrl,
8405             params: {
8406                 id : uid
8407             },
8408             method: 'GET',
8409             success : function(req){
8410                //console.log(data);
8411                 var rdata = false;
8412                 var edata;
8413                 try  {
8414                    rdata = Roo.decode(req.responseText)
8415                 } catch (e) {
8416                     Roo.log("Invalid data from server..");
8417                     Roo.log(edata);
8418                     return;
8419                 }
8420                 if (!rdata || !rdata.success) {
8421                     Roo.log(rdata);
8422                     Roo.MessageBox.alert(Roo.encode(rdata));
8423                     return;
8424                 }
8425                 var data = rdata.data;
8426                 
8427                 if (this.uploadComplete) {
8428                    Roo.MessageBox.hide();
8429                    return;
8430                 }
8431                    
8432                 if (data){
8433                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
8434                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
8435                     );
8436                 }
8437                 this.uploadProgress.defer(2000,this);
8438             },
8439        
8440             failure: function(data) {
8441                 Roo.log('progress url failed ');
8442                 Roo.log(data);
8443             },
8444             scope : this
8445         });
8446            
8447     },
8448     
8449     
8450     run : function()
8451     {
8452         // run get Values on the form, so it syncs any secondary forms.
8453         this.form.getValues();
8454         
8455         var o = this.options;
8456         var method = this.getMethod();
8457         var isPost = method == 'POST';
8458         if(o.clientValidation === false || this.form.isValid()){
8459             
8460             if (this.form.progressUrl) {
8461                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
8462                     (new Date() * 1) + '' + Math.random());
8463                     
8464             } 
8465             
8466             
8467             Roo.Ajax.request(Roo.apply(this.createCallback(), {
8468                 form:this.form.el.dom,
8469                 url:this.getUrl(!isPost),
8470                 method: method,
8471                 params:isPost ? this.getParams() : null,
8472                 isUpload: this.form.fileUpload,
8473                 formData : this.form.formData
8474             }));
8475             
8476             this.uploadProgress();
8477
8478         }else if (o.clientValidation !== false){ // client validation failed
8479             this.failureType = Roo.form.Action.CLIENT_INVALID;
8480             this.form.afterAction(this, false);
8481         }
8482     },
8483
8484     success : function(response)
8485     {
8486         this.uploadComplete= true;
8487         if (this.haveProgress) {
8488             Roo.MessageBox.hide();
8489         }
8490         
8491         
8492         var result = this.processResponse(response);
8493         if(result === true || result.success){
8494             this.form.afterAction(this, true);
8495             return;
8496         }
8497         if(result.errors){
8498             this.form.markInvalid(result.errors);
8499             this.failureType = Roo.form.Action.SERVER_INVALID;
8500         }
8501         this.form.afterAction(this, false);
8502     },
8503     failure : function(response)
8504     {
8505         this.uploadComplete= true;
8506         if (this.haveProgress) {
8507             Roo.MessageBox.hide();
8508         }
8509         
8510         this.response = response;
8511         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8512         this.form.afterAction(this, false);
8513     },
8514     
8515     handleResponse : function(response){
8516         if(this.form.errorReader){
8517             var rs = this.form.errorReader.read(response);
8518             var errors = [];
8519             if(rs.records){
8520                 for(var i = 0, len = rs.records.length; i < len; i++) {
8521                     var r = rs.records[i];
8522                     errors[i] = r.data;
8523                 }
8524             }
8525             if(errors.length < 1){
8526                 errors = null;
8527             }
8528             return {
8529                 success : rs.success,
8530                 errors : errors
8531             };
8532         }
8533         var ret = false;
8534         try {
8535             ret = Roo.decode(response.responseText);
8536         } catch (e) {
8537             ret = {
8538                 success: false,
8539                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8540                 errors : []
8541             };
8542         }
8543         return ret;
8544         
8545     }
8546 });
8547
8548
8549 Roo.form.Action.Load = function(form, options){
8550     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8551     this.reader = this.form.reader;
8552 };
8553
8554 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8555     type : 'load',
8556
8557     run : function(){
8558         
8559         Roo.Ajax.request(Roo.apply(
8560                 this.createCallback(), {
8561                     method:this.getMethod(),
8562                     url:this.getUrl(false),
8563                     params:this.getParams()
8564         }));
8565     },
8566
8567     success : function(response){
8568         
8569         var result = this.processResponse(response);
8570         if(result === true || !result.success || !result.data){
8571             this.failureType = Roo.form.Action.LOAD_FAILURE;
8572             this.form.afterAction(this, false);
8573             return;
8574         }
8575         this.form.clearInvalid();
8576         this.form.setValues(result.data);
8577         this.form.afterAction(this, true);
8578     },
8579
8580     handleResponse : function(response){
8581         if(this.form.reader){
8582             var rs = this.form.reader.read(response);
8583             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8584             return {
8585                 success : rs.success,
8586                 data : data
8587             };
8588         }
8589         return Roo.decode(response.responseText);
8590     }
8591 });
8592
8593 Roo.form.Action.ACTION_TYPES = {
8594     'load' : Roo.form.Action.Load,
8595     'submit' : Roo.form.Action.Submit
8596 };/*
8597  * - LGPL
8598  *
8599  * form
8600  *
8601  */
8602
8603 /**
8604  * @class Roo.bootstrap.Form
8605  * @extends Roo.bootstrap.Component
8606  * Bootstrap Form class
8607  * @cfg {String} method  GET | POST (default POST)
8608  * @cfg {String} labelAlign top | left (default top)
8609  * @cfg {String} align left  | right - for navbars
8610  * @cfg {Boolean} loadMask load mask when submit (default true)
8611
8612  *
8613  * @constructor
8614  * Create a new Form
8615  * @param {Object} config The config object
8616  */
8617
8618
8619 Roo.bootstrap.Form = function(config){
8620     
8621     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8622     
8623     Roo.bootstrap.Form.popover.apply();
8624     
8625     this.addEvents({
8626         /**
8627          * @event clientvalidation
8628          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8629          * @param {Form} this
8630          * @param {Boolean} valid true if the form has passed client-side validation
8631          */
8632         clientvalidation: true,
8633         /**
8634          * @event beforeaction
8635          * Fires before any action is performed. Return false to cancel the action.
8636          * @param {Form} this
8637          * @param {Action} action The action to be performed
8638          */
8639         beforeaction: true,
8640         /**
8641          * @event actionfailed
8642          * Fires when an action fails.
8643          * @param {Form} this
8644          * @param {Action} action The action that failed
8645          */
8646         actionfailed : true,
8647         /**
8648          * @event actioncomplete
8649          * Fires when an action is completed.
8650          * @param {Form} this
8651          * @param {Action} action The action that completed
8652          */
8653         actioncomplete : true
8654     });
8655 };
8656
8657 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8658
8659      /**
8660      * @cfg {String} method
8661      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8662      */
8663     method : 'POST',
8664     /**
8665      * @cfg {String} url
8666      * The URL to use for form actions if one isn't supplied in the action options.
8667      */
8668     /**
8669      * @cfg {Boolean} fileUpload
8670      * Set to true if this form is a file upload.
8671      */
8672
8673     /**
8674      * @cfg {Object} baseParams
8675      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8676      */
8677
8678     /**
8679      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8680      */
8681     timeout: 30,
8682     /**
8683      * @cfg {Sting} align (left|right) for navbar forms
8684      */
8685     align : 'left',
8686
8687     // private
8688     activeAction : null,
8689
8690     /**
8691      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8692      * element by passing it or its id or mask the form itself by passing in true.
8693      * @type Mixed
8694      */
8695     waitMsgTarget : false,
8696
8697     loadMask : true,
8698     
8699     /**
8700      * @cfg {Boolean} errorMask (true|false) default false
8701      */
8702     errorMask : false,
8703     
8704     /**
8705      * @cfg {Number} maskOffset Default 100
8706      */
8707     maskOffset : 100,
8708     
8709     /**
8710      * @cfg {Boolean} maskBody
8711      */
8712     maskBody : false,
8713
8714     getAutoCreate : function(){
8715
8716         var cfg = {
8717             tag: 'form',
8718             method : this.method || 'POST',
8719             id : this.id || Roo.id(),
8720             cls : ''
8721         };
8722         if (this.parent().xtype.match(/^Nav/)) {
8723             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8724
8725         }
8726
8727         if (this.labelAlign == 'left' ) {
8728             cfg.cls += ' form-horizontal';
8729         }
8730
8731
8732         return cfg;
8733     },
8734     initEvents : function()
8735     {
8736         this.el.on('submit', this.onSubmit, this);
8737         // this was added as random key presses on the form where triggering form submit.
8738         this.el.on('keypress', function(e) {
8739             if (e.getCharCode() != 13) {
8740                 return true;
8741             }
8742             // we might need to allow it for textareas.. and some other items.
8743             // check e.getTarget().
8744
8745             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8746                 return true;
8747             }
8748
8749             Roo.log("keypress blocked");
8750
8751             e.preventDefault();
8752             return false;
8753         });
8754         
8755     },
8756     // private
8757     onSubmit : function(e){
8758         e.stopEvent();
8759     },
8760
8761      /**
8762      * Returns true if client-side validation on the form is successful.
8763      * @return Boolean
8764      */
8765     isValid : function(){
8766         var items = this.getItems();
8767         var valid = true;
8768         var target = false;
8769         
8770         items.each(function(f){
8771             
8772             if(f.validate()){
8773                 return;
8774             }
8775             
8776             Roo.log('invalid field: ' + f.name);
8777             
8778             valid = false;
8779
8780             if(!target && f.el.isVisible(true)){
8781                 target = f;
8782             }
8783            
8784         });
8785         
8786         if(this.errorMask && !valid){
8787             Roo.bootstrap.Form.popover.mask(this, target);
8788         }
8789         
8790         return valid;
8791     },
8792     
8793     /**
8794      * Returns true if any fields in this form have changed since their original load.
8795      * @return Boolean
8796      */
8797     isDirty : function(){
8798         var dirty = false;
8799         var items = this.getItems();
8800         items.each(function(f){
8801            if(f.isDirty()){
8802                dirty = true;
8803                return false;
8804            }
8805            return true;
8806         });
8807         return dirty;
8808     },
8809      /**
8810      * Performs a predefined action (submit or load) or custom actions you define on this form.
8811      * @param {String} actionName The name of the action type
8812      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8813      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8814      * accept other config options):
8815      * <pre>
8816 Property          Type             Description
8817 ----------------  ---------------  ----------------------------------------------------------------------------------
8818 url               String           The url for the action (defaults to the form's url)
8819 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8820 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8821 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8822                                    validate the form on the client (defaults to false)
8823      * </pre>
8824      * @return {BasicForm} this
8825      */
8826     doAction : function(action, options){
8827         if(typeof action == 'string'){
8828             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8829         }
8830         if(this.fireEvent('beforeaction', this, action) !== false){
8831             this.beforeAction(action);
8832             action.run.defer(100, action);
8833         }
8834         return this;
8835     },
8836
8837     // private
8838     beforeAction : function(action){
8839         var o = action.options;
8840         
8841         if(this.loadMask){
8842             
8843             if(this.maskBody){
8844                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8845             } else {
8846                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8847             }
8848         }
8849         // not really supported yet.. ??
8850
8851         //if(this.waitMsgTarget === true){
8852         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8853         //}else if(this.waitMsgTarget){
8854         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8855         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8856         //}else {
8857         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8858        // }
8859
8860     },
8861
8862     // private
8863     afterAction : function(action, success){
8864         this.activeAction = null;
8865         var o = action.options;
8866
8867         if(this.loadMask){
8868             
8869             if(this.maskBody){
8870                 Roo.get(document.body).unmask();
8871             } else {
8872                 this.el.unmask();
8873             }
8874         }
8875         
8876         //if(this.waitMsgTarget === true){
8877 //            this.el.unmask();
8878         //}else if(this.waitMsgTarget){
8879         //    this.waitMsgTarget.unmask();
8880         //}else{
8881         //    Roo.MessageBox.updateProgress(1);
8882         //    Roo.MessageBox.hide();
8883        // }
8884         //
8885         if(success){
8886             if(o.reset){
8887                 this.reset();
8888             }
8889             Roo.callback(o.success, o.scope, [this, action]);
8890             this.fireEvent('actioncomplete', this, action);
8891
8892         }else{
8893
8894             // failure condition..
8895             // we have a scenario where updates need confirming.
8896             // eg. if a locking scenario exists..
8897             // we look for { errors : { needs_confirm : true }} in the response.
8898             if (
8899                 (typeof(action.result) != 'undefined')  &&
8900                 (typeof(action.result.errors) != 'undefined')  &&
8901                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8902            ){
8903                 var _t = this;
8904                 Roo.log("not supported yet");
8905                  /*
8906
8907                 Roo.MessageBox.confirm(
8908                     "Change requires confirmation",
8909                     action.result.errorMsg,
8910                     function(r) {
8911                         if (r != 'yes') {
8912                             return;
8913                         }
8914                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8915                     }
8916
8917                 );
8918                 */
8919
8920
8921                 return;
8922             }
8923
8924             Roo.callback(o.failure, o.scope, [this, action]);
8925             // show an error message if no failed handler is set..
8926             if (!this.hasListener('actionfailed')) {
8927                 Roo.log("need to add dialog support");
8928                 /*
8929                 Roo.MessageBox.alert("Error",
8930                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8931                         action.result.errorMsg :
8932                         "Saving Failed, please check your entries or try again"
8933                 );
8934                 */
8935             }
8936
8937             this.fireEvent('actionfailed', this, action);
8938         }
8939
8940     },
8941     /**
8942      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8943      * @param {String} id The value to search for
8944      * @return Field
8945      */
8946     findField : function(id){
8947         var items = this.getItems();
8948         var field = items.get(id);
8949         if(!field){
8950              items.each(function(f){
8951                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8952                     field = f;
8953                     return false;
8954                 }
8955                 return true;
8956             });
8957         }
8958         return field || null;
8959     },
8960      /**
8961      * Mark fields in this form invalid in bulk.
8962      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8963      * @return {BasicForm} this
8964      */
8965     markInvalid : function(errors){
8966         if(errors instanceof Array){
8967             for(var i = 0, len = errors.length; i < len; i++){
8968                 var fieldError = errors[i];
8969                 var f = this.findField(fieldError.id);
8970                 if(f){
8971                     f.markInvalid(fieldError.msg);
8972                 }
8973             }
8974         }else{
8975             var field, id;
8976             for(id in errors){
8977                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8978                     field.markInvalid(errors[id]);
8979                 }
8980             }
8981         }
8982         //Roo.each(this.childForms || [], function (f) {
8983         //    f.markInvalid(errors);
8984         //});
8985
8986         return this;
8987     },
8988
8989     /**
8990      * Set values for fields in this form in bulk.
8991      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8992      * @return {BasicForm} this
8993      */
8994     setValues : function(values){
8995         if(values instanceof Array){ // array of objects
8996             for(var i = 0, len = values.length; i < len; i++){
8997                 var v = values[i];
8998                 var f = this.findField(v.id);
8999                 if(f){
9000                     f.setValue(v.value);
9001                     if(this.trackResetOnLoad){
9002                         f.originalValue = f.getValue();
9003                     }
9004                 }
9005             }
9006         }else{ // object hash
9007             var field, id;
9008             for(id in values){
9009                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9010
9011                     if (field.setFromData &&
9012                         field.valueField &&
9013                         field.displayField &&
9014                         // combos' with local stores can
9015                         // be queried via setValue()
9016                         // to set their value..
9017                         (field.store && !field.store.isLocal)
9018                         ) {
9019                         // it's a combo
9020                         var sd = { };
9021                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9022                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9023                         field.setFromData(sd);
9024
9025                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9026                         
9027                         field.setFromData(values);
9028                         
9029                     } else {
9030                         field.setValue(values[id]);
9031                     }
9032
9033
9034                     if(this.trackResetOnLoad){
9035                         field.originalValue = field.getValue();
9036                     }
9037                 }
9038             }
9039         }
9040
9041         //Roo.each(this.childForms || [], function (f) {
9042         //    f.setValues(values);
9043         //});
9044
9045         return this;
9046     },
9047
9048     /**
9049      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9050      * they are returned as an array.
9051      * @param {Boolean} asString
9052      * @return {Object}
9053      */
9054     getValues : function(asString){
9055         //if (this.childForms) {
9056             // copy values from the child forms
9057         //    Roo.each(this.childForms, function (f) {
9058         //        this.setValues(f.getValues());
9059         //    }, this);
9060         //}
9061
9062
9063
9064         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9065         if(asString === true){
9066             return fs;
9067         }
9068         return Roo.urlDecode(fs);
9069     },
9070
9071     /**
9072      * Returns the fields in this form as an object with key/value pairs.
9073      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9074      * @return {Object}
9075      */
9076     getFieldValues : function(with_hidden)
9077     {
9078         var items = this.getItems();
9079         var ret = {};
9080         items.each(function(f){
9081             
9082             if (!f.getName()) {
9083                 return;
9084             }
9085             
9086             var v = f.getValue();
9087             
9088             if (f.inputType =='radio') {
9089                 if (typeof(ret[f.getName()]) == 'undefined') {
9090                     ret[f.getName()] = ''; // empty..
9091                 }
9092
9093                 if (!f.el.dom.checked) {
9094                     return;
9095
9096                 }
9097                 v = f.el.dom.value;
9098
9099             }
9100             
9101             if(f.xtype == 'MoneyField'){
9102                 ret[f.currencyName] = f.getCurrency();
9103             }
9104
9105             // not sure if this supported any more..
9106             if ((typeof(v) == 'object') && f.getRawValue) {
9107                 v = f.getRawValue() ; // dates..
9108             }
9109             // combo boxes where name != hiddenName...
9110             if (f.name !== false && f.name != '' && f.name != f.getName()) {
9111                 ret[f.name] = f.getRawValue();
9112             }
9113             ret[f.getName()] = v;
9114         });
9115
9116         return ret;
9117     },
9118
9119     /**
9120      * Clears all invalid messages in this form.
9121      * @return {BasicForm} this
9122      */
9123     clearInvalid : function(){
9124         var items = this.getItems();
9125
9126         items.each(function(f){
9127            f.clearInvalid();
9128         });
9129
9130         return this;
9131     },
9132
9133     /**
9134      * Resets this form.
9135      * @return {BasicForm} this
9136      */
9137     reset : function(){
9138         var items = this.getItems();
9139         items.each(function(f){
9140             f.reset();
9141         });
9142
9143         Roo.each(this.childForms || [], function (f) {
9144             f.reset();
9145         });
9146
9147
9148         return this;
9149     },
9150     
9151     getItems : function()
9152     {
9153         var r=new Roo.util.MixedCollection(false, function(o){
9154             return o.id || (o.id = Roo.id());
9155         });
9156         var iter = function(el) {
9157             if (el.inputEl) {
9158                 r.add(el);
9159             }
9160             if (!el.items) {
9161                 return;
9162             }
9163             Roo.each(el.items,function(e) {
9164                 iter(e);
9165             });
9166         };
9167
9168         iter(this);
9169         return r;
9170     },
9171     
9172     hideFields : function(items)
9173     {
9174         Roo.each(items, function(i){
9175             
9176             var f = this.findField(i);
9177             
9178             if(!f){
9179                 return;
9180             }
9181             
9182             f.hide();
9183             
9184         }, this);
9185     },
9186     
9187     showFields : function(items)
9188     {
9189         Roo.each(items, function(i){
9190             
9191             var f = this.findField(i);
9192             
9193             if(!f){
9194                 return;
9195             }
9196             
9197             f.show();
9198             
9199         }, this);
9200     }
9201
9202 });
9203
9204 Roo.apply(Roo.bootstrap.Form, {
9205     
9206     popover : {
9207         
9208         padding : 5,
9209         
9210         isApplied : false,
9211         
9212         isMasked : false,
9213         
9214         form : false,
9215         
9216         target : false,
9217         
9218         toolTip : false,
9219         
9220         intervalID : false,
9221         
9222         maskEl : false,
9223         
9224         apply : function()
9225         {
9226             if(this.isApplied){
9227                 return;
9228             }
9229             
9230             this.maskEl = {
9231                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9232                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9233                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9234                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9235             };
9236             
9237             this.maskEl.top.enableDisplayMode("block");
9238             this.maskEl.left.enableDisplayMode("block");
9239             this.maskEl.bottom.enableDisplayMode("block");
9240             this.maskEl.right.enableDisplayMode("block");
9241             
9242             this.toolTip = new Roo.bootstrap.Tooltip({
9243                 cls : 'roo-form-error-popover',
9244                 alignment : {
9245                     'left' : ['r-l', [-2,0], 'right'],
9246                     'right' : ['l-r', [2,0], 'left'],
9247                     'bottom' : ['tl-bl', [0,2], 'top'],
9248                     'top' : [ 'bl-tl', [0,-2], 'bottom']
9249                 }
9250             });
9251             
9252             this.toolTip.render(Roo.get(document.body));
9253
9254             this.toolTip.el.enableDisplayMode("block");
9255             
9256             Roo.get(document.body).on('click', function(){
9257                 this.unmask();
9258             }, this);
9259             
9260             Roo.get(document.body).on('touchstart', function(){
9261                 this.unmask();
9262             }, this);
9263             
9264             this.isApplied = true
9265         },
9266         
9267         mask : function(form, target)
9268         {
9269             this.form = form;
9270             
9271             this.target = target;
9272             
9273             if(!this.form.errorMask || !target.el){
9274                 return;
9275             }
9276             
9277             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9278             
9279             Roo.log(scrollable);
9280             
9281             var ot = this.target.el.calcOffsetsTo(scrollable);
9282             
9283             var scrollTo = ot[1] - this.form.maskOffset;
9284             
9285             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9286             
9287             scrollable.scrollTo('top', scrollTo);
9288             
9289             var box = this.target.el.getBox();
9290             Roo.log(box);
9291             var zIndex = Roo.bootstrap.Modal.zIndex++;
9292
9293             
9294             this.maskEl.top.setStyle('position', 'absolute');
9295             this.maskEl.top.setStyle('z-index', zIndex);
9296             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9297             this.maskEl.top.setLeft(0);
9298             this.maskEl.top.setTop(0);
9299             this.maskEl.top.show();
9300             
9301             this.maskEl.left.setStyle('position', 'absolute');
9302             this.maskEl.left.setStyle('z-index', zIndex);
9303             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9304             this.maskEl.left.setLeft(0);
9305             this.maskEl.left.setTop(box.y - this.padding);
9306             this.maskEl.left.show();
9307
9308             this.maskEl.bottom.setStyle('position', 'absolute');
9309             this.maskEl.bottom.setStyle('z-index', zIndex);
9310             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9311             this.maskEl.bottom.setLeft(0);
9312             this.maskEl.bottom.setTop(box.bottom + this.padding);
9313             this.maskEl.bottom.show();
9314
9315             this.maskEl.right.setStyle('position', 'absolute');
9316             this.maskEl.right.setStyle('z-index', zIndex);
9317             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9318             this.maskEl.right.setLeft(box.right + this.padding);
9319             this.maskEl.right.setTop(box.y - this.padding);
9320             this.maskEl.right.show();
9321
9322             this.toolTip.bindEl = this.target.el;
9323
9324             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
9325
9326             var tip = this.target.blankText;
9327
9328             if(this.target.getValue() !== '' ) {
9329                 
9330                 if (this.target.invalidText.length) {
9331                     tip = this.target.invalidText;
9332                 } else if (this.target.regexText.length){
9333                     tip = this.target.regexText;
9334                 }
9335             }
9336
9337             this.toolTip.show(tip);
9338
9339             this.intervalID = window.setInterval(function() {
9340                 Roo.bootstrap.Form.popover.unmask();
9341             }, 10000);
9342
9343             window.onwheel = function(){ return false;};
9344             
9345             (function(){ this.isMasked = true; }).defer(500, this);
9346             
9347         },
9348         
9349         unmask : function()
9350         {
9351             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
9352                 return;
9353             }
9354             
9355             this.maskEl.top.setStyle('position', 'absolute');
9356             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
9357             this.maskEl.top.hide();
9358
9359             this.maskEl.left.setStyle('position', 'absolute');
9360             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
9361             this.maskEl.left.hide();
9362
9363             this.maskEl.bottom.setStyle('position', 'absolute');
9364             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
9365             this.maskEl.bottom.hide();
9366
9367             this.maskEl.right.setStyle('position', 'absolute');
9368             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
9369             this.maskEl.right.hide();
9370             
9371             this.toolTip.hide();
9372             
9373             this.toolTip.el.hide();
9374             
9375             window.onwheel = function(){ return true;};
9376             
9377             if(this.intervalID){
9378                 window.clearInterval(this.intervalID);
9379                 this.intervalID = false;
9380             }
9381             
9382             this.isMasked = false;
9383             
9384         }
9385         
9386     }
9387     
9388 });
9389
9390 /*
9391  * Based on:
9392  * Ext JS Library 1.1.1
9393  * Copyright(c) 2006-2007, Ext JS, LLC.
9394  *
9395  * Originally Released Under LGPL - original licence link has changed is not relivant.
9396  *
9397  * Fork - LGPL
9398  * <script type="text/javascript">
9399  */
9400 /**
9401  * @class Roo.form.VTypes
9402  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
9403  * @singleton
9404  */
9405 Roo.form.VTypes = function(){
9406     // closure these in so they are only created once.
9407     var alpha = /^[a-zA-Z_]+$/;
9408     var alphanum = /^[a-zA-Z0-9_]+$/;
9409     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
9410     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
9411
9412     // All these messages and functions are configurable
9413     return {
9414         /**
9415          * The function used to validate email addresses
9416          * @param {String} value The email address
9417          */
9418         'email' : function(v){
9419             return email.test(v);
9420         },
9421         /**
9422          * The error text to display when the email validation function returns false
9423          * @type String
9424          */
9425         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
9426         /**
9427          * The keystroke filter mask to be applied on email input
9428          * @type RegExp
9429          */
9430         'emailMask' : /[a-z0-9_\.\-@]/i,
9431
9432         /**
9433          * The function used to validate URLs
9434          * @param {String} value The URL
9435          */
9436         'url' : function(v){
9437             return url.test(v);
9438         },
9439         /**
9440          * The error text to display when the url validation function returns false
9441          * @type String
9442          */
9443         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
9444         
9445         /**
9446          * The function used to validate alpha values
9447          * @param {String} value The value
9448          */
9449         'alpha' : function(v){
9450             return alpha.test(v);
9451         },
9452         /**
9453          * The error text to display when the alpha validation function returns false
9454          * @type String
9455          */
9456         'alphaText' : 'This field should only contain letters and _',
9457         /**
9458          * The keystroke filter mask to be applied on alpha input
9459          * @type RegExp
9460          */
9461         'alphaMask' : /[a-z_]/i,
9462
9463         /**
9464          * The function used to validate alphanumeric values
9465          * @param {String} value The value
9466          */
9467         'alphanum' : function(v){
9468             return alphanum.test(v);
9469         },
9470         /**
9471          * The error text to display when the alphanumeric validation function returns false
9472          * @type String
9473          */
9474         'alphanumText' : 'This field should only contain letters, numbers and _',
9475         /**
9476          * The keystroke filter mask to be applied on alphanumeric input
9477          * @type RegExp
9478          */
9479         'alphanumMask' : /[a-z0-9_]/i
9480     };
9481 }();/*
9482  * - LGPL
9483  *
9484  * Input
9485  * 
9486  */
9487
9488 /**
9489  * @class Roo.bootstrap.Input
9490  * @extends Roo.bootstrap.Component
9491  * Bootstrap Input class
9492  * @cfg {Boolean} disabled is it disabled
9493  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
9494  * @cfg {String} name name of the input
9495  * @cfg {string} fieldLabel - the label associated
9496  * @cfg {string} placeholder - placeholder to put in text.
9497  * @cfg {string}  before - input group add on before
9498  * @cfg {string} after - input group add on after
9499  * @cfg {string} size - (lg|sm) or leave empty..
9500  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
9501  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
9502  * @cfg {Number} md colspan out of 12 for computer-sized screens
9503  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
9504  * @cfg {string} value default value of the input
9505  * @cfg {Number} labelWidth set the width of label 
9506  * @cfg {Number} labellg set the width of label (1-12)
9507  * @cfg {Number} labelmd set the width of label (1-12)
9508  * @cfg {Number} labelsm set the width of label (1-12)
9509  * @cfg {Number} labelxs set the width of label (1-12)
9510  * @cfg {String} labelAlign (top|left)
9511  * @cfg {Boolean} readOnly Specifies that the field should be read-only
9512  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9513  * @cfg {String} indicatorpos (left|right) default left
9514  * @cfg {String} capture (user|camera) use for file input only. (default empty)
9515  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9516
9517  * @cfg {String} align (left|center|right) Default left
9518  * @cfg {Boolean} forceFeedback (true|false) Default false
9519  * 
9520  * @constructor
9521  * Create a new Input
9522  * @param {Object} config The config object
9523  */
9524
9525 Roo.bootstrap.Input = function(config){
9526     
9527     Roo.bootstrap.Input.superclass.constructor.call(this, config);
9528     
9529     this.addEvents({
9530         /**
9531          * @event focus
9532          * Fires when this field receives input focus.
9533          * @param {Roo.form.Field} this
9534          */
9535         focus : true,
9536         /**
9537          * @event blur
9538          * Fires when this field loses input focus.
9539          * @param {Roo.form.Field} this
9540          */
9541         blur : true,
9542         /**
9543          * @event specialkey
9544          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9545          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9546          * @param {Roo.form.Field} this
9547          * @param {Roo.EventObject} e The event object
9548          */
9549         specialkey : true,
9550         /**
9551          * @event change
9552          * Fires just before the field blurs if the field value has changed.
9553          * @param {Roo.form.Field} this
9554          * @param {Mixed} newValue The new value
9555          * @param {Mixed} oldValue The original value
9556          */
9557         change : true,
9558         /**
9559          * @event invalid
9560          * Fires after the field has been marked as invalid.
9561          * @param {Roo.form.Field} this
9562          * @param {String} msg The validation message
9563          */
9564         invalid : true,
9565         /**
9566          * @event valid
9567          * Fires after the field has been validated with no errors.
9568          * @param {Roo.form.Field} this
9569          */
9570         valid : true,
9571          /**
9572          * @event keyup
9573          * Fires after the key up
9574          * @param {Roo.form.Field} this
9575          * @param {Roo.EventObject}  e The event Object
9576          */
9577         keyup : true
9578     });
9579 };
9580
9581 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9582      /**
9583      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9584       automatic validation (defaults to "keyup").
9585      */
9586     validationEvent : "keyup",
9587      /**
9588      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9589      */
9590     validateOnBlur : true,
9591     /**
9592      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9593      */
9594     validationDelay : 250,
9595      /**
9596      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9597      */
9598     focusClass : "x-form-focus",  // not needed???
9599     
9600        
9601     /**
9602      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9603      */
9604     invalidClass : "has-warning",
9605     
9606     /**
9607      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9608      */
9609     validClass : "has-success",
9610     
9611     /**
9612      * @cfg {Boolean} hasFeedback (true|false) default true
9613      */
9614     hasFeedback : true,
9615     
9616     /**
9617      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9618      */
9619     invalidFeedbackClass : "glyphicon-warning-sign",
9620     
9621     /**
9622      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9623      */
9624     validFeedbackClass : "glyphicon-ok",
9625     
9626     /**
9627      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9628      */
9629     selectOnFocus : false,
9630     
9631      /**
9632      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9633      */
9634     maskRe : null,
9635        /**
9636      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9637      */
9638     vtype : null,
9639     
9640       /**
9641      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9642      */
9643     disableKeyFilter : false,
9644     
9645        /**
9646      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9647      */
9648     disabled : false,
9649      /**
9650      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9651      */
9652     allowBlank : true,
9653     /**
9654      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9655      */
9656     blankText : "Please complete this mandatory field",
9657     
9658      /**
9659      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9660      */
9661     minLength : 0,
9662     /**
9663      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9664      */
9665     maxLength : Number.MAX_VALUE,
9666     /**
9667      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9668      */
9669     minLengthText : "The minimum length for this field is {0}",
9670     /**
9671      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9672      */
9673     maxLengthText : "The maximum length for this field is {0}",
9674   
9675     
9676     /**
9677      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9678      * If available, this function will be called only after the basic validators all return true, and will be passed the
9679      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9680      */
9681     validator : null,
9682     /**
9683      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9684      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9685      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9686      */
9687     regex : null,
9688     /**
9689      * @cfg {String} regexText -- Depricated - use Invalid Text
9690      */
9691     regexText : "",
9692     
9693     /**
9694      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9695      */
9696     invalidText : "",
9697     
9698     
9699     
9700     autocomplete: false,
9701     
9702     
9703     fieldLabel : '',
9704     inputType : 'text',
9705     
9706     name : false,
9707     placeholder: false,
9708     before : false,
9709     after : false,
9710     size : false,
9711     hasFocus : false,
9712     preventMark: false,
9713     isFormField : true,
9714     value : '',
9715     labelWidth : 2,
9716     labelAlign : false,
9717     readOnly : false,
9718     align : false,
9719     formatedValue : false,
9720     forceFeedback : false,
9721     
9722     indicatorpos : 'left',
9723     
9724     labellg : 0,
9725     labelmd : 0,
9726     labelsm : 0,
9727     labelxs : 0,
9728     
9729     capture : '',
9730     accept : '',
9731     
9732     parentLabelAlign : function()
9733     {
9734         var parent = this;
9735         while (parent.parent()) {
9736             parent = parent.parent();
9737             if (typeof(parent.labelAlign) !='undefined') {
9738                 return parent.labelAlign;
9739             }
9740         }
9741         return 'left';
9742         
9743     },
9744     
9745     getAutoCreate : function()
9746     {
9747         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9748         
9749         var id = Roo.id();
9750         
9751         var cfg = {};
9752         
9753         if(this.inputType != 'hidden'){
9754             cfg.cls = 'form-group' //input-group
9755         }
9756         
9757         var input =  {
9758             tag: 'input',
9759             id : id,
9760             type : this.inputType,
9761             value : this.value,
9762             cls : 'form-control',
9763             placeholder : this.placeholder || '',
9764             autocomplete : this.autocomplete || 'new-password'
9765         };
9766         
9767         if(this.capture.length){
9768             input.capture = this.capture;
9769         }
9770         
9771         if(this.accept.length){
9772             input.accept = this.accept + "/*";
9773         }
9774         
9775         if(this.align){
9776             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9777         }
9778         
9779         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9780             input.maxLength = this.maxLength;
9781         }
9782         
9783         if (this.disabled) {
9784             input.disabled=true;
9785         }
9786         
9787         if (this.readOnly) {
9788             input.readonly=true;
9789         }
9790         
9791         if (this.name) {
9792             input.name = this.name;
9793         }
9794         
9795         if (this.size) {
9796             input.cls += ' input-' + this.size;
9797         }
9798         
9799         var settings=this;
9800         ['xs','sm','md','lg'].map(function(size){
9801             if (settings[size]) {
9802                 cfg.cls += ' col-' + size + '-' + settings[size];
9803             }
9804         });
9805         
9806         var inputblock = input;
9807         
9808         var feedback = {
9809             tag: 'span',
9810             cls: 'glyphicon form-control-feedback'
9811         };
9812             
9813         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9814             
9815             inputblock = {
9816                 cls : 'has-feedback',
9817                 cn :  [
9818                     input,
9819                     feedback
9820                 ] 
9821             };  
9822         }
9823         
9824         if (this.before || this.after) {
9825             
9826             inputblock = {
9827                 cls : 'input-group',
9828                 cn :  [] 
9829             };
9830             
9831             if (this.before && typeof(this.before) == 'string') {
9832                 
9833                 inputblock.cn.push({
9834                     tag :'span',
9835                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9836                     html : this.before
9837                 });
9838             }
9839             if (this.before && typeof(this.before) == 'object') {
9840                 this.before = Roo.factory(this.before);
9841                 
9842                 inputblock.cn.push({
9843                     tag :'span',
9844                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9845                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9846                 });
9847             }
9848             
9849             inputblock.cn.push(input);
9850             
9851             if (this.after && typeof(this.after) == 'string') {
9852                 inputblock.cn.push({
9853                     tag :'span',
9854                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9855                     html : this.after
9856                 });
9857             }
9858             if (this.after && typeof(this.after) == 'object') {
9859                 this.after = Roo.factory(this.after);
9860                 
9861                 inputblock.cn.push({
9862                     tag :'span',
9863                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9864                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9865                 });
9866             }
9867             
9868             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9869                 inputblock.cls += ' has-feedback';
9870                 inputblock.cn.push(feedback);
9871             }
9872         };
9873         var indicator = {
9874             tag : 'i',
9875             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9876             tooltip : 'This field is required'
9877         };
9878         if (Roo.bootstrap.version == 4) {
9879             indicator = {
9880                 tag : 'i',
9881                 style : 'display-none'
9882             };
9883         }
9884         if (align ==='left' && this.fieldLabel.length) {
9885             
9886             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9887             
9888             cfg.cn = [
9889                 indicator,
9890                 {
9891                     tag: 'label',
9892                     'for' :  id,
9893                     cls : 'control-label col-form-label',
9894                     html : this.fieldLabel
9895
9896                 },
9897                 {
9898                     cls : "", 
9899                     cn: [
9900                         inputblock
9901                     ]
9902                 }
9903             ];
9904             
9905             var labelCfg = cfg.cn[1];
9906             var contentCfg = cfg.cn[2];
9907             
9908             if(this.indicatorpos == 'right'){
9909                 cfg.cn = [
9910                     {
9911                         tag: 'label',
9912                         'for' :  id,
9913                         cls : 'control-label col-form-label',
9914                         cn : [
9915                             {
9916                                 tag : 'span',
9917                                 html : this.fieldLabel
9918                             },
9919                             indicator
9920                         ]
9921                     },
9922                     {
9923                         cls : "",
9924                         cn: [
9925                             inputblock
9926                         ]
9927                     }
9928
9929                 ];
9930                 
9931                 labelCfg = cfg.cn[0];
9932                 contentCfg = cfg.cn[1];
9933             
9934             }
9935             
9936             if(this.labelWidth > 12){
9937                 labelCfg.style = "width: " + this.labelWidth + 'px';
9938             }
9939             
9940             if(this.labelWidth < 13 && this.labelmd == 0){
9941                 this.labelmd = this.labelWidth;
9942             }
9943             
9944             if(this.labellg > 0){
9945                 labelCfg.cls += ' col-lg-' + this.labellg;
9946                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9947             }
9948             
9949             if(this.labelmd > 0){
9950                 labelCfg.cls += ' col-md-' + this.labelmd;
9951                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9952             }
9953             
9954             if(this.labelsm > 0){
9955                 labelCfg.cls += ' col-sm-' + this.labelsm;
9956                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9957             }
9958             
9959             if(this.labelxs > 0){
9960                 labelCfg.cls += ' col-xs-' + this.labelxs;
9961                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9962             }
9963             
9964             
9965         } else if ( this.fieldLabel.length) {
9966                 
9967             cfg.cn = [
9968                 {
9969                     tag : 'i',
9970                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9971                     tooltip : 'This field is required'
9972                 },
9973                 {
9974                     tag: 'label',
9975                    //cls : 'input-group-addon',
9976                     html : this.fieldLabel
9977
9978                 },
9979
9980                inputblock
9981
9982            ];
9983            
9984            if(this.indicatorpos == 'right'){
9985                 
9986                 cfg.cn = [
9987                     {
9988                         tag: 'label',
9989                        //cls : 'input-group-addon',
9990                         html : this.fieldLabel
9991
9992                     },
9993                     {
9994                         tag : 'i',
9995                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9996                         tooltip : 'This field is required'
9997                     },
9998
9999                    inputblock
10000
10001                ];
10002
10003             }
10004
10005         } else {
10006             
10007             cfg.cn = [
10008
10009                     inputblock
10010
10011             ];
10012                 
10013                 
10014         };
10015         
10016         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10017            cfg.cls += ' navbar-form';
10018         }
10019         
10020         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10021             // on BS4 we do this only if not form 
10022             cfg.cls += ' navbar-form';
10023             cfg.tag = 'li';
10024         }
10025         
10026         return cfg;
10027         
10028     },
10029     /**
10030      * return the real input element.
10031      */
10032     inputEl: function ()
10033     {
10034         return this.el.select('input.form-control',true).first();
10035     },
10036     
10037     tooltipEl : function()
10038     {
10039         return this.inputEl();
10040     },
10041     
10042     indicatorEl : function()
10043     {
10044         if (Roo.bootstrap.version == 4) {
10045             return false; // not enabled in v4 yet.
10046         }
10047         
10048         var indicator = this.el.select('i.roo-required-indicator',true).first();
10049         
10050         if(!indicator){
10051             return false;
10052         }
10053         
10054         return indicator;
10055         
10056     },
10057     
10058     setDisabled : function(v)
10059     {
10060         var i  = this.inputEl().dom;
10061         if (!v) {
10062             i.removeAttribute('disabled');
10063             return;
10064             
10065         }
10066         i.setAttribute('disabled','true');
10067     },
10068     initEvents : function()
10069     {
10070           
10071         this.inputEl().on("keydown" , this.fireKey,  this);
10072         this.inputEl().on("focus", this.onFocus,  this);
10073         this.inputEl().on("blur", this.onBlur,  this);
10074         
10075         this.inputEl().relayEvent('keyup', this);
10076         
10077         this.indicator = this.indicatorEl();
10078         
10079         if(this.indicator){
10080             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
10081         }
10082  
10083         // reference to original value for reset
10084         this.originalValue = this.getValue();
10085         //Roo.form.TextField.superclass.initEvents.call(this);
10086         if(this.validationEvent == 'keyup'){
10087             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10088             this.inputEl().on('keyup', this.filterValidation, this);
10089         }
10090         else if(this.validationEvent !== false){
10091             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10092         }
10093         
10094         if(this.selectOnFocus){
10095             this.on("focus", this.preFocus, this);
10096             
10097         }
10098         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10099             this.inputEl().on("keypress", this.filterKeys, this);
10100         } else {
10101             this.inputEl().relayEvent('keypress', this);
10102         }
10103        /* if(this.grow){
10104             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
10105             this.el.on("click", this.autoSize,  this);
10106         }
10107         */
10108         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10109             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10110         }
10111         
10112         if (typeof(this.before) == 'object') {
10113             this.before.render(this.el.select('.roo-input-before',true).first());
10114         }
10115         if (typeof(this.after) == 'object') {
10116             this.after.render(this.el.select('.roo-input-after',true).first());
10117         }
10118         
10119         this.inputEl().on('change', this.onChange, this);
10120         
10121     },
10122     filterValidation : function(e){
10123         if(!e.isNavKeyPress()){
10124             this.validationTask.delay(this.validationDelay);
10125         }
10126     },
10127      /**
10128      * Validates the field value
10129      * @return {Boolean} True if the value is valid, else false
10130      */
10131     validate : function(){
10132         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10133         if(this.disabled || this.validateValue(this.getRawValue())){
10134             this.markValid();
10135             return true;
10136         }
10137         
10138         this.markInvalid();
10139         return false;
10140     },
10141     
10142     
10143     /**
10144      * Validates a value according to the field's validation rules and marks the field as invalid
10145      * if the validation fails
10146      * @param {Mixed} value The value to validate
10147      * @return {Boolean} True if the value is valid, else false
10148      */
10149     validateValue : function(value)
10150     {
10151         if(this.getVisibilityEl().hasClass('hidden')){
10152             return true;
10153         }
10154         
10155         if(value.length < 1)  { // if it's blank
10156             if(this.allowBlank){
10157                 return true;
10158             }
10159             return false;
10160         }
10161         
10162         if(value.length < this.minLength){
10163             return false;
10164         }
10165         if(value.length > this.maxLength){
10166             return false;
10167         }
10168         if(this.vtype){
10169             var vt = Roo.form.VTypes;
10170             if(!vt[this.vtype](value, this)){
10171                 return false;
10172             }
10173         }
10174         if(typeof this.validator == "function"){
10175             var msg = this.validator(value);
10176             if(msg !== true){
10177                 return false;
10178             }
10179             if (typeof(msg) == 'string') {
10180                 this.invalidText = msg;
10181             }
10182         }
10183         
10184         if(this.regex && !this.regex.test(value)){
10185             return false;
10186         }
10187         
10188         return true;
10189     },
10190     
10191      // private
10192     fireKey : function(e){
10193         //Roo.log('field ' + e.getKey());
10194         if(e.isNavKeyPress()){
10195             this.fireEvent("specialkey", this, e);
10196         }
10197     },
10198     focus : function (selectText){
10199         if(this.rendered){
10200             this.inputEl().focus();
10201             if(selectText === true){
10202                 this.inputEl().dom.select();
10203             }
10204         }
10205         return this;
10206     } ,
10207     
10208     onFocus : function(){
10209         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10210            // this.el.addClass(this.focusClass);
10211         }
10212         if(!this.hasFocus){
10213             this.hasFocus = true;
10214             this.startValue = this.getValue();
10215             this.fireEvent("focus", this);
10216         }
10217     },
10218     
10219     beforeBlur : Roo.emptyFn,
10220
10221     
10222     // private
10223     onBlur : function(){
10224         this.beforeBlur();
10225         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10226             //this.el.removeClass(this.focusClass);
10227         }
10228         this.hasFocus = false;
10229         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10230             this.validate();
10231         }
10232         var v = this.getValue();
10233         if(String(v) !== String(this.startValue)){
10234             this.fireEvent('change', this, v, this.startValue);
10235         }
10236         this.fireEvent("blur", this);
10237     },
10238     
10239     onChange : function(e)
10240     {
10241         var v = this.getValue();
10242         if(String(v) !== String(this.startValue)){
10243             this.fireEvent('change', this, v, this.startValue);
10244         }
10245         
10246     },
10247     
10248     /**
10249      * Resets the current field value to the originally loaded value and clears any validation messages
10250      */
10251     reset : function(){
10252         this.setValue(this.originalValue);
10253         this.validate();
10254     },
10255      /**
10256      * Returns the name of the field
10257      * @return {Mixed} name The name field
10258      */
10259     getName: function(){
10260         return this.name;
10261     },
10262      /**
10263      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
10264      * @return {Mixed} value The field value
10265      */
10266     getValue : function(){
10267         
10268         var v = this.inputEl().getValue();
10269         
10270         return v;
10271     },
10272     /**
10273      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
10274      * @return {Mixed} value The field value
10275      */
10276     getRawValue : function(){
10277         var v = this.inputEl().getValue();
10278         
10279         return v;
10280     },
10281     
10282     /**
10283      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
10284      * @param {Mixed} value The value to set
10285      */
10286     setRawValue : function(v){
10287         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10288     },
10289     
10290     selectText : function(start, end){
10291         var v = this.getRawValue();
10292         if(v.length > 0){
10293             start = start === undefined ? 0 : start;
10294             end = end === undefined ? v.length : end;
10295             var d = this.inputEl().dom;
10296             if(d.setSelectionRange){
10297                 d.setSelectionRange(start, end);
10298             }else if(d.createTextRange){
10299                 var range = d.createTextRange();
10300                 range.moveStart("character", start);
10301                 range.moveEnd("character", v.length-end);
10302                 range.select();
10303             }
10304         }
10305     },
10306     
10307     /**
10308      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
10309      * @param {Mixed} value The value to set
10310      */
10311     setValue : function(v){
10312         this.value = v;
10313         if(this.rendered){
10314             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10315             this.validate();
10316         }
10317     },
10318     
10319     /*
10320     processValue : function(value){
10321         if(this.stripCharsRe){
10322             var newValue = value.replace(this.stripCharsRe, '');
10323             if(newValue !== value){
10324                 this.setRawValue(newValue);
10325                 return newValue;
10326             }
10327         }
10328         return value;
10329     },
10330   */
10331     preFocus : function(){
10332         
10333         if(this.selectOnFocus){
10334             this.inputEl().dom.select();
10335         }
10336     },
10337     filterKeys : function(e){
10338         var k = e.getKey();
10339         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
10340             return;
10341         }
10342         var c = e.getCharCode(), cc = String.fromCharCode(c);
10343         if(Roo.isIE && (e.isSpecialKey() || !cc)){
10344             return;
10345         }
10346         if(!this.maskRe.test(cc)){
10347             e.stopEvent();
10348         }
10349     },
10350      /**
10351      * Clear any invalid styles/messages for this field
10352      */
10353     clearInvalid : function(){
10354         
10355         if(!this.el || this.preventMark){ // not rendered
10356             return;
10357         }
10358         
10359         
10360         this.el.removeClass([this.invalidClass, 'is-invalid']);
10361         
10362         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10363             
10364             var feedback = this.el.select('.form-control-feedback', true).first();
10365             
10366             if(feedback){
10367                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10368             }
10369             
10370         }
10371         
10372         if(this.indicator){
10373             this.indicator.removeClass('visible');
10374             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10375         }
10376         
10377         this.fireEvent('valid', this);
10378     },
10379     
10380      /**
10381      * Mark this field as valid
10382      */
10383     markValid : function()
10384     {
10385         if(!this.el  || this.preventMark){ // not rendered...
10386             return;
10387         }
10388         
10389         this.el.removeClass([this.invalidClass, this.validClass]);
10390         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10391
10392         var feedback = this.el.select('.form-control-feedback', true).first();
10393             
10394         if(feedback){
10395             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10396         }
10397         
10398         if(this.indicator){
10399             this.indicator.removeClass('visible');
10400             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10401         }
10402         
10403         if(this.disabled){
10404             return;
10405         }
10406         
10407         if(this.allowBlank && !this.getRawValue().length){
10408             return;
10409         }
10410         if (Roo.bootstrap.version == 3) {
10411             this.el.addClass(this.validClass);
10412         } else {
10413             this.inputEl().addClass('is-valid');
10414         }
10415
10416         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10417             
10418             var feedback = this.el.select('.form-control-feedback', true).first();
10419             
10420             if(feedback){
10421                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10422                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10423             }
10424             
10425         }
10426         
10427         this.fireEvent('valid', this);
10428     },
10429     
10430      /**
10431      * Mark this field as invalid
10432      * @param {String} msg The validation message
10433      */
10434     markInvalid : function(msg)
10435     {
10436         if(!this.el  || this.preventMark){ // not rendered
10437             return;
10438         }
10439         
10440         this.el.removeClass([this.invalidClass, this.validClass]);
10441         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10442         
10443         var feedback = this.el.select('.form-control-feedback', true).first();
10444             
10445         if(feedback){
10446             this.el.select('.form-control-feedback', true).first().removeClass(
10447                     [this.invalidFeedbackClass, this.validFeedbackClass]);
10448         }
10449
10450         if(this.disabled){
10451             return;
10452         }
10453         
10454         if(this.allowBlank && !this.getRawValue().length){
10455             return;
10456         }
10457         
10458         if(this.indicator){
10459             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10460             this.indicator.addClass('visible');
10461         }
10462         if (Roo.bootstrap.version == 3) {
10463             this.el.addClass(this.invalidClass);
10464         } else {
10465             this.inputEl().addClass('is-invalid');
10466         }
10467         
10468         
10469         
10470         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10471             
10472             var feedback = this.el.select('.form-control-feedback', true).first();
10473             
10474             if(feedback){
10475                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10476                 
10477                 if(this.getValue().length || this.forceFeedback){
10478                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10479                 }
10480                 
10481             }
10482             
10483         }
10484         
10485         this.fireEvent('invalid', this, msg);
10486     },
10487     // private
10488     SafariOnKeyDown : function(event)
10489     {
10490         // this is a workaround for a password hang bug on chrome/ webkit.
10491         if (this.inputEl().dom.type != 'password') {
10492             return;
10493         }
10494         
10495         var isSelectAll = false;
10496         
10497         if(this.inputEl().dom.selectionEnd > 0){
10498             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
10499         }
10500         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
10501             event.preventDefault();
10502             this.setValue('');
10503             return;
10504         }
10505         
10506         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10507             
10508             event.preventDefault();
10509             // this is very hacky as keydown always get's upper case.
10510             //
10511             var cc = String.fromCharCode(event.getCharCode());
10512             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
10513             
10514         }
10515     },
10516     adjustWidth : function(tag, w){
10517         tag = tag.toLowerCase();
10518         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10519             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10520                 if(tag == 'input'){
10521                     return w + 2;
10522                 }
10523                 if(tag == 'textarea'){
10524                     return w-2;
10525                 }
10526             }else if(Roo.isOpera){
10527                 if(tag == 'input'){
10528                     return w + 2;
10529                 }
10530                 if(tag == 'textarea'){
10531                     return w-2;
10532                 }
10533             }
10534         }
10535         return w;
10536     },
10537     
10538     setFieldLabel : function(v)
10539     {
10540         if(!this.rendered){
10541             return;
10542         }
10543         
10544         if(this.indicatorEl()){
10545             var ar = this.el.select('label > span',true);
10546             
10547             if (ar.elements.length) {
10548                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10549                 this.fieldLabel = v;
10550                 return;
10551             }
10552             
10553             var br = this.el.select('label',true);
10554             
10555             if(br.elements.length) {
10556                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10557                 this.fieldLabel = v;
10558                 return;
10559             }
10560             
10561             Roo.log('Cannot Found any of label > span || label in input');
10562             return;
10563         }
10564         
10565         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10566         this.fieldLabel = v;
10567         
10568         
10569     }
10570 });
10571
10572  
10573 /*
10574  * - LGPL
10575  *
10576  * Input
10577  * 
10578  */
10579
10580 /**
10581  * @class Roo.bootstrap.TextArea
10582  * @extends Roo.bootstrap.Input
10583  * Bootstrap TextArea class
10584  * @cfg {Number} cols Specifies the visible width of a text area
10585  * @cfg {Number} rows Specifies the visible number of lines in a text area
10586  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10587  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10588  * @cfg {string} html text
10589  * 
10590  * @constructor
10591  * Create a new TextArea
10592  * @param {Object} config The config object
10593  */
10594
10595 Roo.bootstrap.TextArea = function(config){
10596     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10597    
10598 };
10599
10600 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10601      
10602     cols : false,
10603     rows : 5,
10604     readOnly : false,
10605     warp : 'soft',
10606     resize : false,
10607     value: false,
10608     html: false,
10609     
10610     getAutoCreate : function(){
10611         
10612         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10613         
10614         var id = Roo.id();
10615         
10616         var cfg = {};
10617         
10618         if(this.inputType != 'hidden'){
10619             cfg.cls = 'form-group' //input-group
10620         }
10621         
10622         var input =  {
10623             tag: 'textarea',
10624             id : id,
10625             warp : this.warp,
10626             rows : this.rows,
10627             value : this.value || '',
10628             html: this.html || '',
10629             cls : 'form-control',
10630             placeholder : this.placeholder || '' 
10631             
10632         };
10633         
10634         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10635             input.maxLength = this.maxLength;
10636         }
10637         
10638         if(this.resize){
10639             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10640         }
10641         
10642         if(this.cols){
10643             input.cols = this.cols;
10644         }
10645         
10646         if (this.readOnly) {
10647             input.readonly = true;
10648         }
10649         
10650         if (this.name) {
10651             input.name = this.name;
10652         }
10653         
10654         if (this.size) {
10655             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10656         }
10657         
10658         var settings=this;
10659         ['xs','sm','md','lg'].map(function(size){
10660             if (settings[size]) {
10661                 cfg.cls += ' col-' + size + '-' + settings[size];
10662             }
10663         });
10664         
10665         var inputblock = input;
10666         
10667         if(this.hasFeedback && !this.allowBlank){
10668             
10669             var feedback = {
10670                 tag: 'span',
10671                 cls: 'glyphicon form-control-feedback'
10672             };
10673
10674             inputblock = {
10675                 cls : 'has-feedback',
10676                 cn :  [
10677                     input,
10678                     feedback
10679                 ] 
10680             };  
10681         }
10682         
10683         
10684         if (this.before || this.after) {
10685             
10686             inputblock = {
10687                 cls : 'input-group',
10688                 cn :  [] 
10689             };
10690             if (this.before) {
10691                 inputblock.cn.push({
10692                     tag :'span',
10693                     cls : 'input-group-addon',
10694                     html : this.before
10695                 });
10696             }
10697             
10698             inputblock.cn.push(input);
10699             
10700             if(this.hasFeedback && !this.allowBlank){
10701                 inputblock.cls += ' has-feedback';
10702                 inputblock.cn.push(feedback);
10703             }
10704             
10705             if (this.after) {
10706                 inputblock.cn.push({
10707                     tag :'span',
10708                     cls : 'input-group-addon',
10709                     html : this.after
10710                 });
10711             }
10712             
10713         }
10714         
10715         if (align ==='left' && this.fieldLabel.length) {
10716             cfg.cn = [
10717                 {
10718                     tag: 'label',
10719                     'for' :  id,
10720                     cls : 'control-label',
10721                     html : this.fieldLabel
10722                 },
10723                 {
10724                     cls : "",
10725                     cn: [
10726                         inputblock
10727                     ]
10728                 }
10729
10730             ];
10731             
10732             if(this.labelWidth > 12){
10733                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10734             }
10735
10736             if(this.labelWidth < 13 && this.labelmd == 0){
10737                 this.labelmd = this.labelWidth;
10738             }
10739
10740             if(this.labellg > 0){
10741                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10742                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10743             }
10744
10745             if(this.labelmd > 0){
10746                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10747                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10748             }
10749
10750             if(this.labelsm > 0){
10751                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10752                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10753             }
10754
10755             if(this.labelxs > 0){
10756                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10757                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10758             }
10759             
10760         } else if ( this.fieldLabel.length) {
10761             cfg.cn = [
10762
10763                {
10764                    tag: 'label',
10765                    //cls : 'input-group-addon',
10766                    html : this.fieldLabel
10767
10768                },
10769
10770                inputblock
10771
10772            ];
10773
10774         } else {
10775
10776             cfg.cn = [
10777
10778                 inputblock
10779
10780             ];
10781                 
10782         }
10783         
10784         if (this.disabled) {
10785             input.disabled=true;
10786         }
10787         
10788         return cfg;
10789         
10790     },
10791     /**
10792      * return the real textarea element.
10793      */
10794     inputEl: function ()
10795     {
10796         return this.el.select('textarea.form-control',true).first();
10797     },
10798     
10799     /**
10800      * Clear any invalid styles/messages for this field
10801      */
10802     clearInvalid : function()
10803     {
10804         
10805         if(!this.el || this.preventMark){ // not rendered
10806             return;
10807         }
10808         
10809         var label = this.el.select('label', true).first();
10810         var icon = this.el.select('i.fa-star', true).first();
10811         
10812         if(label && icon){
10813             icon.remove();
10814         }
10815         this.el.removeClass( this.validClass);
10816         this.inputEl().removeClass('is-invalid');
10817          
10818         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10819             
10820             var feedback = this.el.select('.form-control-feedback', true).first();
10821             
10822             if(feedback){
10823                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10824             }
10825             
10826         }
10827         
10828         this.fireEvent('valid', this);
10829     },
10830     
10831      /**
10832      * Mark this field as valid
10833      */
10834     markValid : function()
10835     {
10836         if(!this.el  || this.preventMark){ // not rendered
10837             return;
10838         }
10839         
10840         this.el.removeClass([this.invalidClass, this.validClass]);
10841         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10842         
10843         var feedback = this.el.select('.form-control-feedback', true).first();
10844             
10845         if(feedback){
10846             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10847         }
10848
10849         if(this.disabled || this.allowBlank){
10850             return;
10851         }
10852         
10853         var label = this.el.select('label', true).first();
10854         var icon = this.el.select('i.fa-star', true).first();
10855         
10856         if(label && icon){
10857             icon.remove();
10858         }
10859         if (Roo.bootstrap.version == 3) {
10860             this.el.addClass(this.validClass);
10861         } else {
10862             this.inputEl().addClass('is-valid');
10863         }
10864         
10865         
10866         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10867             
10868             var feedback = this.el.select('.form-control-feedback', true).first();
10869             
10870             if(feedback){
10871                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10872                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10873             }
10874             
10875         }
10876         
10877         this.fireEvent('valid', this);
10878     },
10879     
10880      /**
10881      * Mark this field as invalid
10882      * @param {String} msg The validation message
10883      */
10884     markInvalid : function(msg)
10885     {
10886         if(!this.el  || this.preventMark){ // not rendered
10887             return;
10888         }
10889         
10890         this.el.removeClass([this.invalidClass, this.validClass]);
10891         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10892         
10893         var feedback = this.el.select('.form-control-feedback', true).first();
10894             
10895         if(feedback){
10896             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10897         }
10898
10899         if(this.disabled || this.allowBlank){
10900             return;
10901         }
10902         
10903         var label = this.el.select('label', true).first();
10904         var icon = this.el.select('i.fa-star', true).first();
10905         
10906         if(!this.getValue().length && label && !icon){
10907             this.el.createChild({
10908                 tag : 'i',
10909                 cls : 'text-danger fa fa-lg fa-star',
10910                 tooltip : 'This field is required',
10911                 style : 'margin-right:5px;'
10912             }, label, true);
10913         }
10914         
10915         if (Roo.bootstrap.version == 3) {
10916             this.el.addClass(this.invalidClass);
10917         } else {
10918             this.inputEl().addClass('is-invalid');
10919         }
10920         
10921         // fixme ... this may be depricated need to test..
10922         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10923             
10924             var feedback = this.el.select('.form-control-feedback', true).first();
10925             
10926             if(feedback){
10927                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10928                 
10929                 if(this.getValue().length || this.forceFeedback){
10930                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10931                 }
10932                 
10933             }
10934             
10935         }
10936         
10937         this.fireEvent('invalid', this, msg);
10938     }
10939 });
10940
10941  
10942 /*
10943  * - LGPL
10944  *
10945  * trigger field - base class for combo..
10946  * 
10947  */
10948  
10949 /**
10950  * @class Roo.bootstrap.TriggerField
10951  * @extends Roo.bootstrap.Input
10952  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10953  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10954  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10955  * for which you can provide a custom implementation.  For example:
10956  * <pre><code>
10957 var trigger = new Roo.bootstrap.TriggerField();
10958 trigger.onTriggerClick = myTriggerFn;
10959 trigger.applyTo('my-field');
10960 </code></pre>
10961  *
10962  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10963  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10964  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10965  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10966  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
10967
10968  * @constructor
10969  * Create a new TriggerField.
10970  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10971  * to the base TextField)
10972  */
10973 Roo.bootstrap.TriggerField = function(config){
10974     this.mimicing = false;
10975     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10976 };
10977
10978 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10979     /**
10980      * @cfg {String} triggerClass A CSS class to apply to the trigger
10981      */
10982      /**
10983      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10984      */
10985     hideTrigger:false,
10986
10987     /**
10988      * @cfg {Boolean} removable (true|false) special filter default false
10989      */
10990     removable : false,
10991     
10992     /** @cfg {Boolean} grow @hide */
10993     /** @cfg {Number} growMin @hide */
10994     /** @cfg {Number} growMax @hide */
10995
10996     /**
10997      * @hide 
10998      * @method
10999      */
11000     autoSize: Roo.emptyFn,
11001     // private
11002     monitorTab : true,
11003     // private
11004     deferHeight : true,
11005
11006     
11007     actionMode : 'wrap',
11008     
11009     caret : false,
11010     
11011     
11012     getAutoCreate : function(){
11013        
11014         var align = this.labelAlign || this.parentLabelAlign();
11015         
11016         var id = Roo.id();
11017         
11018         var cfg = {
11019             cls: 'form-group' //input-group
11020         };
11021         
11022         
11023         var input =  {
11024             tag: 'input',
11025             id : id,
11026             type : this.inputType,
11027             cls : 'form-control',
11028             autocomplete: 'new-password',
11029             placeholder : this.placeholder || '' 
11030             
11031         };
11032         if (this.name) {
11033             input.name = this.name;
11034         }
11035         if (this.size) {
11036             input.cls += ' input-' + this.size;
11037         }
11038         
11039         if (this.disabled) {
11040             input.disabled=true;
11041         }
11042         
11043         var inputblock = input;
11044         
11045         if(this.hasFeedback && !this.allowBlank){
11046             
11047             var feedback = {
11048                 tag: 'span',
11049                 cls: 'glyphicon form-control-feedback'
11050             };
11051             
11052             if(this.removable && !this.editable && !this.tickable){
11053                 inputblock = {
11054                     cls : 'has-feedback',
11055                     cn :  [
11056                         inputblock,
11057                         {
11058                             tag: 'button',
11059                             html : 'x',
11060                             cls : 'roo-combo-removable-btn close'
11061                         },
11062                         feedback
11063                     ] 
11064                 };
11065             } else {
11066                 inputblock = {
11067                     cls : 'has-feedback',
11068                     cn :  [
11069                         inputblock,
11070                         feedback
11071                     ] 
11072                 };
11073             }
11074
11075         } else {
11076             if(this.removable && !this.editable && !this.tickable){
11077                 inputblock = {
11078                     cls : 'roo-removable',
11079                     cn :  [
11080                         inputblock,
11081                         {
11082                             tag: 'button',
11083                             html : 'x',
11084                             cls : 'roo-combo-removable-btn close'
11085                         }
11086                     ] 
11087                 };
11088             }
11089         }
11090         
11091         if (this.before || this.after) {
11092             
11093             inputblock = {
11094                 cls : 'input-group',
11095                 cn :  [] 
11096             };
11097             if (this.before) {
11098                 inputblock.cn.push({
11099                     tag :'span',
11100                     cls : 'input-group-addon input-group-prepend input-group-text',
11101                     html : this.before
11102                 });
11103             }
11104             
11105             inputblock.cn.push(input);
11106             
11107             if(this.hasFeedback && !this.allowBlank){
11108                 inputblock.cls += ' has-feedback';
11109                 inputblock.cn.push(feedback);
11110             }
11111             
11112             if (this.after) {
11113                 inputblock.cn.push({
11114                     tag :'span',
11115                     cls : 'input-group-addon input-group-append input-group-text',
11116                     html : this.after
11117                 });
11118             }
11119             
11120         };
11121         
11122       
11123         
11124         var ibwrap = inputblock;
11125         
11126         if(this.multiple){
11127             ibwrap = {
11128                 tag: 'ul',
11129                 cls: 'roo-select2-choices',
11130                 cn:[
11131                     {
11132                         tag: 'li',
11133                         cls: 'roo-select2-search-field',
11134                         cn: [
11135
11136                             inputblock
11137                         ]
11138                     }
11139                 ]
11140             };
11141                 
11142         }
11143         
11144         var combobox = {
11145             cls: 'roo-select2-container input-group',
11146             cn: [
11147                  {
11148                     tag: 'input',
11149                     type : 'hidden',
11150                     cls: 'form-hidden-field'
11151                 },
11152                 ibwrap
11153             ]
11154         };
11155         
11156         if(!this.multiple && this.showToggleBtn){
11157             
11158             var caret = {
11159                         tag: 'span',
11160                         cls: 'caret'
11161              };
11162             if (this.caret != false) {
11163                 caret = {
11164                      tag: 'i',
11165                      cls: 'fa fa-' + this.caret
11166                 };
11167                 
11168             }
11169             
11170             combobox.cn.push({
11171                 tag :'span',
11172                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11173                 cn : [
11174                     Roo.bootstrap.version == 3 ? caret : '',
11175                     {
11176                         tag: 'span',
11177                         cls: 'combobox-clear',
11178                         cn  : [
11179                             {
11180                                 tag : 'i',
11181                                 cls: 'icon-remove'
11182                             }
11183                         ]
11184                     }
11185                 ]
11186
11187             })
11188         }
11189         
11190         if(this.multiple){
11191             combobox.cls += ' roo-select2-container-multi';
11192         }
11193          var indicator = {
11194             tag : 'i',
11195             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11196             tooltip : 'This field is required'
11197         };
11198         if (Roo.bootstrap.version == 4) {
11199             indicator = {
11200                 tag : 'i',
11201                 style : 'display:none'
11202             };
11203         }
11204         
11205         
11206         if (align ==='left' && this.fieldLabel.length) {
11207             
11208             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11209
11210             cfg.cn = [
11211                 indicator,
11212                 {
11213                     tag: 'label',
11214                     'for' :  id,
11215                     cls : 'control-label',
11216                     html : this.fieldLabel
11217
11218                 },
11219                 {
11220                     cls : "", 
11221                     cn: [
11222                         combobox
11223                     ]
11224                 }
11225
11226             ];
11227             
11228             var labelCfg = cfg.cn[1];
11229             var contentCfg = cfg.cn[2];
11230             
11231             if(this.indicatorpos == 'right'){
11232                 cfg.cn = [
11233                     {
11234                         tag: 'label',
11235                         'for' :  id,
11236                         cls : 'control-label',
11237                         cn : [
11238                             {
11239                                 tag : 'span',
11240                                 html : this.fieldLabel
11241                             },
11242                             indicator
11243                         ]
11244                     },
11245                     {
11246                         cls : "", 
11247                         cn: [
11248                             combobox
11249                         ]
11250                     }
11251
11252                 ];
11253                 
11254                 labelCfg = cfg.cn[0];
11255                 contentCfg = cfg.cn[1];
11256             }
11257             
11258             if(this.labelWidth > 12){
11259                 labelCfg.style = "width: " + this.labelWidth + 'px';
11260             }
11261             
11262             if(this.labelWidth < 13 && this.labelmd == 0){
11263                 this.labelmd = this.labelWidth;
11264             }
11265             
11266             if(this.labellg > 0){
11267                 labelCfg.cls += ' col-lg-' + this.labellg;
11268                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11269             }
11270             
11271             if(this.labelmd > 0){
11272                 labelCfg.cls += ' col-md-' + this.labelmd;
11273                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11274             }
11275             
11276             if(this.labelsm > 0){
11277                 labelCfg.cls += ' col-sm-' + this.labelsm;
11278                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11279             }
11280             
11281             if(this.labelxs > 0){
11282                 labelCfg.cls += ' col-xs-' + this.labelxs;
11283                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11284             }
11285             
11286         } else if ( this.fieldLabel.length) {
11287 //                Roo.log(" label");
11288             cfg.cn = [
11289                 indicator,
11290                {
11291                    tag: 'label',
11292                    //cls : 'input-group-addon',
11293                    html : this.fieldLabel
11294
11295                },
11296
11297                combobox
11298
11299             ];
11300             
11301             if(this.indicatorpos == 'right'){
11302                 
11303                 cfg.cn = [
11304                     {
11305                        tag: 'label',
11306                        cn : [
11307                            {
11308                                tag : 'span',
11309                                html : this.fieldLabel
11310                            },
11311                            indicator
11312                        ]
11313
11314                     },
11315                     combobox
11316
11317                 ];
11318
11319             }
11320
11321         } else {
11322             
11323 //                Roo.log(" no label && no align");
11324                 cfg = combobox
11325                      
11326                 
11327         }
11328         
11329         var settings=this;
11330         ['xs','sm','md','lg'].map(function(size){
11331             if (settings[size]) {
11332                 cfg.cls += ' col-' + size + '-' + settings[size];
11333             }
11334         });
11335         
11336         return cfg;
11337         
11338     },
11339     
11340     
11341     
11342     // private
11343     onResize : function(w, h){
11344 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
11345 //        if(typeof w == 'number'){
11346 //            var x = w - this.trigger.getWidth();
11347 //            this.inputEl().setWidth(this.adjustWidth('input', x));
11348 //            this.trigger.setStyle('left', x+'px');
11349 //        }
11350     },
11351
11352     // private
11353     adjustSize : Roo.BoxComponent.prototype.adjustSize,
11354
11355     // private
11356     getResizeEl : function(){
11357         return this.inputEl();
11358     },
11359
11360     // private
11361     getPositionEl : function(){
11362         return this.inputEl();
11363     },
11364
11365     // private
11366     alignErrorIcon : function(){
11367         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
11368     },
11369
11370     // private
11371     initEvents : function(){
11372         
11373         this.createList();
11374         
11375         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
11376         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
11377         if(!this.multiple && this.showToggleBtn){
11378             this.trigger = this.el.select('span.dropdown-toggle',true).first();
11379             if(this.hideTrigger){
11380                 this.trigger.setDisplayed(false);
11381             }
11382             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
11383         }
11384         
11385         if(this.multiple){
11386             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
11387         }
11388         
11389         if(this.removable && !this.editable && !this.tickable){
11390             var close = this.closeTriggerEl();
11391             
11392             if(close){
11393                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
11394                 close.on('click', this.removeBtnClick, this, close);
11395             }
11396         }
11397         
11398         //this.trigger.addClassOnOver('x-form-trigger-over');
11399         //this.trigger.addClassOnClick('x-form-trigger-click');
11400         
11401         //if(!this.width){
11402         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
11403         //}
11404     },
11405     
11406     closeTriggerEl : function()
11407     {
11408         var close = this.el.select('.roo-combo-removable-btn', true).first();
11409         return close ? close : false;
11410     },
11411     
11412     removeBtnClick : function(e, h, el)
11413     {
11414         e.preventDefault();
11415         
11416         if(this.fireEvent("remove", this) !== false){
11417             this.reset();
11418             this.fireEvent("afterremove", this)
11419         }
11420     },
11421     
11422     createList : function()
11423     {
11424         this.list = Roo.get(document.body).createChild({
11425             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
11426             cls: 'typeahead typeahead-long dropdown-menu',
11427             style: 'display:none'
11428         });
11429         
11430         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
11431         
11432     },
11433
11434     // private
11435     initTrigger : function(){
11436        
11437     },
11438
11439     // private
11440     onDestroy : function(){
11441         if(this.trigger){
11442             this.trigger.removeAllListeners();
11443           //  this.trigger.remove();
11444         }
11445         //if(this.wrap){
11446         //    this.wrap.remove();
11447         //}
11448         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
11449     },
11450
11451     // private
11452     onFocus : function(){
11453         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
11454         /*
11455         if(!this.mimicing){
11456             this.wrap.addClass('x-trigger-wrap-focus');
11457             this.mimicing = true;
11458             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
11459             if(this.monitorTab){
11460                 this.el.on("keydown", this.checkTab, this);
11461             }
11462         }
11463         */
11464     },
11465
11466     // private
11467     checkTab : function(e){
11468         if(e.getKey() == e.TAB){
11469             this.triggerBlur();
11470         }
11471     },
11472
11473     // private
11474     onBlur : function(){
11475         // do nothing
11476     },
11477
11478     // private
11479     mimicBlur : function(e, t){
11480         /*
11481         if(!this.wrap.contains(t) && this.validateBlur()){
11482             this.triggerBlur();
11483         }
11484         */
11485     },
11486
11487     // private
11488     triggerBlur : function(){
11489         this.mimicing = false;
11490         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
11491         if(this.monitorTab){
11492             this.el.un("keydown", this.checkTab, this);
11493         }
11494         //this.wrap.removeClass('x-trigger-wrap-focus');
11495         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
11496     },
11497
11498     // private
11499     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
11500     validateBlur : function(e, t){
11501         return true;
11502     },
11503
11504     // private
11505     onDisable : function(){
11506         this.inputEl().dom.disabled = true;
11507         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11508         //if(this.wrap){
11509         //    this.wrap.addClass('x-item-disabled');
11510         //}
11511     },
11512
11513     // private
11514     onEnable : function(){
11515         this.inputEl().dom.disabled = false;
11516         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11517         //if(this.wrap){
11518         //    this.el.removeClass('x-item-disabled');
11519         //}
11520     },
11521
11522     // private
11523     onShow : function(){
11524         var ae = this.getActionEl();
11525         
11526         if(ae){
11527             ae.dom.style.display = '';
11528             ae.dom.style.visibility = 'visible';
11529         }
11530     },
11531
11532     // private
11533     
11534     onHide : function(){
11535         var ae = this.getActionEl();
11536         ae.dom.style.display = 'none';
11537     },
11538
11539     /**
11540      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
11541      * by an implementing function.
11542      * @method
11543      * @param {EventObject} e
11544      */
11545     onTriggerClick : Roo.emptyFn
11546 });
11547  /*
11548  * Based on:
11549  * Ext JS Library 1.1.1
11550  * Copyright(c) 2006-2007, Ext JS, LLC.
11551  *
11552  * Originally Released Under LGPL - original licence link has changed is not relivant.
11553  *
11554  * Fork - LGPL
11555  * <script type="text/javascript">
11556  */
11557
11558
11559 /**
11560  * @class Roo.data.SortTypes
11561  * @singleton
11562  * Defines the default sorting (casting?) comparison functions used when sorting data.
11563  */
11564 Roo.data.SortTypes = {
11565     /**
11566      * Default sort that does nothing
11567      * @param {Mixed} s The value being converted
11568      * @return {Mixed} The comparison value
11569      */
11570     none : function(s){
11571         return s;
11572     },
11573     
11574     /**
11575      * The regular expression used to strip tags
11576      * @type {RegExp}
11577      * @property
11578      */
11579     stripTagsRE : /<\/?[^>]+>/gi,
11580     
11581     /**
11582      * Strips all HTML tags to sort on text only
11583      * @param {Mixed} s The value being converted
11584      * @return {String} The comparison value
11585      */
11586     asText : function(s){
11587         return String(s).replace(this.stripTagsRE, "");
11588     },
11589     
11590     /**
11591      * Strips all HTML tags to sort on text only - Case insensitive
11592      * @param {Mixed} s The value being converted
11593      * @return {String} The comparison value
11594      */
11595     asUCText : function(s){
11596         return String(s).toUpperCase().replace(this.stripTagsRE, "");
11597     },
11598     
11599     /**
11600      * Case insensitive string
11601      * @param {Mixed} s The value being converted
11602      * @return {String} The comparison value
11603      */
11604     asUCString : function(s) {
11605         return String(s).toUpperCase();
11606     },
11607     
11608     /**
11609      * Date sorting
11610      * @param {Mixed} s The value being converted
11611      * @return {Number} The comparison value
11612      */
11613     asDate : function(s) {
11614         if(!s){
11615             return 0;
11616         }
11617         if(s instanceof Date){
11618             return s.getTime();
11619         }
11620         return Date.parse(String(s));
11621     },
11622     
11623     /**
11624      * Float sorting
11625      * @param {Mixed} s The value being converted
11626      * @return {Float} The comparison value
11627      */
11628     asFloat : function(s) {
11629         var val = parseFloat(String(s).replace(/,/g, ""));
11630         if(isNaN(val)) {
11631             val = 0;
11632         }
11633         return val;
11634     },
11635     
11636     /**
11637      * Integer sorting
11638      * @param {Mixed} s The value being converted
11639      * @return {Number} The comparison value
11640      */
11641     asInt : function(s) {
11642         var val = parseInt(String(s).replace(/,/g, ""));
11643         if(isNaN(val)) {
11644             val = 0;
11645         }
11646         return val;
11647     }
11648 };/*
11649  * Based on:
11650  * Ext JS Library 1.1.1
11651  * Copyright(c) 2006-2007, Ext JS, LLC.
11652  *
11653  * Originally Released Under LGPL - original licence link has changed is not relivant.
11654  *
11655  * Fork - LGPL
11656  * <script type="text/javascript">
11657  */
11658
11659 /**
11660 * @class Roo.data.Record
11661  * Instances of this class encapsulate both record <em>definition</em> information, and record
11662  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11663  * to access Records cached in an {@link Roo.data.Store} object.<br>
11664  * <p>
11665  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11666  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11667  * objects.<br>
11668  * <p>
11669  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11670  * @constructor
11671  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11672  * {@link #create}. The parameters are the same.
11673  * @param {Array} data An associative Array of data values keyed by the field name.
11674  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11675  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11676  * not specified an integer id is generated.
11677  */
11678 Roo.data.Record = function(data, id){
11679     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11680     this.data = data;
11681 };
11682
11683 /**
11684  * Generate a constructor for a specific record layout.
11685  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11686  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11687  * Each field definition object may contain the following properties: <ul>
11688  * <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,
11689  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11690  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11691  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11692  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11693  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11694  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11695  * this may be omitted.</p></li>
11696  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11697  * <ul><li>auto (Default, implies no conversion)</li>
11698  * <li>string</li>
11699  * <li>int</li>
11700  * <li>float</li>
11701  * <li>boolean</li>
11702  * <li>date</li></ul></p></li>
11703  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11704  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11705  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11706  * by the Reader into an object that will be stored in the Record. It is passed the
11707  * following parameters:<ul>
11708  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11709  * </ul></p></li>
11710  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11711  * </ul>
11712  * <br>usage:<br><pre><code>
11713 var TopicRecord = Roo.data.Record.create(
11714     {name: 'title', mapping: 'topic_title'},
11715     {name: 'author', mapping: 'username'},
11716     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11717     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11718     {name: 'lastPoster', mapping: 'user2'},
11719     {name: 'excerpt', mapping: 'post_text'}
11720 );
11721
11722 var myNewRecord = new TopicRecord({
11723     title: 'Do my job please',
11724     author: 'noobie',
11725     totalPosts: 1,
11726     lastPost: new Date(),
11727     lastPoster: 'Animal',
11728     excerpt: 'No way dude!'
11729 });
11730 myStore.add(myNewRecord);
11731 </code></pre>
11732  * @method create
11733  * @static
11734  */
11735 Roo.data.Record.create = function(o){
11736     var f = function(){
11737         f.superclass.constructor.apply(this, arguments);
11738     };
11739     Roo.extend(f, Roo.data.Record);
11740     var p = f.prototype;
11741     p.fields = new Roo.util.MixedCollection(false, function(field){
11742         return field.name;
11743     });
11744     for(var i = 0, len = o.length; i < len; i++){
11745         p.fields.add(new Roo.data.Field(o[i]));
11746     }
11747     f.getField = function(name){
11748         return p.fields.get(name);  
11749     };
11750     return f;
11751 };
11752
11753 Roo.data.Record.AUTO_ID = 1000;
11754 Roo.data.Record.EDIT = 'edit';
11755 Roo.data.Record.REJECT = 'reject';
11756 Roo.data.Record.COMMIT = 'commit';
11757
11758 Roo.data.Record.prototype = {
11759     /**
11760      * Readonly flag - true if this record has been modified.
11761      * @type Boolean
11762      */
11763     dirty : false,
11764     editing : false,
11765     error: null,
11766     modified: null,
11767
11768     // private
11769     join : function(store){
11770         this.store = store;
11771     },
11772
11773     /**
11774      * Set the named field to the specified value.
11775      * @param {String} name The name of the field to set.
11776      * @param {Object} value The value to set the field to.
11777      */
11778     set : function(name, value){
11779         if(this.data[name] == value){
11780             return;
11781         }
11782         this.dirty = true;
11783         if(!this.modified){
11784             this.modified = {};
11785         }
11786         if(typeof this.modified[name] == 'undefined'){
11787             this.modified[name] = this.data[name];
11788         }
11789         this.data[name] = value;
11790         if(!this.editing && this.store){
11791             this.store.afterEdit(this);
11792         }       
11793     },
11794
11795     /**
11796      * Get the value of the named field.
11797      * @param {String} name The name of the field to get the value of.
11798      * @return {Object} The value of the field.
11799      */
11800     get : function(name){
11801         return this.data[name]; 
11802     },
11803
11804     // private
11805     beginEdit : function(){
11806         this.editing = true;
11807         this.modified = {}; 
11808     },
11809
11810     // private
11811     cancelEdit : function(){
11812         this.editing = false;
11813         delete this.modified;
11814     },
11815
11816     // private
11817     endEdit : function(){
11818         this.editing = false;
11819         if(this.dirty && this.store){
11820             this.store.afterEdit(this);
11821         }
11822     },
11823
11824     /**
11825      * Usually called by the {@link Roo.data.Store} which owns the Record.
11826      * Rejects all changes made to the Record since either creation, or the last commit operation.
11827      * Modified fields are reverted to their original values.
11828      * <p>
11829      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11830      * of reject operations.
11831      */
11832     reject : function(){
11833         var m = this.modified;
11834         for(var n in m){
11835             if(typeof m[n] != "function"){
11836                 this.data[n] = m[n];
11837             }
11838         }
11839         this.dirty = false;
11840         delete this.modified;
11841         this.editing = false;
11842         if(this.store){
11843             this.store.afterReject(this);
11844         }
11845     },
11846
11847     /**
11848      * Usually called by the {@link Roo.data.Store} which owns the Record.
11849      * Commits all changes made to the Record since either creation, or the last commit operation.
11850      * <p>
11851      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11852      * of commit operations.
11853      */
11854     commit : function(){
11855         this.dirty = false;
11856         delete this.modified;
11857         this.editing = false;
11858         if(this.store){
11859             this.store.afterCommit(this);
11860         }
11861     },
11862
11863     // private
11864     hasError : function(){
11865         return this.error != null;
11866     },
11867
11868     // private
11869     clearError : function(){
11870         this.error = null;
11871     },
11872
11873     /**
11874      * Creates a copy of this record.
11875      * @param {String} id (optional) A new record id if you don't want to use this record's id
11876      * @return {Record}
11877      */
11878     copy : function(newId) {
11879         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11880     }
11881 };/*
11882  * Based on:
11883  * Ext JS Library 1.1.1
11884  * Copyright(c) 2006-2007, Ext JS, LLC.
11885  *
11886  * Originally Released Under LGPL - original licence link has changed is not relivant.
11887  *
11888  * Fork - LGPL
11889  * <script type="text/javascript">
11890  */
11891
11892
11893
11894 /**
11895  * @class Roo.data.Store
11896  * @extends Roo.util.Observable
11897  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11898  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11899  * <p>
11900  * 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
11901  * has no knowledge of the format of the data returned by the Proxy.<br>
11902  * <p>
11903  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11904  * instances from the data object. These records are cached and made available through accessor functions.
11905  * @constructor
11906  * Creates a new Store.
11907  * @param {Object} config A config object containing the objects needed for the Store to access data,
11908  * and read the data into Records.
11909  */
11910 Roo.data.Store = function(config){
11911     this.data = new Roo.util.MixedCollection(false);
11912     this.data.getKey = function(o){
11913         return o.id;
11914     };
11915     this.baseParams = {};
11916     // private
11917     this.paramNames = {
11918         "start" : "start",
11919         "limit" : "limit",
11920         "sort" : "sort",
11921         "dir" : "dir",
11922         "multisort" : "_multisort"
11923     };
11924
11925     if(config && config.data){
11926         this.inlineData = config.data;
11927         delete config.data;
11928     }
11929
11930     Roo.apply(this, config);
11931     
11932     if(this.reader){ // reader passed
11933         this.reader = Roo.factory(this.reader, Roo.data);
11934         this.reader.xmodule = this.xmodule || false;
11935         if(!this.recordType){
11936             this.recordType = this.reader.recordType;
11937         }
11938         if(this.reader.onMetaChange){
11939             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11940         }
11941     }
11942
11943     if(this.recordType){
11944         this.fields = this.recordType.prototype.fields;
11945     }
11946     this.modified = [];
11947
11948     this.addEvents({
11949         /**
11950          * @event datachanged
11951          * Fires when the data cache has changed, and a widget which is using this Store
11952          * as a Record cache should refresh its view.
11953          * @param {Store} this
11954          */
11955         datachanged : true,
11956         /**
11957          * @event metachange
11958          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11959          * @param {Store} this
11960          * @param {Object} meta The JSON metadata
11961          */
11962         metachange : true,
11963         /**
11964          * @event add
11965          * Fires when Records have been added to the Store
11966          * @param {Store} this
11967          * @param {Roo.data.Record[]} records The array of Records added
11968          * @param {Number} index The index at which the record(s) were added
11969          */
11970         add : true,
11971         /**
11972          * @event remove
11973          * Fires when a Record has been removed from the Store
11974          * @param {Store} this
11975          * @param {Roo.data.Record} record The Record that was removed
11976          * @param {Number} index The index at which the record was removed
11977          */
11978         remove : true,
11979         /**
11980          * @event update
11981          * Fires when a Record has been updated
11982          * @param {Store} this
11983          * @param {Roo.data.Record} record The Record that was updated
11984          * @param {String} operation The update operation being performed.  Value may be one of:
11985          * <pre><code>
11986  Roo.data.Record.EDIT
11987  Roo.data.Record.REJECT
11988  Roo.data.Record.COMMIT
11989          * </code></pre>
11990          */
11991         update : true,
11992         /**
11993          * @event clear
11994          * Fires when the data cache has been cleared.
11995          * @param {Store} this
11996          */
11997         clear : true,
11998         /**
11999          * @event beforeload
12000          * Fires before a request is made for a new data object.  If the beforeload handler returns false
12001          * the load action will be canceled.
12002          * @param {Store} this
12003          * @param {Object} options The loading options that were specified (see {@link #load} for details)
12004          */
12005         beforeload : true,
12006         /**
12007          * @event beforeloadadd
12008          * Fires after a new set of Records has been loaded.
12009          * @param {Store} this
12010          * @param {Roo.data.Record[]} records The Records that were loaded
12011          * @param {Object} options The loading options that were specified (see {@link #load} for details)
12012          */
12013         beforeloadadd : true,
12014         /**
12015          * @event load
12016          * Fires after a new set of Records has been loaded, before they are added to the store.
12017          * @param {Store} this
12018          * @param {Roo.data.Record[]} records The Records that were loaded
12019          * @param {Object} options The loading options that were specified (see {@link #load} for details)
12020          * @params {Object} return from reader
12021          */
12022         load : true,
12023         /**
12024          * @event loadexception
12025          * Fires if an exception occurs in the Proxy during loading.
12026          * Called with the signature of the Proxy's "loadexception" event.
12027          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
12028          * 
12029          * @param {Proxy} 
12030          * @param {Object} return from JsonData.reader() - success, totalRecords, records
12031          * @param {Object} load options 
12032          * @param {Object} jsonData from your request (normally this contains the Exception)
12033          */
12034         loadexception : true
12035     });
12036     
12037     if(this.proxy){
12038         this.proxy = Roo.factory(this.proxy, Roo.data);
12039         this.proxy.xmodule = this.xmodule || false;
12040         this.relayEvents(this.proxy,  ["loadexception"]);
12041     }
12042     this.sortToggle = {};
12043     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
12044
12045     Roo.data.Store.superclass.constructor.call(this);
12046
12047     if(this.inlineData){
12048         this.loadData(this.inlineData);
12049         delete this.inlineData;
12050     }
12051 };
12052
12053 Roo.extend(Roo.data.Store, Roo.util.Observable, {
12054      /**
12055     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
12056     * without a remote query - used by combo/forms at present.
12057     */
12058     
12059     /**
12060     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
12061     */
12062     /**
12063     * @cfg {Array} data Inline data to be loaded when the store is initialized.
12064     */
12065     /**
12066     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
12067     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
12068     */
12069     /**
12070     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
12071     * on any HTTP request
12072     */
12073     /**
12074     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
12075     */
12076     /**
12077     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
12078     */
12079     multiSort: false,
12080     /**
12081     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
12082     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
12083     */
12084     remoteSort : false,
12085
12086     /**
12087     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
12088      * loaded or when a record is removed. (defaults to false).
12089     */
12090     pruneModifiedRecords : false,
12091
12092     // private
12093     lastOptions : null,
12094
12095     /**
12096      * Add Records to the Store and fires the add event.
12097      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
12098      */
12099     add : function(records){
12100         records = [].concat(records);
12101         for(var i = 0, len = records.length; i < len; i++){
12102             records[i].join(this);
12103         }
12104         var index = this.data.length;
12105         this.data.addAll(records);
12106         this.fireEvent("add", this, records, index);
12107     },
12108
12109     /**
12110      * Remove a Record from the Store and fires the remove event.
12111      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
12112      */
12113     remove : function(record){
12114         var index = this.data.indexOf(record);
12115         this.data.removeAt(index);
12116  
12117         if(this.pruneModifiedRecords){
12118             this.modified.remove(record);
12119         }
12120         this.fireEvent("remove", this, record, index);
12121     },
12122
12123     /**
12124      * Remove all Records from the Store and fires the clear event.
12125      */
12126     removeAll : function(){
12127         this.data.clear();
12128         if(this.pruneModifiedRecords){
12129             this.modified = [];
12130         }
12131         this.fireEvent("clear", this);
12132     },
12133
12134     /**
12135      * Inserts Records to the Store at the given index and fires the add event.
12136      * @param {Number} index The start index at which to insert the passed Records.
12137      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
12138      */
12139     insert : function(index, records){
12140         records = [].concat(records);
12141         for(var i = 0, len = records.length; i < len; i++){
12142             this.data.insert(index, records[i]);
12143             records[i].join(this);
12144         }
12145         this.fireEvent("add", this, records, index);
12146     },
12147
12148     /**
12149      * Get the index within the cache of the passed Record.
12150      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
12151      * @return {Number} The index of the passed Record. Returns -1 if not found.
12152      */
12153     indexOf : function(record){
12154         return this.data.indexOf(record);
12155     },
12156
12157     /**
12158      * Get the index within the cache of the Record with the passed id.
12159      * @param {String} id The id of the Record to find.
12160      * @return {Number} The index of the Record. Returns -1 if not found.
12161      */
12162     indexOfId : function(id){
12163         return this.data.indexOfKey(id);
12164     },
12165
12166     /**
12167      * Get the Record with the specified id.
12168      * @param {String} id The id of the Record to find.
12169      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
12170      */
12171     getById : function(id){
12172         return this.data.key(id);
12173     },
12174
12175     /**
12176      * Get the Record at the specified index.
12177      * @param {Number} index The index of the Record to find.
12178      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
12179      */
12180     getAt : function(index){
12181         return this.data.itemAt(index);
12182     },
12183
12184     /**
12185      * Returns a range of Records between specified indices.
12186      * @param {Number} startIndex (optional) The starting index (defaults to 0)
12187      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
12188      * @return {Roo.data.Record[]} An array of Records
12189      */
12190     getRange : function(start, end){
12191         return this.data.getRange(start, end);
12192     },
12193
12194     // private
12195     storeOptions : function(o){
12196         o = Roo.apply({}, o);
12197         delete o.callback;
12198         delete o.scope;
12199         this.lastOptions = o;
12200     },
12201
12202     /**
12203      * Loads the Record cache from the configured Proxy using the configured Reader.
12204      * <p>
12205      * If using remote paging, then the first load call must specify the <em>start</em>
12206      * and <em>limit</em> properties in the options.params property to establish the initial
12207      * position within the dataset, and the number of Records to cache on each read from the Proxy.
12208      * <p>
12209      * <strong>It is important to note that for remote data sources, loading is asynchronous,
12210      * and this call will return before the new data has been loaded. Perform any post-processing
12211      * in a callback function, or in a "load" event handler.</strong>
12212      * <p>
12213      * @param {Object} options An object containing properties which control loading options:<ul>
12214      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
12215      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
12216      * passed the following arguments:<ul>
12217      * <li>r : Roo.data.Record[]</li>
12218      * <li>options: Options object from the load call</li>
12219      * <li>success: Boolean success indicator</li></ul></li>
12220      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
12221      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
12222      * </ul>
12223      */
12224     load : function(options){
12225         options = options || {};
12226         if(this.fireEvent("beforeload", this, options) !== false){
12227             this.storeOptions(options);
12228             var p = Roo.apply(options.params || {}, this.baseParams);
12229             // if meta was not loaded from remote source.. try requesting it.
12230             if (!this.reader.metaFromRemote) {
12231                 p._requestMeta = 1;
12232             }
12233             if(this.sortInfo && this.remoteSort){
12234                 var pn = this.paramNames;
12235                 p[pn["sort"]] = this.sortInfo.field;
12236                 p[pn["dir"]] = this.sortInfo.direction;
12237             }
12238             if (this.multiSort) {
12239                 var pn = this.paramNames;
12240                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
12241             }
12242             
12243             this.proxy.load(p, this.reader, this.loadRecords, this, options);
12244         }
12245     },
12246
12247     /**
12248      * Reloads the Record cache from the configured Proxy using the configured Reader and
12249      * the options from the last load operation performed.
12250      * @param {Object} options (optional) An object containing properties which may override the options
12251      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
12252      * the most recently used options are reused).
12253      */
12254     reload : function(options){
12255         this.load(Roo.applyIf(options||{}, this.lastOptions));
12256     },
12257
12258     // private
12259     // Called as a callback by the Reader during a load operation.
12260     loadRecords : function(o, options, success){
12261         if(!o || success === false){
12262             if(success !== false){
12263                 this.fireEvent("load", this, [], options, o);
12264             }
12265             if(options.callback){
12266                 options.callback.call(options.scope || this, [], options, false);
12267             }
12268             return;
12269         }
12270         // if data returned failure - throw an exception.
12271         if (o.success === false) {
12272             // show a message if no listener is registered.
12273             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
12274                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
12275             }
12276             // loadmask wil be hooked into this..
12277             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
12278             return;
12279         }
12280         var r = o.records, t = o.totalRecords || r.length;
12281         
12282         this.fireEvent("beforeloadadd", this, r, options, o);
12283         
12284         if(!options || options.add !== true){
12285             if(this.pruneModifiedRecords){
12286                 this.modified = [];
12287             }
12288             for(var i = 0, len = r.length; i < len; i++){
12289                 r[i].join(this);
12290             }
12291             if(this.snapshot){
12292                 this.data = this.snapshot;
12293                 delete this.snapshot;
12294             }
12295             this.data.clear();
12296             this.data.addAll(r);
12297             this.totalLength = t;
12298             this.applySort();
12299             this.fireEvent("datachanged", this);
12300         }else{
12301             this.totalLength = Math.max(t, this.data.length+r.length);
12302             this.add(r);
12303         }
12304         
12305         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
12306                 
12307             var e = new Roo.data.Record({});
12308
12309             e.set(this.parent.displayField, this.parent.emptyTitle);
12310             e.set(this.parent.valueField, '');
12311
12312             this.insert(0, e);
12313         }
12314             
12315         this.fireEvent("load", this, r, options, o);
12316         if(options.callback){
12317             options.callback.call(options.scope || this, r, options, true);
12318         }
12319     },
12320
12321
12322     /**
12323      * Loads data from a passed data block. A Reader which understands the format of the data
12324      * must have been configured in the constructor.
12325      * @param {Object} data The data block from which to read the Records.  The format of the data expected
12326      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
12327      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
12328      */
12329     loadData : function(o, append){
12330         var r = this.reader.readRecords(o);
12331         this.loadRecords(r, {add: append}, true);
12332     },
12333     
12334      /**
12335      * using 'cn' the nested child reader read the child array into it's child stores.
12336      * @param {Object} rec The record with a 'children array
12337      */
12338     loadDataFromChildren : function(rec)
12339     {
12340         this.loadData(this.reader.toLoadData(rec));
12341     },
12342     
12343
12344     /**
12345      * Gets the number of cached records.
12346      * <p>
12347      * <em>If using paging, this may not be the total size of the dataset. If the data object
12348      * used by the Reader contains the dataset size, then the getTotalCount() function returns
12349      * the data set size</em>
12350      */
12351     getCount : function(){
12352         return this.data.length || 0;
12353     },
12354
12355     /**
12356      * Gets the total number of records in the dataset as returned by the server.
12357      * <p>
12358      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
12359      * the dataset size</em>
12360      */
12361     getTotalCount : function(){
12362         return this.totalLength || 0;
12363     },
12364
12365     /**
12366      * Returns the sort state of the Store as an object with two properties:
12367      * <pre><code>
12368  field {String} The name of the field by which the Records are sorted
12369  direction {String} The sort order, "ASC" or "DESC"
12370      * </code></pre>
12371      */
12372     getSortState : function(){
12373         return this.sortInfo;
12374     },
12375
12376     // private
12377     applySort : function(){
12378         if(this.sortInfo && !this.remoteSort){
12379             var s = this.sortInfo, f = s.field;
12380             var st = this.fields.get(f).sortType;
12381             var fn = function(r1, r2){
12382                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
12383                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12384             };
12385             this.data.sort(s.direction, fn);
12386             if(this.snapshot && this.snapshot != this.data){
12387                 this.snapshot.sort(s.direction, fn);
12388             }
12389         }
12390     },
12391
12392     /**
12393      * Sets the default sort column and order to be used by the next load operation.
12394      * @param {String} fieldName The name of the field to sort by.
12395      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12396      */
12397     setDefaultSort : function(field, dir){
12398         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
12399     },
12400
12401     /**
12402      * Sort the Records.
12403      * If remote sorting is used, the sort is performed on the server, and the cache is
12404      * reloaded. If local sorting is used, the cache is sorted internally.
12405      * @param {String} fieldName The name of the field to sort by.
12406      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12407      */
12408     sort : function(fieldName, dir){
12409         var f = this.fields.get(fieldName);
12410         if(!dir){
12411             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
12412             
12413             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
12414                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
12415             }else{
12416                 dir = f.sortDir;
12417             }
12418         }
12419         this.sortToggle[f.name] = dir;
12420         this.sortInfo = {field: f.name, direction: dir};
12421         if(!this.remoteSort){
12422             this.applySort();
12423             this.fireEvent("datachanged", this);
12424         }else{
12425             this.load(this.lastOptions);
12426         }
12427     },
12428
12429     /**
12430      * Calls the specified function for each of the Records in the cache.
12431      * @param {Function} fn The function to call. The Record is passed as the first parameter.
12432      * Returning <em>false</em> aborts and exits the iteration.
12433      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
12434      */
12435     each : function(fn, scope){
12436         this.data.each(fn, scope);
12437     },
12438
12439     /**
12440      * Gets all records modified since the last commit.  Modified records are persisted across load operations
12441      * (e.g., during paging).
12442      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
12443      */
12444     getModifiedRecords : function(){
12445         return this.modified;
12446     },
12447
12448     // private
12449     createFilterFn : function(property, value, anyMatch){
12450         if(!value.exec){ // not a regex
12451             value = String(value);
12452             if(value.length == 0){
12453                 return false;
12454             }
12455             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
12456         }
12457         return function(r){
12458             return value.test(r.data[property]);
12459         };
12460     },
12461
12462     /**
12463      * Sums the value of <i>property</i> for each record between start and end and returns the result.
12464      * @param {String} property A field on your records
12465      * @param {Number} start The record index to start at (defaults to 0)
12466      * @param {Number} end The last record index to include (defaults to length - 1)
12467      * @return {Number} The sum
12468      */
12469     sum : function(property, start, end){
12470         var rs = this.data.items, v = 0;
12471         start = start || 0;
12472         end = (end || end === 0) ? end : rs.length-1;
12473
12474         for(var i = start; i <= end; i++){
12475             v += (rs[i].data[property] || 0);
12476         }
12477         return v;
12478     },
12479
12480     /**
12481      * Filter the records by a specified property.
12482      * @param {String} field A field on your records
12483      * @param {String/RegExp} value Either a string that the field
12484      * should start with or a RegExp to test against the field
12485      * @param {Boolean} anyMatch True to match any part not just the beginning
12486      */
12487     filter : function(property, value, anyMatch){
12488         var fn = this.createFilterFn(property, value, anyMatch);
12489         return fn ? this.filterBy(fn) : this.clearFilter();
12490     },
12491
12492     /**
12493      * Filter by a function. The specified function will be called with each
12494      * record in this data source. If the function returns true the record is included,
12495      * otherwise it is filtered.
12496      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12497      * @param {Object} scope (optional) The scope of the function (defaults to this)
12498      */
12499     filterBy : function(fn, scope){
12500         this.snapshot = this.snapshot || this.data;
12501         this.data = this.queryBy(fn, scope||this);
12502         this.fireEvent("datachanged", this);
12503     },
12504
12505     /**
12506      * Query the records by a specified property.
12507      * @param {String} field A field on your records
12508      * @param {String/RegExp} value Either a string that the field
12509      * should start with or a RegExp to test against the field
12510      * @param {Boolean} anyMatch True to match any part not just the beginning
12511      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12512      */
12513     query : function(property, value, anyMatch){
12514         var fn = this.createFilterFn(property, value, anyMatch);
12515         return fn ? this.queryBy(fn) : this.data.clone();
12516     },
12517
12518     /**
12519      * Query by a function. The specified function will be called with each
12520      * record in this data source. If the function returns true the record is included
12521      * in the results.
12522      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12523      * @param {Object} scope (optional) The scope of the function (defaults to this)
12524       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12525      **/
12526     queryBy : function(fn, scope){
12527         var data = this.snapshot || this.data;
12528         return data.filterBy(fn, scope||this);
12529     },
12530
12531     /**
12532      * Collects unique values for a particular dataIndex from this store.
12533      * @param {String} dataIndex The property to collect
12534      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12535      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12536      * @return {Array} An array of the unique values
12537      **/
12538     collect : function(dataIndex, allowNull, bypassFilter){
12539         var d = (bypassFilter === true && this.snapshot) ?
12540                 this.snapshot.items : this.data.items;
12541         var v, sv, r = [], l = {};
12542         for(var i = 0, len = d.length; i < len; i++){
12543             v = d[i].data[dataIndex];
12544             sv = String(v);
12545             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12546                 l[sv] = true;
12547                 r[r.length] = v;
12548             }
12549         }
12550         return r;
12551     },
12552
12553     /**
12554      * Revert to a view of the Record cache with no filtering applied.
12555      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12556      */
12557     clearFilter : function(suppressEvent){
12558         if(this.snapshot && this.snapshot != this.data){
12559             this.data = this.snapshot;
12560             delete this.snapshot;
12561             if(suppressEvent !== true){
12562                 this.fireEvent("datachanged", this);
12563             }
12564         }
12565     },
12566
12567     // private
12568     afterEdit : function(record){
12569         if(this.modified.indexOf(record) == -1){
12570             this.modified.push(record);
12571         }
12572         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12573     },
12574     
12575     // private
12576     afterReject : function(record){
12577         this.modified.remove(record);
12578         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12579     },
12580
12581     // private
12582     afterCommit : function(record){
12583         this.modified.remove(record);
12584         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12585     },
12586
12587     /**
12588      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12589      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12590      */
12591     commitChanges : function(){
12592         var m = this.modified.slice(0);
12593         this.modified = [];
12594         for(var i = 0, len = m.length; i < len; i++){
12595             m[i].commit();
12596         }
12597     },
12598
12599     /**
12600      * Cancel outstanding changes on all changed records.
12601      */
12602     rejectChanges : function(){
12603         var m = this.modified.slice(0);
12604         this.modified = [];
12605         for(var i = 0, len = m.length; i < len; i++){
12606             m[i].reject();
12607         }
12608     },
12609
12610     onMetaChange : function(meta, rtype, o){
12611         this.recordType = rtype;
12612         this.fields = rtype.prototype.fields;
12613         delete this.snapshot;
12614         this.sortInfo = meta.sortInfo || this.sortInfo;
12615         this.modified = [];
12616         this.fireEvent('metachange', this, this.reader.meta);
12617     },
12618     
12619     moveIndex : function(data, type)
12620     {
12621         var index = this.indexOf(data);
12622         
12623         var newIndex = index + type;
12624         
12625         this.remove(data);
12626         
12627         this.insert(newIndex, data);
12628         
12629     }
12630 });/*
12631  * Based on:
12632  * Ext JS Library 1.1.1
12633  * Copyright(c) 2006-2007, Ext JS, LLC.
12634  *
12635  * Originally Released Under LGPL - original licence link has changed is not relivant.
12636  *
12637  * Fork - LGPL
12638  * <script type="text/javascript">
12639  */
12640
12641 /**
12642  * @class Roo.data.SimpleStore
12643  * @extends Roo.data.Store
12644  * Small helper class to make creating Stores from Array data easier.
12645  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12646  * @cfg {Array} fields An array of field definition objects, or field name strings.
12647  * @cfg {Object} an existing reader (eg. copied from another store)
12648  * @cfg {Array} data The multi-dimensional array of data
12649  * @constructor
12650  * @param {Object} config
12651  */
12652 Roo.data.SimpleStore = function(config)
12653 {
12654     Roo.data.SimpleStore.superclass.constructor.call(this, {
12655         isLocal : true,
12656         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
12657                 id: config.id
12658             },
12659             Roo.data.Record.create(config.fields)
12660         ),
12661         proxy : new Roo.data.MemoryProxy(config.data)
12662     });
12663     this.load();
12664 };
12665 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12666  * Based on:
12667  * Ext JS Library 1.1.1
12668  * Copyright(c) 2006-2007, Ext JS, LLC.
12669  *
12670  * Originally Released Under LGPL - original licence link has changed is not relivant.
12671  *
12672  * Fork - LGPL
12673  * <script type="text/javascript">
12674  */
12675
12676 /**
12677 /**
12678  * @extends Roo.data.Store
12679  * @class Roo.data.JsonStore
12680  * Small helper class to make creating Stores for JSON data easier. <br/>
12681 <pre><code>
12682 var store = new Roo.data.JsonStore({
12683     url: 'get-images.php',
12684     root: 'images',
12685     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12686 });
12687 </code></pre>
12688  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12689  * JsonReader and HttpProxy (unless inline data is provided).</b>
12690  * @cfg {Array} fields An array of field definition objects, or field name strings.
12691  * @constructor
12692  * @param {Object} config
12693  */
12694 Roo.data.JsonStore = function(c){
12695     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12696         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12697         reader: new Roo.data.JsonReader(c, c.fields)
12698     }));
12699 };
12700 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12701  * Based on:
12702  * Ext JS Library 1.1.1
12703  * Copyright(c) 2006-2007, Ext JS, LLC.
12704  *
12705  * Originally Released Under LGPL - original licence link has changed is not relivant.
12706  *
12707  * Fork - LGPL
12708  * <script type="text/javascript">
12709  */
12710
12711  
12712 Roo.data.Field = function(config){
12713     if(typeof config == "string"){
12714         config = {name: config};
12715     }
12716     Roo.apply(this, config);
12717     
12718     if(!this.type){
12719         this.type = "auto";
12720     }
12721     
12722     var st = Roo.data.SortTypes;
12723     // named sortTypes are supported, here we look them up
12724     if(typeof this.sortType == "string"){
12725         this.sortType = st[this.sortType];
12726     }
12727     
12728     // set default sortType for strings and dates
12729     if(!this.sortType){
12730         switch(this.type){
12731             case "string":
12732                 this.sortType = st.asUCString;
12733                 break;
12734             case "date":
12735                 this.sortType = st.asDate;
12736                 break;
12737             default:
12738                 this.sortType = st.none;
12739         }
12740     }
12741
12742     // define once
12743     var stripRe = /[\$,%]/g;
12744
12745     // prebuilt conversion function for this field, instead of
12746     // switching every time we're reading a value
12747     if(!this.convert){
12748         var cv, dateFormat = this.dateFormat;
12749         switch(this.type){
12750             case "":
12751             case "auto":
12752             case undefined:
12753                 cv = function(v){ return v; };
12754                 break;
12755             case "string":
12756                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12757                 break;
12758             case "int":
12759                 cv = function(v){
12760                     return v !== undefined && v !== null && v !== '' ?
12761                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12762                     };
12763                 break;
12764             case "float":
12765                 cv = function(v){
12766                     return v !== undefined && v !== null && v !== '' ?
12767                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12768                     };
12769                 break;
12770             case "bool":
12771             case "boolean":
12772                 cv = function(v){ return v === true || v === "true" || v == 1; };
12773                 break;
12774             case "date":
12775                 cv = function(v){
12776                     if(!v){
12777                         return '';
12778                     }
12779                     if(v instanceof Date){
12780                         return v;
12781                     }
12782                     if(dateFormat){
12783                         if(dateFormat == "timestamp"){
12784                             return new Date(v*1000);
12785                         }
12786                         return Date.parseDate(v, dateFormat);
12787                     }
12788                     var parsed = Date.parse(v);
12789                     return parsed ? new Date(parsed) : null;
12790                 };
12791              break;
12792             
12793         }
12794         this.convert = cv;
12795     }
12796 };
12797
12798 Roo.data.Field.prototype = {
12799     dateFormat: null,
12800     defaultValue: "",
12801     mapping: null,
12802     sortType : null,
12803     sortDir : "ASC"
12804 };/*
12805  * Based on:
12806  * Ext JS Library 1.1.1
12807  * Copyright(c) 2006-2007, Ext JS, LLC.
12808  *
12809  * Originally Released Under LGPL - original licence link has changed is not relivant.
12810  *
12811  * Fork - LGPL
12812  * <script type="text/javascript">
12813  */
12814  
12815 // Base class for reading structured data from a data source.  This class is intended to be
12816 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12817
12818 /**
12819  * @class Roo.data.DataReader
12820  * Base class for reading structured data from a data source.  This class is intended to be
12821  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12822  */
12823
12824 Roo.data.DataReader = function(meta, recordType){
12825     
12826     this.meta = meta;
12827     
12828     this.recordType = recordType instanceof Array ? 
12829         Roo.data.Record.create(recordType) : recordType;
12830 };
12831
12832 Roo.data.DataReader.prototype = {
12833     
12834     
12835     readerType : 'Data',
12836      /**
12837      * Create an empty record
12838      * @param {Object} data (optional) - overlay some values
12839      * @return {Roo.data.Record} record created.
12840      */
12841     newRow :  function(d) {
12842         var da =  {};
12843         this.recordType.prototype.fields.each(function(c) {
12844             switch( c.type) {
12845                 case 'int' : da[c.name] = 0; break;
12846                 case 'date' : da[c.name] = new Date(); break;
12847                 case 'float' : da[c.name] = 0.0; break;
12848                 case 'boolean' : da[c.name] = false; break;
12849                 default : da[c.name] = ""; break;
12850             }
12851             
12852         });
12853         return new this.recordType(Roo.apply(da, d));
12854     }
12855     
12856     
12857 };/*
12858  * Based on:
12859  * Ext JS Library 1.1.1
12860  * Copyright(c) 2006-2007, Ext JS, LLC.
12861  *
12862  * Originally Released Under LGPL - original licence link has changed is not relivant.
12863  *
12864  * Fork - LGPL
12865  * <script type="text/javascript">
12866  */
12867
12868 /**
12869  * @class Roo.data.DataProxy
12870  * @extends Roo.data.Observable
12871  * This class is an abstract base class for implementations which provide retrieval of
12872  * unformatted data objects.<br>
12873  * <p>
12874  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12875  * (of the appropriate type which knows how to parse the data object) to provide a block of
12876  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12877  * <p>
12878  * Custom implementations must implement the load method as described in
12879  * {@link Roo.data.HttpProxy#load}.
12880  */
12881 Roo.data.DataProxy = function(){
12882     this.addEvents({
12883         /**
12884          * @event beforeload
12885          * Fires before a network request is made to retrieve a data object.
12886          * @param {Object} This DataProxy object.
12887          * @param {Object} params The params parameter to the load function.
12888          */
12889         beforeload : true,
12890         /**
12891          * @event load
12892          * Fires before the load method's callback is called.
12893          * @param {Object} This DataProxy object.
12894          * @param {Object} o The data object.
12895          * @param {Object} arg The callback argument object passed to the load function.
12896          */
12897         load : true,
12898         /**
12899          * @event loadexception
12900          * Fires if an Exception occurs during data retrieval.
12901          * @param {Object} This DataProxy object.
12902          * @param {Object} o The data object.
12903          * @param {Object} arg The callback argument object passed to the load function.
12904          * @param {Object} e The Exception.
12905          */
12906         loadexception : true
12907     });
12908     Roo.data.DataProxy.superclass.constructor.call(this);
12909 };
12910
12911 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12912
12913     /**
12914      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12915      */
12916 /*
12917  * Based on:
12918  * Ext JS Library 1.1.1
12919  * Copyright(c) 2006-2007, Ext JS, LLC.
12920  *
12921  * Originally Released Under LGPL - original licence link has changed is not relivant.
12922  *
12923  * Fork - LGPL
12924  * <script type="text/javascript">
12925  */
12926 /**
12927  * @class Roo.data.MemoryProxy
12928  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12929  * to the Reader when its load method is called.
12930  * @constructor
12931  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12932  */
12933 Roo.data.MemoryProxy = function(data){
12934     if (data.data) {
12935         data = data.data;
12936     }
12937     Roo.data.MemoryProxy.superclass.constructor.call(this);
12938     this.data = data;
12939 };
12940
12941 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12942     
12943     /**
12944      * Load data from the requested source (in this case an in-memory
12945      * data object passed to the constructor), read the data object into
12946      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12947      * process that block using the passed callback.
12948      * @param {Object} params This parameter is not used by the MemoryProxy class.
12949      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12950      * object into a block of Roo.data.Records.
12951      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12952      * The function must be passed <ul>
12953      * <li>The Record block object</li>
12954      * <li>The "arg" argument from the load function</li>
12955      * <li>A boolean success indicator</li>
12956      * </ul>
12957      * @param {Object} scope The scope in which to call the callback
12958      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12959      */
12960     load : function(params, reader, callback, scope, arg){
12961         params = params || {};
12962         var result;
12963         try {
12964             result = reader.readRecords(params.data ? params.data :this.data);
12965         }catch(e){
12966             this.fireEvent("loadexception", this, arg, null, e);
12967             callback.call(scope, null, arg, false);
12968             return;
12969         }
12970         callback.call(scope, result, arg, true);
12971     },
12972     
12973     // private
12974     update : function(params, records){
12975         
12976     }
12977 });/*
12978  * Based on:
12979  * Ext JS Library 1.1.1
12980  * Copyright(c) 2006-2007, Ext JS, LLC.
12981  *
12982  * Originally Released Under LGPL - original licence link has changed is not relivant.
12983  *
12984  * Fork - LGPL
12985  * <script type="text/javascript">
12986  */
12987 /**
12988  * @class Roo.data.HttpProxy
12989  * @extends Roo.data.DataProxy
12990  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12991  * configured to reference a certain URL.<br><br>
12992  * <p>
12993  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12994  * from which the running page was served.<br><br>
12995  * <p>
12996  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12997  * <p>
12998  * Be aware that to enable the browser to parse an XML document, the server must set
12999  * the Content-Type header in the HTTP response to "text/xml".
13000  * @constructor
13001  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
13002  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
13003  * will be used to make the request.
13004  */
13005 Roo.data.HttpProxy = function(conn){
13006     Roo.data.HttpProxy.superclass.constructor.call(this);
13007     // is conn a conn config or a real conn?
13008     this.conn = conn;
13009     this.useAjax = !conn || !conn.events;
13010   
13011 };
13012
13013 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
13014     // thse are take from connection...
13015     
13016     /**
13017      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
13018      */
13019     /**
13020      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
13021      * extra parameters to each request made by this object. (defaults to undefined)
13022      */
13023     /**
13024      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
13025      *  to each request made by this object. (defaults to undefined)
13026      */
13027     /**
13028      * @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)
13029      */
13030     /**
13031      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13032      */
13033      /**
13034      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
13035      * @type Boolean
13036      */
13037   
13038
13039     /**
13040      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
13041      * @type Boolean
13042      */
13043     /**
13044      * Return the {@link Roo.data.Connection} object being used by this Proxy.
13045      * @return {Connection} The Connection object. This object may be used to subscribe to events on
13046      * a finer-grained basis than the DataProxy events.
13047      */
13048     getConnection : function(){
13049         return this.useAjax ? Roo.Ajax : this.conn;
13050     },
13051
13052     /**
13053      * Load data from the configured {@link Roo.data.Connection}, read the data object into
13054      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
13055      * process that block using the passed callback.
13056      * @param {Object} params An object containing properties which are to be used as HTTP parameters
13057      * for the request to the remote server.
13058      * @param {Roo.data.DataReader} reader The Reader object which converts the data
13059      * object into a block of Roo.data.Records.
13060      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
13061      * The function must be passed <ul>
13062      * <li>The Record block object</li>
13063      * <li>The "arg" argument from the load function</li>
13064      * <li>A boolean success indicator</li>
13065      * </ul>
13066      * @param {Object} scope The scope in which to call the callback
13067      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13068      */
13069     load : function(params, reader, callback, scope, arg){
13070         if(this.fireEvent("beforeload", this, params) !== false){
13071             var  o = {
13072                 params : params || {},
13073                 request: {
13074                     callback : callback,
13075                     scope : scope,
13076                     arg : arg
13077                 },
13078                 reader: reader,
13079                 callback : this.loadResponse,
13080                 scope: this
13081             };
13082             if(this.useAjax){
13083                 Roo.applyIf(o, this.conn);
13084                 if(this.activeRequest){
13085                     Roo.Ajax.abort(this.activeRequest);
13086                 }
13087                 this.activeRequest = Roo.Ajax.request(o);
13088             }else{
13089                 this.conn.request(o);
13090             }
13091         }else{
13092             callback.call(scope||this, null, arg, false);
13093         }
13094     },
13095
13096     // private
13097     loadResponse : function(o, success, response){
13098         delete this.activeRequest;
13099         if(!success){
13100             this.fireEvent("loadexception", this, o, response);
13101             o.request.callback.call(o.request.scope, null, o.request.arg, false);
13102             return;
13103         }
13104         var result;
13105         try {
13106             result = o.reader.read(response);
13107         }catch(e){
13108             this.fireEvent("loadexception", this, o, response, e);
13109             o.request.callback.call(o.request.scope, null, o.request.arg, false);
13110             return;
13111         }
13112         
13113         this.fireEvent("load", this, o, o.request.arg);
13114         o.request.callback.call(o.request.scope, result, o.request.arg, true);
13115     },
13116
13117     // private
13118     update : function(dataSet){
13119
13120     },
13121
13122     // private
13123     updateResponse : function(dataSet){
13124
13125     }
13126 });/*
13127  * Based on:
13128  * Ext JS Library 1.1.1
13129  * Copyright(c) 2006-2007, Ext JS, LLC.
13130  *
13131  * Originally Released Under LGPL - original licence link has changed is not relivant.
13132  *
13133  * Fork - LGPL
13134  * <script type="text/javascript">
13135  */
13136
13137 /**
13138  * @class Roo.data.ScriptTagProxy
13139  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
13140  * other than the originating domain of the running page.<br><br>
13141  * <p>
13142  * <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
13143  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
13144  * <p>
13145  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
13146  * source code that is used as the source inside a &lt;script> tag.<br><br>
13147  * <p>
13148  * In order for the browser to process the returned data, the server must wrap the data object
13149  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
13150  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
13151  * depending on whether the callback name was passed:
13152  * <p>
13153  * <pre><code>
13154 boolean scriptTag = false;
13155 String cb = request.getParameter("callback");
13156 if (cb != null) {
13157     scriptTag = true;
13158     response.setContentType("text/javascript");
13159 } else {
13160     response.setContentType("application/x-json");
13161 }
13162 Writer out = response.getWriter();
13163 if (scriptTag) {
13164     out.write(cb + "(");
13165 }
13166 out.print(dataBlock.toJsonString());
13167 if (scriptTag) {
13168     out.write(");");
13169 }
13170 </pre></code>
13171  *
13172  * @constructor
13173  * @param {Object} config A configuration object.
13174  */
13175 Roo.data.ScriptTagProxy = function(config){
13176     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
13177     Roo.apply(this, config);
13178     this.head = document.getElementsByTagName("head")[0];
13179 };
13180
13181 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
13182
13183 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
13184     /**
13185      * @cfg {String} url The URL from which to request the data object.
13186      */
13187     /**
13188      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
13189      */
13190     timeout : 30000,
13191     /**
13192      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
13193      * the server the name of the callback function set up by the load call to process the returned data object.
13194      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
13195      * javascript output which calls this named function passing the data object as its only parameter.
13196      */
13197     callbackParam : "callback",
13198     /**
13199      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
13200      * name to the request.
13201      */
13202     nocache : true,
13203
13204     /**
13205      * Load data from the configured URL, read the data object into
13206      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13207      * process that block using the passed callback.
13208      * @param {Object} params An object containing properties which are to be used as HTTP parameters
13209      * for the request to the remote server.
13210      * @param {Roo.data.DataReader} reader The Reader object which converts the data
13211      * object into a block of Roo.data.Records.
13212      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
13213      * The function must be passed <ul>
13214      * <li>The Record block object</li>
13215      * <li>The "arg" argument from the load function</li>
13216      * <li>A boolean success indicator</li>
13217      * </ul>
13218      * @param {Object} scope The scope in which to call the callback
13219      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13220      */
13221     load : function(params, reader, callback, scope, arg){
13222         if(this.fireEvent("beforeload", this, params) !== false){
13223
13224             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
13225
13226             var url = this.url;
13227             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
13228             if(this.nocache){
13229                 url += "&_dc=" + (new Date().getTime());
13230             }
13231             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
13232             var trans = {
13233                 id : transId,
13234                 cb : "stcCallback"+transId,
13235                 scriptId : "stcScript"+transId,
13236                 params : params,
13237                 arg : arg,
13238                 url : url,
13239                 callback : callback,
13240                 scope : scope,
13241                 reader : reader
13242             };
13243             var conn = this;
13244
13245             window[trans.cb] = function(o){
13246                 conn.handleResponse(o, trans);
13247             };
13248
13249             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
13250
13251             if(this.autoAbort !== false){
13252                 this.abort();
13253             }
13254
13255             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
13256
13257             var script = document.createElement("script");
13258             script.setAttribute("src", url);
13259             script.setAttribute("type", "text/javascript");
13260             script.setAttribute("id", trans.scriptId);
13261             this.head.appendChild(script);
13262
13263             this.trans = trans;
13264         }else{
13265             callback.call(scope||this, null, arg, false);
13266         }
13267     },
13268
13269     // private
13270     isLoading : function(){
13271         return this.trans ? true : false;
13272     },
13273
13274     /**
13275      * Abort the current server request.
13276      */
13277     abort : function(){
13278         if(this.isLoading()){
13279             this.destroyTrans(this.trans);
13280         }
13281     },
13282
13283     // private
13284     destroyTrans : function(trans, isLoaded){
13285         this.head.removeChild(document.getElementById(trans.scriptId));
13286         clearTimeout(trans.timeoutId);
13287         if(isLoaded){
13288             window[trans.cb] = undefined;
13289             try{
13290                 delete window[trans.cb];
13291             }catch(e){}
13292         }else{
13293             // if hasn't been loaded, wait for load to remove it to prevent script error
13294             window[trans.cb] = function(){
13295                 window[trans.cb] = undefined;
13296                 try{
13297                     delete window[trans.cb];
13298                 }catch(e){}
13299             };
13300         }
13301     },
13302
13303     // private
13304     handleResponse : function(o, trans){
13305         this.trans = false;
13306         this.destroyTrans(trans, true);
13307         var result;
13308         try {
13309             result = trans.reader.readRecords(o);
13310         }catch(e){
13311             this.fireEvent("loadexception", this, o, trans.arg, e);
13312             trans.callback.call(trans.scope||window, null, trans.arg, false);
13313             return;
13314         }
13315         this.fireEvent("load", this, o, trans.arg);
13316         trans.callback.call(trans.scope||window, result, trans.arg, true);
13317     },
13318
13319     // private
13320     handleFailure : function(trans){
13321         this.trans = false;
13322         this.destroyTrans(trans, false);
13323         this.fireEvent("loadexception", this, null, trans.arg);
13324         trans.callback.call(trans.scope||window, null, trans.arg, false);
13325     }
13326 });/*
13327  * Based on:
13328  * Ext JS Library 1.1.1
13329  * Copyright(c) 2006-2007, Ext JS, LLC.
13330  *
13331  * Originally Released Under LGPL - original licence link has changed is not relivant.
13332  *
13333  * Fork - LGPL
13334  * <script type="text/javascript">
13335  */
13336
13337 /**
13338  * @class Roo.data.JsonReader
13339  * @extends Roo.data.DataReader
13340  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
13341  * based on mappings in a provided Roo.data.Record constructor.
13342  * 
13343  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
13344  * in the reply previously. 
13345  * 
13346  * <p>
13347  * Example code:
13348  * <pre><code>
13349 var RecordDef = Roo.data.Record.create([
13350     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
13351     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
13352 ]);
13353 var myReader = new Roo.data.JsonReader({
13354     totalProperty: "results",    // The property which contains the total dataset size (optional)
13355     root: "rows",                // The property which contains an Array of row objects
13356     id: "id"                     // The property within each row object that provides an ID for the record (optional)
13357 }, RecordDef);
13358 </code></pre>
13359  * <p>
13360  * This would consume a JSON file like this:
13361  * <pre><code>
13362 { 'results': 2, 'rows': [
13363     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
13364     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
13365 }
13366 </code></pre>
13367  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
13368  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
13369  * paged from the remote server.
13370  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
13371  * @cfg {String} root name of the property which contains the Array of row objects.
13372  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13373  * @cfg {Array} fields Array of field definition objects
13374  * @constructor
13375  * Create a new JsonReader
13376  * @param {Object} meta Metadata configuration options
13377  * @param {Object} recordType Either an Array of field definition objects,
13378  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
13379  */
13380 Roo.data.JsonReader = function(meta, recordType){
13381     
13382     meta = meta || {};
13383     // set some defaults:
13384     Roo.applyIf(meta, {
13385         totalProperty: 'total',
13386         successProperty : 'success',
13387         root : 'data',
13388         id : 'id'
13389     });
13390     
13391     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13392 };
13393 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
13394     
13395     readerType : 'Json',
13396     
13397     /**
13398      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
13399      * Used by Store query builder to append _requestMeta to params.
13400      * 
13401      */
13402     metaFromRemote : false,
13403     /**
13404      * This method is only used by a DataProxy which has retrieved data from a remote server.
13405      * @param {Object} response The XHR object which contains the JSON data in its responseText.
13406      * @return {Object} data A data block which is used by an Roo.data.Store object as
13407      * a cache of Roo.data.Records.
13408      */
13409     read : function(response){
13410         var json = response.responseText;
13411        
13412         var o = /* eval:var:o */ eval("("+json+")");
13413         if(!o) {
13414             throw {message: "JsonReader.read: Json object not found"};
13415         }
13416         
13417         if(o.metaData){
13418             
13419             delete this.ef;
13420             this.metaFromRemote = true;
13421             this.meta = o.metaData;
13422             this.recordType = Roo.data.Record.create(o.metaData.fields);
13423             this.onMetaChange(this.meta, this.recordType, o);
13424         }
13425         return this.readRecords(o);
13426     },
13427
13428     // private function a store will implement
13429     onMetaChange : function(meta, recordType, o){
13430
13431     },
13432
13433     /**
13434          * @ignore
13435          */
13436     simpleAccess: function(obj, subsc) {
13437         return obj[subsc];
13438     },
13439
13440         /**
13441          * @ignore
13442          */
13443     getJsonAccessor: function(){
13444         var re = /[\[\.]/;
13445         return function(expr) {
13446             try {
13447                 return(re.test(expr))
13448                     ? new Function("obj", "return obj." + expr)
13449                     : function(obj){
13450                         return obj[expr];
13451                     };
13452             } catch(e){}
13453             return Roo.emptyFn;
13454         };
13455     }(),
13456
13457     /**
13458      * Create a data block containing Roo.data.Records from an XML document.
13459      * @param {Object} o An object which contains an Array of row objects in the property specified
13460      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
13461      * which contains the total size of the dataset.
13462      * @return {Object} data A data block which is used by an Roo.data.Store object as
13463      * a cache of Roo.data.Records.
13464      */
13465     readRecords : function(o){
13466         /**
13467          * After any data loads, the raw JSON data is available for further custom processing.
13468          * @type Object
13469          */
13470         this.o = o;
13471         var s = this.meta, Record = this.recordType,
13472             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
13473
13474 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
13475         if (!this.ef) {
13476             if(s.totalProperty) {
13477                     this.getTotal = this.getJsonAccessor(s.totalProperty);
13478                 }
13479                 if(s.successProperty) {
13480                     this.getSuccess = this.getJsonAccessor(s.successProperty);
13481                 }
13482                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
13483                 if (s.id) {
13484                         var g = this.getJsonAccessor(s.id);
13485                         this.getId = function(rec) {
13486                                 var r = g(rec);  
13487                                 return (r === undefined || r === "") ? null : r;
13488                         };
13489                 } else {
13490                         this.getId = function(){return null;};
13491                 }
13492             this.ef = [];
13493             for(var jj = 0; jj < fl; jj++){
13494                 f = fi[jj];
13495                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
13496                 this.ef[jj] = this.getJsonAccessor(map);
13497             }
13498         }
13499
13500         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
13501         if(s.totalProperty){
13502             var vt = parseInt(this.getTotal(o), 10);
13503             if(!isNaN(vt)){
13504                 totalRecords = vt;
13505             }
13506         }
13507         if(s.successProperty){
13508             var vs = this.getSuccess(o);
13509             if(vs === false || vs === 'false'){
13510                 success = false;
13511             }
13512         }
13513         var records = [];
13514         for(var i = 0; i < c; i++){
13515                 var n = root[i];
13516             var values = {};
13517             var id = this.getId(n);
13518             for(var j = 0; j < fl; j++){
13519                 f = fi[j];
13520             var v = this.ef[j](n);
13521             if (!f.convert) {
13522                 Roo.log('missing convert for ' + f.name);
13523                 Roo.log(f);
13524                 continue;
13525             }
13526             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13527             }
13528             var record = new Record(values, id);
13529             record.json = n;
13530             records[i] = record;
13531         }
13532         return {
13533             raw : o,
13534             success : success,
13535             records : records,
13536             totalRecords : totalRecords
13537         };
13538     },
13539     // used when loading children.. @see loadDataFromChildren
13540     toLoadData: function(rec)
13541     {
13542         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13543         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13544         return { data : data, total : data.length };
13545         
13546     }
13547 });/*
13548  * Based on:
13549  * Ext JS Library 1.1.1
13550  * Copyright(c) 2006-2007, Ext JS, LLC.
13551  *
13552  * Originally Released Under LGPL - original licence link has changed is not relivant.
13553  *
13554  * Fork - LGPL
13555  * <script type="text/javascript">
13556  */
13557
13558 /**
13559  * @class Roo.data.ArrayReader
13560  * @extends Roo.data.DataReader
13561  * Data reader class to create an Array of Roo.data.Record objects from an Array.
13562  * Each element of that Array represents a row of data fields. The
13563  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13564  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13565  * <p>
13566  * Example code:.
13567  * <pre><code>
13568 var RecordDef = Roo.data.Record.create([
13569     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
13570     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
13571 ]);
13572 var myReader = new Roo.data.ArrayReader({
13573     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
13574 }, RecordDef);
13575 </code></pre>
13576  * <p>
13577  * This would consume an Array like this:
13578  * <pre><code>
13579 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13580   </code></pre>
13581  
13582  * @constructor
13583  * Create a new JsonReader
13584  * @param {Object} meta Metadata configuration options.
13585  * @param {Object|Array} recordType Either an Array of field definition objects
13586  * 
13587  * @cfg {Array} fields Array of field definition objects
13588  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13589  * as specified to {@link Roo.data.Record#create},
13590  * or an {@link Roo.data.Record} object
13591  *
13592  * 
13593  * created using {@link Roo.data.Record#create}.
13594  */
13595 Roo.data.ArrayReader = function(meta, recordType)
13596 {    
13597     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13598 };
13599
13600 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13601     
13602       /**
13603      * Create a data block containing Roo.data.Records from an XML document.
13604      * @param {Object} o An Array of row objects which represents the dataset.
13605      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13606      * a cache of Roo.data.Records.
13607      */
13608     readRecords : function(o)
13609     {
13610         var sid = this.meta ? this.meta.id : null;
13611         var recordType = this.recordType, fields = recordType.prototype.fields;
13612         var records = [];
13613         var root = o;
13614         for(var i = 0; i < root.length; i++){
13615                 var n = root[i];
13616             var values = {};
13617             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13618             for(var j = 0, jlen = fields.length; j < jlen; j++){
13619                 var f = fields.items[j];
13620                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13621                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13622                 v = f.convert(v);
13623                 values[f.name] = v;
13624             }
13625             var record = new recordType(values, id);
13626             record.json = n;
13627             records[records.length] = record;
13628         }
13629         return {
13630             records : records,
13631             totalRecords : records.length
13632         };
13633     },
13634     // used when loading children.. @see loadDataFromChildren
13635     toLoadData: function(rec)
13636     {
13637         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13638         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13639         
13640     }
13641     
13642     
13643 });/*
13644  * - LGPL
13645  * * 
13646  */
13647
13648 /**
13649  * @class Roo.bootstrap.ComboBox
13650  * @extends Roo.bootstrap.TriggerField
13651  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13652  * @cfg {Boolean} append (true|false) default false
13653  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13654  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13655  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13656  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13657  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13658  * @cfg {Boolean} animate default true
13659  * @cfg {Boolean} emptyResultText only for touch device
13660  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13661  * @cfg {String} emptyTitle default ''
13662  * @constructor
13663  * Create a new ComboBox.
13664  * @param {Object} config Configuration options
13665  */
13666 Roo.bootstrap.ComboBox = function(config){
13667     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13668     this.addEvents({
13669         /**
13670          * @event expand
13671          * Fires when the dropdown list is expanded
13672         * @param {Roo.bootstrap.ComboBox} combo This combo box
13673         */
13674         'expand' : true,
13675         /**
13676          * @event collapse
13677          * Fires when the dropdown list is collapsed
13678         * @param {Roo.bootstrap.ComboBox} combo This combo box
13679         */
13680         'collapse' : true,
13681         /**
13682          * @event beforeselect
13683          * Fires before a list item is selected. Return false to cancel the selection.
13684         * @param {Roo.bootstrap.ComboBox} combo This combo box
13685         * @param {Roo.data.Record} record The data record returned from the underlying store
13686         * @param {Number} index The index of the selected item in the dropdown list
13687         */
13688         'beforeselect' : true,
13689         /**
13690          * @event select
13691          * Fires when a list item is selected
13692         * @param {Roo.bootstrap.ComboBox} combo This combo box
13693         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13694         * @param {Number} index The index of the selected item in the dropdown list
13695         */
13696         'select' : true,
13697         /**
13698          * @event beforequery
13699          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13700          * The event object passed has these properties:
13701         * @param {Roo.bootstrap.ComboBox} combo This combo box
13702         * @param {String} query The query
13703         * @param {Boolean} forceAll true to force "all" query
13704         * @param {Boolean} cancel true to cancel the query
13705         * @param {Object} e The query event object
13706         */
13707         'beforequery': true,
13708          /**
13709          * @event add
13710          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13711         * @param {Roo.bootstrap.ComboBox} combo This combo box
13712         */
13713         'add' : true,
13714         /**
13715          * @event edit
13716          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13717         * @param {Roo.bootstrap.ComboBox} combo This combo box
13718         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13719         */
13720         'edit' : true,
13721         /**
13722          * @event remove
13723          * Fires when the remove value from the combobox array
13724         * @param {Roo.bootstrap.ComboBox} combo This combo box
13725         */
13726         'remove' : true,
13727         /**
13728          * @event afterremove
13729          * Fires when the remove value from the combobox array
13730         * @param {Roo.bootstrap.ComboBox} combo This combo box
13731         */
13732         'afterremove' : true,
13733         /**
13734          * @event specialfilter
13735          * Fires when specialfilter
13736             * @param {Roo.bootstrap.ComboBox} combo This combo box
13737             */
13738         'specialfilter' : true,
13739         /**
13740          * @event tick
13741          * Fires when tick the element
13742             * @param {Roo.bootstrap.ComboBox} combo This combo box
13743             */
13744         'tick' : true,
13745         /**
13746          * @event touchviewdisplay
13747          * Fires when touch view require special display (default is using displayField)
13748             * @param {Roo.bootstrap.ComboBox} combo This combo box
13749             * @param {Object} cfg set html .
13750             */
13751         'touchviewdisplay' : true
13752         
13753     });
13754     
13755     this.item = [];
13756     this.tickItems = [];
13757     
13758     this.selectedIndex = -1;
13759     if(this.mode == 'local'){
13760         if(config.queryDelay === undefined){
13761             this.queryDelay = 10;
13762         }
13763         if(config.minChars === undefined){
13764             this.minChars = 0;
13765         }
13766     }
13767 };
13768
13769 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13770      
13771     /**
13772      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13773      * rendering into an Roo.Editor, defaults to false)
13774      */
13775     /**
13776      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13777      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13778      */
13779     /**
13780      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13781      */
13782     /**
13783      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13784      * the dropdown list (defaults to undefined, with no header element)
13785      */
13786
13787      /**
13788      * @cfg {String/Roo.Template} tpl The template to use to render the output
13789      */
13790      
13791      /**
13792      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13793      */
13794     listWidth: undefined,
13795     /**
13796      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13797      * mode = 'remote' or 'text' if mode = 'local')
13798      */
13799     displayField: undefined,
13800     
13801     /**
13802      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13803      * mode = 'remote' or 'value' if mode = 'local'). 
13804      * Note: use of a valueField requires the user make a selection
13805      * in order for a value to be mapped.
13806      */
13807     valueField: undefined,
13808     /**
13809      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13810      */
13811     modalTitle : '',
13812     
13813     /**
13814      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13815      * field's data value (defaults to the underlying DOM element's name)
13816      */
13817     hiddenName: undefined,
13818     /**
13819      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13820      */
13821     listClass: '',
13822     /**
13823      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13824      */
13825     selectedClass: 'active',
13826     
13827     /**
13828      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13829      */
13830     shadow:'sides',
13831     /**
13832      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13833      * anchor positions (defaults to 'tl-bl')
13834      */
13835     listAlign: 'tl-bl?',
13836     /**
13837      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13838      */
13839     maxHeight: 300,
13840     /**
13841      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13842      * query specified by the allQuery config option (defaults to 'query')
13843      */
13844     triggerAction: 'query',
13845     /**
13846      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13847      * (defaults to 4, does not apply if editable = false)
13848      */
13849     minChars : 4,
13850     /**
13851      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13852      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13853      */
13854     typeAhead: false,
13855     /**
13856      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13857      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13858      */
13859     queryDelay: 500,
13860     /**
13861      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13862      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13863      */
13864     pageSize: 0,
13865     /**
13866      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13867      * when editable = true (defaults to false)
13868      */
13869     selectOnFocus:false,
13870     /**
13871      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13872      */
13873     queryParam: 'query',
13874     /**
13875      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13876      * when mode = 'remote' (defaults to 'Loading...')
13877      */
13878     loadingText: 'Loading...',
13879     /**
13880      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13881      */
13882     resizable: false,
13883     /**
13884      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13885      */
13886     handleHeight : 8,
13887     /**
13888      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13889      * traditional select (defaults to true)
13890      */
13891     editable: true,
13892     /**
13893      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13894      */
13895     allQuery: '',
13896     /**
13897      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13898      */
13899     mode: 'remote',
13900     /**
13901      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13902      * listWidth has a higher value)
13903      */
13904     minListWidth : 70,
13905     /**
13906      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13907      * allow the user to set arbitrary text into the field (defaults to false)
13908      */
13909     forceSelection:false,
13910     /**
13911      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13912      * if typeAhead = true (defaults to 250)
13913      */
13914     typeAheadDelay : 250,
13915     /**
13916      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13917      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13918      */
13919     valueNotFoundText : undefined,
13920     /**
13921      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13922      */
13923     blockFocus : false,
13924     
13925     /**
13926      * @cfg {Boolean} disableClear Disable showing of clear button.
13927      */
13928     disableClear : false,
13929     /**
13930      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13931      */
13932     alwaysQuery : false,
13933     
13934     /**
13935      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13936      */
13937     multiple : false,
13938     
13939     /**
13940      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13941      */
13942     invalidClass : "has-warning",
13943     
13944     /**
13945      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13946      */
13947     validClass : "has-success",
13948     
13949     /**
13950      * @cfg {Boolean} specialFilter (true|false) special filter default false
13951      */
13952     specialFilter : false,
13953     
13954     /**
13955      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13956      */
13957     mobileTouchView : true,
13958     
13959     /**
13960      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13961      */
13962     useNativeIOS : false,
13963     
13964     /**
13965      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13966      */
13967     mobile_restrict_height : false,
13968     
13969     ios_options : false,
13970     
13971     //private
13972     addicon : false,
13973     editicon: false,
13974     
13975     page: 0,
13976     hasQuery: false,
13977     append: false,
13978     loadNext: false,
13979     autoFocus : true,
13980     tickable : false,
13981     btnPosition : 'right',
13982     triggerList : true,
13983     showToggleBtn : true,
13984     animate : true,
13985     emptyResultText: 'Empty',
13986     triggerText : 'Select',
13987     emptyTitle : '',
13988     
13989     // element that contains real text value.. (when hidden is used..)
13990     
13991     getAutoCreate : function()
13992     {   
13993         var cfg = false;
13994         //render
13995         /*
13996          * Render classic select for iso
13997          */
13998         
13999         if(Roo.isIOS && this.useNativeIOS){
14000             cfg = this.getAutoCreateNativeIOS();
14001             return cfg;
14002         }
14003         
14004         /*
14005          * Touch Devices
14006          */
14007         
14008         if(Roo.isTouch && this.mobileTouchView){
14009             cfg = this.getAutoCreateTouchView();
14010             return cfg;;
14011         }
14012         
14013         /*
14014          *  Normal ComboBox
14015          */
14016         if(!this.tickable){
14017             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
14018             return cfg;
14019         }
14020         
14021         /*
14022          *  ComboBox with tickable selections
14023          */
14024              
14025         var align = this.labelAlign || this.parentLabelAlign();
14026         
14027         cfg = {
14028             cls : 'form-group roo-combobox-tickable' //input-group
14029         };
14030         
14031         var btn_text_select = '';
14032         var btn_text_done = '';
14033         var btn_text_cancel = '';
14034         
14035         if (this.btn_text_show) {
14036             btn_text_select = 'Select';
14037             btn_text_done = 'Done';
14038             btn_text_cancel = 'Cancel'; 
14039         }
14040         
14041         var buttons = {
14042             tag : 'div',
14043             cls : 'tickable-buttons',
14044             cn : [
14045                 {
14046                     tag : 'button',
14047                     type : 'button',
14048                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
14049                     //html : this.triggerText
14050                     html: btn_text_select
14051                 },
14052                 {
14053                     tag : 'button',
14054                     type : 'button',
14055                     name : 'ok',
14056                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
14057                     //html : 'Done'
14058                     html: btn_text_done
14059                 },
14060                 {
14061                     tag : 'button',
14062                     type : 'button',
14063                     name : 'cancel',
14064                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
14065                     //html : 'Cancel'
14066                     html: btn_text_cancel
14067                 }
14068             ]
14069         };
14070         
14071         if(this.editable){
14072             buttons.cn.unshift({
14073                 tag: 'input',
14074                 cls: 'roo-select2-search-field-input'
14075             });
14076         }
14077         
14078         var _this = this;
14079         
14080         Roo.each(buttons.cn, function(c){
14081             if (_this.size) {
14082                 c.cls += ' btn-' + _this.size;
14083             }
14084
14085             if (_this.disabled) {
14086                 c.disabled = true;
14087             }
14088         });
14089         
14090         var box = {
14091             tag: 'div',
14092             style : 'display: contents',
14093             cn: [
14094                 {
14095                     tag: 'input',
14096                     type : 'hidden',
14097                     cls: 'form-hidden-field'
14098                 },
14099                 {
14100                     tag: 'ul',
14101                     cls: 'roo-select2-choices',
14102                     cn:[
14103                         {
14104                             tag: 'li',
14105                             cls: 'roo-select2-search-field',
14106                             cn: [
14107                                 buttons
14108                             ]
14109                         }
14110                     ]
14111                 }
14112             ]
14113         };
14114         
14115         var combobox = {
14116             cls: 'roo-select2-container input-group roo-select2-container-multi',
14117             cn: [
14118                 
14119                 box
14120 //                {
14121 //                    tag: 'ul',
14122 //                    cls: 'typeahead typeahead-long dropdown-menu',
14123 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
14124 //                }
14125             ]
14126         };
14127         
14128         if(this.hasFeedback && !this.allowBlank){
14129             
14130             var feedback = {
14131                 tag: 'span',
14132                 cls: 'glyphicon form-control-feedback'
14133             };
14134
14135             combobox.cn.push(feedback);
14136         }
14137         
14138         var indicator = {
14139             tag : 'i',
14140             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
14141             tooltip : 'This field is required'
14142         };
14143         if (Roo.bootstrap.version == 4) {
14144             indicator = {
14145                 tag : 'i',
14146                 style : 'display:none'
14147             };
14148         }
14149         if (align ==='left' && this.fieldLabel.length) {
14150             
14151             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
14152             
14153             cfg.cn = [
14154                 indicator,
14155                 {
14156                     tag: 'label',
14157                     'for' :  id,
14158                     cls : 'control-label col-form-label',
14159                     html : this.fieldLabel
14160
14161                 },
14162                 {
14163                     cls : "", 
14164                     cn: [
14165                         combobox
14166                     ]
14167                 }
14168
14169             ];
14170             
14171             var labelCfg = cfg.cn[1];
14172             var contentCfg = cfg.cn[2];
14173             
14174
14175             if(this.indicatorpos == 'right'){
14176                 
14177                 cfg.cn = [
14178                     {
14179                         tag: 'label',
14180                         'for' :  id,
14181                         cls : 'control-label col-form-label',
14182                         cn : [
14183                             {
14184                                 tag : 'span',
14185                                 html : this.fieldLabel
14186                             },
14187                             indicator
14188                         ]
14189                     },
14190                     {
14191                         cls : "",
14192                         cn: [
14193                             combobox
14194                         ]
14195                     }
14196
14197                 ];
14198                 
14199                 
14200                 
14201                 labelCfg = cfg.cn[0];
14202                 contentCfg = cfg.cn[1];
14203             
14204             }
14205             
14206             if(this.labelWidth > 12){
14207                 labelCfg.style = "width: " + this.labelWidth + 'px';
14208             }
14209             
14210             if(this.labelWidth < 13 && this.labelmd == 0){
14211                 this.labelmd = this.labelWidth;
14212             }
14213             
14214             if(this.labellg > 0){
14215                 labelCfg.cls += ' col-lg-' + this.labellg;
14216                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14217             }
14218             
14219             if(this.labelmd > 0){
14220                 labelCfg.cls += ' col-md-' + this.labelmd;
14221                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14222             }
14223             
14224             if(this.labelsm > 0){
14225                 labelCfg.cls += ' col-sm-' + this.labelsm;
14226                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14227             }
14228             
14229             if(this.labelxs > 0){
14230                 labelCfg.cls += ' col-xs-' + this.labelxs;
14231                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14232             }
14233                 
14234                 
14235         } else if ( this.fieldLabel.length) {
14236 //                Roo.log(" label");
14237                  cfg.cn = [
14238                    indicator,
14239                     {
14240                         tag: 'label',
14241                         //cls : 'input-group-addon',
14242                         html : this.fieldLabel
14243                     },
14244                     combobox
14245                 ];
14246                 
14247                 if(this.indicatorpos == 'right'){
14248                     cfg.cn = [
14249                         {
14250                             tag: 'label',
14251                             //cls : 'input-group-addon',
14252                             html : this.fieldLabel
14253                         },
14254                         indicator,
14255                         combobox
14256                     ];
14257                     
14258                 }
14259
14260         } else {
14261             
14262 //                Roo.log(" no label && no align");
14263                 cfg = combobox
14264                      
14265                 
14266         }
14267          
14268         var settings=this;
14269         ['xs','sm','md','lg'].map(function(size){
14270             if (settings[size]) {
14271                 cfg.cls += ' col-' + size + '-' + settings[size];
14272             }
14273         });
14274         
14275         return cfg;
14276         
14277     },
14278     
14279     _initEventsCalled : false,
14280     
14281     // private
14282     initEvents: function()
14283     {   
14284         if (this._initEventsCalled) { // as we call render... prevent looping...
14285             return;
14286         }
14287         this._initEventsCalled = true;
14288         
14289         if (!this.store) {
14290             throw "can not find store for combo";
14291         }
14292         
14293         this.indicator = this.indicatorEl();
14294         
14295         this.store = Roo.factory(this.store, Roo.data);
14296         this.store.parent = this;
14297         
14298         // if we are building from html. then this element is so complex, that we can not really
14299         // use the rendered HTML.
14300         // so we have to trash and replace the previous code.
14301         if (Roo.XComponent.build_from_html) {
14302             // remove this element....
14303             var e = this.el.dom, k=0;
14304             while (e ) { e = e.previousSibling;  ++k;}
14305
14306             this.el.remove();
14307             
14308             this.el=false;
14309             this.rendered = false;
14310             
14311             this.render(this.parent().getChildContainer(true), k);
14312         }
14313         
14314         if(Roo.isIOS && this.useNativeIOS){
14315             this.initIOSView();
14316             return;
14317         }
14318         
14319         /*
14320          * Touch Devices
14321          */
14322         
14323         if(Roo.isTouch && this.mobileTouchView){
14324             this.initTouchView();
14325             return;
14326         }
14327         
14328         if(this.tickable){
14329             this.initTickableEvents();
14330             return;
14331         }
14332         
14333         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
14334         
14335         if(this.hiddenName){
14336             
14337             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14338             
14339             this.hiddenField.dom.value =
14340                 this.hiddenValue !== undefined ? this.hiddenValue :
14341                 this.value !== undefined ? this.value : '';
14342
14343             // prevent input submission
14344             this.el.dom.removeAttribute('name');
14345             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14346              
14347              
14348         }
14349         //if(Roo.isGecko){
14350         //    this.el.dom.setAttribute('autocomplete', 'off');
14351         //}
14352         
14353         var cls = 'x-combo-list';
14354         
14355         //this.list = new Roo.Layer({
14356         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
14357         //});
14358         
14359         var _this = this;
14360         
14361         (function(){
14362             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14363             _this.list.setWidth(lw);
14364         }).defer(100);
14365         
14366         this.list.on('mouseover', this.onViewOver, this);
14367         this.list.on('mousemove', this.onViewMove, this);
14368         this.list.on('scroll', this.onViewScroll, this);
14369         
14370         /*
14371         this.list.swallowEvent('mousewheel');
14372         this.assetHeight = 0;
14373
14374         if(this.title){
14375             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
14376             this.assetHeight += this.header.getHeight();
14377         }
14378
14379         this.innerList = this.list.createChild({cls:cls+'-inner'});
14380         this.innerList.on('mouseover', this.onViewOver, this);
14381         this.innerList.on('mousemove', this.onViewMove, this);
14382         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14383         
14384         if(this.allowBlank && !this.pageSize && !this.disableClear){
14385             this.footer = this.list.createChild({cls:cls+'-ft'});
14386             this.pageTb = new Roo.Toolbar(this.footer);
14387            
14388         }
14389         if(this.pageSize){
14390             this.footer = this.list.createChild({cls:cls+'-ft'});
14391             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
14392                     {pageSize: this.pageSize});
14393             
14394         }
14395         
14396         if (this.pageTb && this.allowBlank && !this.disableClear) {
14397             var _this = this;
14398             this.pageTb.add(new Roo.Toolbar.Fill(), {
14399                 cls: 'x-btn-icon x-btn-clear',
14400                 text: '&#160;',
14401                 handler: function()
14402                 {
14403                     _this.collapse();
14404                     _this.clearValue();
14405                     _this.onSelect(false, -1);
14406                 }
14407             });
14408         }
14409         if (this.footer) {
14410             this.assetHeight += this.footer.getHeight();
14411         }
14412         */
14413             
14414         if(!this.tpl){
14415             this.tpl = Roo.bootstrap.version == 4 ?
14416                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
14417                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
14418         }
14419
14420         this.view = new Roo.View(this.list, this.tpl, {
14421             singleSelect:true, store: this.store, selectedClass: this.selectedClass
14422         });
14423         //this.view.wrapEl.setDisplayed(false);
14424         this.view.on('click', this.onViewClick, this);
14425         
14426         
14427         this.store.on('beforeload', this.onBeforeLoad, this);
14428         this.store.on('load', this.onLoad, this);
14429         this.store.on('loadexception', this.onLoadException, this);
14430         /*
14431         if(this.resizable){
14432             this.resizer = new Roo.Resizable(this.list,  {
14433                pinned:true, handles:'se'
14434             });
14435             this.resizer.on('resize', function(r, w, h){
14436                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
14437                 this.listWidth = w;
14438                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
14439                 this.restrictHeight();
14440             }, this);
14441             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
14442         }
14443         */
14444         if(!this.editable){
14445             this.editable = true;
14446             this.setEditable(false);
14447         }
14448         
14449         /*
14450         
14451         if (typeof(this.events.add.listeners) != 'undefined') {
14452             
14453             this.addicon = this.wrap.createChild(
14454                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
14455        
14456             this.addicon.on('click', function(e) {
14457                 this.fireEvent('add', this);
14458             }, this);
14459         }
14460         if (typeof(this.events.edit.listeners) != 'undefined') {
14461             
14462             this.editicon = this.wrap.createChild(
14463                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
14464             if (this.addicon) {
14465                 this.editicon.setStyle('margin-left', '40px');
14466             }
14467             this.editicon.on('click', function(e) {
14468                 
14469                 // we fire even  if inothing is selected..
14470                 this.fireEvent('edit', this, this.lastData );
14471                 
14472             }, this);
14473         }
14474         */
14475         
14476         this.keyNav = new Roo.KeyNav(this.inputEl(), {
14477             "up" : function(e){
14478                 this.inKeyMode = true;
14479                 this.selectPrev();
14480             },
14481
14482             "down" : function(e){
14483                 if(!this.isExpanded()){
14484                     this.onTriggerClick();
14485                 }else{
14486                     this.inKeyMode = true;
14487                     this.selectNext();
14488                 }
14489             },
14490
14491             "enter" : function(e){
14492 //                this.onViewClick();
14493                 //return true;
14494                 this.collapse();
14495                 
14496                 if(this.fireEvent("specialkey", this, e)){
14497                     this.onViewClick(false);
14498                 }
14499                 
14500                 return true;
14501             },
14502
14503             "esc" : function(e){
14504                 this.collapse();
14505             },
14506
14507             "tab" : function(e){
14508                 this.collapse();
14509                 
14510                 if(this.fireEvent("specialkey", this, e)){
14511                     this.onViewClick(false);
14512                 }
14513                 
14514                 return true;
14515             },
14516
14517             scope : this,
14518
14519             doRelay : function(foo, bar, hname){
14520                 if(hname == 'down' || this.scope.isExpanded()){
14521                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14522                 }
14523                 return true;
14524             },
14525
14526             forceKeyDown: true
14527         });
14528         
14529         
14530         this.queryDelay = Math.max(this.queryDelay || 10,
14531                 this.mode == 'local' ? 10 : 250);
14532         
14533         
14534         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14535         
14536         if(this.typeAhead){
14537             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14538         }
14539         if(this.editable !== false){
14540             this.inputEl().on("keyup", this.onKeyUp, this);
14541         }
14542         if(this.forceSelection){
14543             this.inputEl().on('blur', this.doForce, this);
14544         }
14545         
14546         if(this.multiple){
14547             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14548             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14549         }
14550     },
14551     
14552     initTickableEvents: function()
14553     {   
14554         this.createList();
14555         
14556         if(this.hiddenName){
14557             
14558             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14559             
14560             this.hiddenField.dom.value =
14561                 this.hiddenValue !== undefined ? this.hiddenValue :
14562                 this.value !== undefined ? this.value : '';
14563
14564             // prevent input submission
14565             this.el.dom.removeAttribute('name');
14566             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14567              
14568              
14569         }
14570         
14571 //        this.list = this.el.select('ul.dropdown-menu',true).first();
14572         
14573         this.choices = this.el.select('ul.roo-select2-choices', true).first();
14574         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14575         if(this.triggerList){
14576             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14577         }
14578          
14579         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14580         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14581         
14582         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14583         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14584         
14585         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14586         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14587         
14588         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14589         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14590         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14591         
14592         this.okBtn.hide();
14593         this.cancelBtn.hide();
14594         
14595         var _this = this;
14596         
14597         (function(){
14598             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14599             _this.list.setWidth(lw);
14600         }).defer(100);
14601         
14602         this.list.on('mouseover', this.onViewOver, this);
14603         this.list.on('mousemove', this.onViewMove, this);
14604         
14605         this.list.on('scroll', this.onViewScroll, this);
14606         
14607         if(!this.tpl){
14608             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
14609                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14610         }
14611
14612         this.view = new Roo.View(this.list, this.tpl, {
14613             singleSelect:true,
14614             tickable:true,
14615             parent:this,
14616             store: this.store,
14617             selectedClass: this.selectedClass
14618         });
14619         
14620         //this.view.wrapEl.setDisplayed(false);
14621         this.view.on('click', this.onViewClick, this);
14622         
14623         
14624         
14625         this.store.on('beforeload', this.onBeforeLoad, this);
14626         this.store.on('load', this.onLoad, this);
14627         this.store.on('loadexception', this.onLoadException, this);
14628         
14629         if(this.editable){
14630             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14631                 "up" : function(e){
14632                     this.inKeyMode = true;
14633                     this.selectPrev();
14634                 },
14635
14636                 "down" : function(e){
14637                     this.inKeyMode = true;
14638                     this.selectNext();
14639                 },
14640
14641                 "enter" : function(e){
14642                     if(this.fireEvent("specialkey", this, e)){
14643                         this.onViewClick(false);
14644                     }
14645                     
14646                     return true;
14647                 },
14648
14649                 "esc" : function(e){
14650                     this.onTickableFooterButtonClick(e, false, false);
14651                 },
14652
14653                 "tab" : function(e){
14654                     this.fireEvent("specialkey", this, e);
14655                     
14656                     this.onTickableFooterButtonClick(e, false, false);
14657                     
14658                     return true;
14659                 },
14660
14661                 scope : this,
14662
14663                 doRelay : function(e, fn, key){
14664                     if(this.scope.isExpanded()){
14665                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14666                     }
14667                     return true;
14668                 },
14669
14670                 forceKeyDown: true
14671             });
14672         }
14673         
14674         this.queryDelay = Math.max(this.queryDelay || 10,
14675                 this.mode == 'local' ? 10 : 250);
14676         
14677         
14678         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14679         
14680         if(this.typeAhead){
14681             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14682         }
14683         
14684         if(this.editable !== false){
14685             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14686         }
14687         
14688         this.indicator = this.indicatorEl();
14689         
14690         if(this.indicator){
14691             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14692             this.indicator.hide();
14693         }
14694         
14695     },
14696
14697     onDestroy : function(){
14698         if(this.view){
14699             this.view.setStore(null);
14700             this.view.el.removeAllListeners();
14701             this.view.el.remove();
14702             this.view.purgeListeners();
14703         }
14704         if(this.list){
14705             this.list.dom.innerHTML  = '';
14706         }
14707         
14708         if(this.store){
14709             this.store.un('beforeload', this.onBeforeLoad, this);
14710             this.store.un('load', this.onLoad, this);
14711             this.store.un('loadexception', this.onLoadException, this);
14712         }
14713         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14714     },
14715
14716     // private
14717     fireKey : function(e){
14718         if(e.isNavKeyPress() && !this.list.isVisible()){
14719             this.fireEvent("specialkey", this, e);
14720         }
14721     },
14722
14723     // private
14724     onResize: function(w, h){
14725 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14726 //        
14727 //        if(typeof w != 'number'){
14728 //            // we do not handle it!?!?
14729 //            return;
14730 //        }
14731 //        var tw = this.trigger.getWidth();
14732 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14733 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14734 //        var x = w - tw;
14735 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14736 //            
14737 //        //this.trigger.setStyle('left', x+'px');
14738 //        
14739 //        if(this.list && this.listWidth === undefined){
14740 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14741 //            this.list.setWidth(lw);
14742 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14743 //        }
14744         
14745     
14746         
14747     },
14748
14749     /**
14750      * Allow or prevent the user from directly editing the field text.  If false is passed,
14751      * the user will only be able to select from the items defined in the dropdown list.  This method
14752      * is the runtime equivalent of setting the 'editable' config option at config time.
14753      * @param {Boolean} value True to allow the user to directly edit the field text
14754      */
14755     setEditable : function(value){
14756         if(value == this.editable){
14757             return;
14758         }
14759         this.editable = value;
14760         if(!value){
14761             this.inputEl().dom.setAttribute('readOnly', true);
14762             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14763             this.inputEl().addClass('x-combo-noedit');
14764         }else{
14765             this.inputEl().dom.setAttribute('readOnly', false);
14766             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14767             this.inputEl().removeClass('x-combo-noedit');
14768         }
14769     },
14770
14771     // private
14772     
14773     onBeforeLoad : function(combo,opts){
14774         if(!this.hasFocus){
14775             return;
14776         }
14777          if (!opts.add) {
14778             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14779          }
14780         this.restrictHeight();
14781         this.selectedIndex = -1;
14782     },
14783
14784     // private
14785     onLoad : function(){
14786         
14787         this.hasQuery = false;
14788         
14789         if(!this.hasFocus){
14790             return;
14791         }
14792         
14793         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14794             this.loading.hide();
14795         }
14796         
14797         if(this.store.getCount() > 0){
14798             
14799             this.expand();
14800             this.restrictHeight();
14801             if(this.lastQuery == this.allQuery){
14802                 if(this.editable && !this.tickable){
14803                     this.inputEl().dom.select();
14804                 }
14805                 
14806                 if(
14807                     !this.selectByValue(this.value, true) &&
14808                     this.autoFocus && 
14809                     (
14810                         !this.store.lastOptions ||
14811                         typeof(this.store.lastOptions.add) == 'undefined' || 
14812                         this.store.lastOptions.add != true
14813                     )
14814                 ){
14815                     this.select(0, true);
14816                 }
14817             }else{
14818                 if(this.autoFocus){
14819                     this.selectNext();
14820                 }
14821                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14822                     this.taTask.delay(this.typeAheadDelay);
14823                 }
14824             }
14825         }else{
14826             this.onEmptyResults();
14827         }
14828         
14829         //this.el.focus();
14830     },
14831     // private
14832     onLoadException : function()
14833     {
14834         this.hasQuery = false;
14835         
14836         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14837             this.loading.hide();
14838         }
14839         
14840         if(this.tickable && this.editable){
14841             return;
14842         }
14843         
14844         this.collapse();
14845         // only causes errors at present
14846         //Roo.log(this.store.reader.jsonData);
14847         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14848             // fixme
14849             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14850         //}
14851         
14852         
14853     },
14854     // private
14855     onTypeAhead : function(){
14856         if(this.store.getCount() > 0){
14857             var r = this.store.getAt(0);
14858             var newValue = r.data[this.displayField];
14859             var len = newValue.length;
14860             var selStart = this.getRawValue().length;
14861             
14862             if(selStart != len){
14863                 this.setRawValue(newValue);
14864                 this.selectText(selStart, newValue.length);
14865             }
14866         }
14867     },
14868
14869     // private
14870     onSelect : function(record, index){
14871         
14872         if(this.fireEvent('beforeselect', this, record, index) !== false){
14873         
14874             this.setFromData(index > -1 ? record.data : false);
14875             
14876             this.collapse();
14877             this.fireEvent('select', this, record, index);
14878         }
14879     },
14880
14881     /**
14882      * Returns the currently selected field value or empty string if no value is set.
14883      * @return {String} value The selected value
14884      */
14885     getValue : function()
14886     {
14887         if(Roo.isIOS && this.useNativeIOS){
14888             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14889         }
14890         
14891         if(this.multiple){
14892             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14893         }
14894         
14895         if(this.valueField){
14896             return typeof this.value != 'undefined' ? this.value : '';
14897         }else{
14898             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14899         }
14900     },
14901     
14902     getRawValue : function()
14903     {
14904         if(Roo.isIOS && this.useNativeIOS){
14905             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14906         }
14907         
14908         var v = this.inputEl().getValue();
14909         
14910         return v;
14911     },
14912
14913     /**
14914      * Clears any text/value currently set in the field
14915      */
14916     clearValue : function(){
14917         
14918         if(this.hiddenField){
14919             this.hiddenField.dom.value = '';
14920         }
14921         this.value = '';
14922         this.setRawValue('');
14923         this.lastSelectionText = '';
14924         this.lastData = false;
14925         
14926         var close = this.closeTriggerEl();
14927         
14928         if(close){
14929             close.hide();
14930         }
14931         
14932         this.validate();
14933         
14934     },
14935
14936     /**
14937      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14938      * will be displayed in the field.  If the value does not match the data value of an existing item,
14939      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14940      * Otherwise the field will be blank (although the value will still be set).
14941      * @param {String} value The value to match
14942      */
14943     setValue : function(v)
14944     {
14945         if(Roo.isIOS && this.useNativeIOS){
14946             this.setIOSValue(v);
14947             return;
14948         }
14949         
14950         if(this.multiple){
14951             this.syncValue();
14952             return;
14953         }
14954         
14955         var text = v;
14956         if(this.valueField){
14957             var r = this.findRecord(this.valueField, v);
14958             if(r){
14959                 text = r.data[this.displayField];
14960             }else if(this.valueNotFoundText !== undefined){
14961                 text = this.valueNotFoundText;
14962             }
14963         }
14964         this.lastSelectionText = text;
14965         if(this.hiddenField){
14966             this.hiddenField.dom.value = v;
14967         }
14968         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14969         this.value = v;
14970         
14971         var close = this.closeTriggerEl();
14972         
14973         if(close){
14974             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14975         }
14976         
14977         this.validate();
14978     },
14979     /**
14980      * @property {Object} the last set data for the element
14981      */
14982     
14983     lastData : false,
14984     /**
14985      * Sets the value of the field based on a object which is related to the record format for the store.
14986      * @param {Object} value the value to set as. or false on reset?
14987      */
14988     setFromData : function(o){
14989         
14990         if(this.multiple){
14991             this.addItem(o);
14992             return;
14993         }
14994             
14995         var dv = ''; // display value
14996         var vv = ''; // value value..
14997         this.lastData = o;
14998         if (this.displayField) {
14999             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15000         } else {
15001             // this is an error condition!!!
15002             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15003         }
15004         
15005         if(this.valueField){
15006             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
15007         }
15008         
15009         var close = this.closeTriggerEl();
15010         
15011         if(close){
15012             if(dv.length || vv * 1 > 0){
15013                 close.show() ;
15014                 this.blockFocus=true;
15015             } else {
15016                 close.hide();
15017             }             
15018         }
15019         
15020         if(this.hiddenField){
15021             this.hiddenField.dom.value = vv;
15022             
15023             this.lastSelectionText = dv;
15024             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
15025             this.value = vv;
15026             return;
15027         }
15028         // no hidden field.. - we store the value in 'value', but still display
15029         // display field!!!!
15030         this.lastSelectionText = dv;
15031         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
15032         this.value = vv;
15033         
15034         
15035         
15036     },
15037     // private
15038     reset : function(){
15039         // overridden so that last data is reset..
15040         
15041         if(this.multiple){
15042             this.clearItem();
15043             return;
15044         }
15045         
15046         this.setValue(this.originalValue);
15047         //this.clearInvalid();
15048         this.lastData = false;
15049         if (this.view) {
15050             this.view.clearSelections();
15051         }
15052         
15053         this.validate();
15054     },
15055     // private
15056     findRecord : function(prop, value){
15057         var record;
15058         if(this.store.getCount() > 0){
15059             this.store.each(function(r){
15060                 if(r.data[prop] == value){
15061                     record = r;
15062                     return false;
15063                 }
15064                 return true;
15065             });
15066         }
15067         return record;
15068     },
15069     
15070     getName: function()
15071     {
15072         // returns hidden if it's set..
15073         if (!this.rendered) {return ''};
15074         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
15075         
15076     },
15077     // private
15078     onViewMove : function(e, t){
15079         this.inKeyMode = false;
15080     },
15081
15082     // private
15083     onViewOver : function(e, t){
15084         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
15085             return;
15086         }
15087         var item = this.view.findItemFromChild(t);
15088         
15089         if(item){
15090             var index = this.view.indexOf(item);
15091             this.select(index, false);
15092         }
15093     },
15094
15095     // private
15096     onViewClick : function(view, doFocus, el, e)
15097     {
15098         var index = this.view.getSelectedIndexes()[0];
15099         
15100         var r = this.store.getAt(index);
15101         
15102         if(this.tickable){
15103             
15104             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
15105                 return;
15106             }
15107             
15108             var rm = false;
15109             var _this = this;
15110             
15111             Roo.each(this.tickItems, function(v,k){
15112                 
15113                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
15114                     Roo.log(v);
15115                     _this.tickItems.splice(k, 1);
15116                     
15117                     if(typeof(e) == 'undefined' && view == false){
15118                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
15119                     }
15120                     
15121                     rm = true;
15122                     return;
15123                 }
15124             });
15125             
15126             if(rm){
15127                 return;
15128             }
15129             
15130             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
15131                 this.tickItems.push(r.data);
15132             }
15133             
15134             if(typeof(e) == 'undefined' && view == false){
15135                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
15136             }
15137                     
15138             return;
15139         }
15140         
15141         if(r){
15142             this.onSelect(r, index);
15143         }
15144         if(doFocus !== false && !this.blockFocus){
15145             this.inputEl().focus();
15146         }
15147     },
15148
15149     // private
15150     restrictHeight : function(){
15151         //this.innerList.dom.style.height = '';
15152         //var inner = this.innerList.dom;
15153         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
15154         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
15155         //this.list.beginUpdate();
15156         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
15157         this.list.alignTo(this.inputEl(), this.listAlign);
15158         this.list.alignTo(this.inputEl(), this.listAlign);
15159         //this.list.endUpdate();
15160     },
15161
15162     // private
15163     onEmptyResults : function(){
15164         
15165         if(this.tickable && this.editable){
15166             this.hasFocus = false;
15167             this.restrictHeight();
15168             return;
15169         }
15170         
15171         this.collapse();
15172     },
15173
15174     /**
15175      * Returns true if the dropdown list is expanded, else false.
15176      */
15177     isExpanded : function(){
15178         return this.list.isVisible();
15179     },
15180
15181     /**
15182      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
15183      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15184      * @param {String} value The data value of the item to select
15185      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15186      * selected item if it is not currently in view (defaults to true)
15187      * @return {Boolean} True if the value matched an item in the list, else false
15188      */
15189     selectByValue : function(v, scrollIntoView){
15190         if(v !== undefined && v !== null){
15191             var r = this.findRecord(this.valueField || this.displayField, v);
15192             if(r){
15193                 this.select(this.store.indexOf(r), scrollIntoView);
15194                 return true;
15195             }
15196         }
15197         return false;
15198     },
15199
15200     /**
15201      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
15202      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15203      * @param {Number} index The zero-based index of the list item to select
15204      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15205      * selected item if it is not currently in view (defaults to true)
15206      */
15207     select : function(index, scrollIntoView){
15208         this.selectedIndex = index;
15209         this.view.select(index);
15210         if(scrollIntoView !== false){
15211             var el = this.view.getNode(index);
15212             /*
15213              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
15214              */
15215             if(el){
15216                 this.list.scrollChildIntoView(el, false);
15217             }
15218         }
15219     },
15220
15221     // private
15222     selectNext : function(){
15223         var ct = this.store.getCount();
15224         if(ct > 0){
15225             if(this.selectedIndex == -1){
15226                 this.select(0);
15227             }else if(this.selectedIndex < ct-1){
15228                 this.select(this.selectedIndex+1);
15229             }
15230         }
15231     },
15232
15233     // private
15234     selectPrev : function(){
15235         var ct = this.store.getCount();
15236         if(ct > 0){
15237             if(this.selectedIndex == -1){
15238                 this.select(0);
15239             }else if(this.selectedIndex != 0){
15240                 this.select(this.selectedIndex-1);
15241             }
15242         }
15243     },
15244
15245     // private
15246     onKeyUp : function(e){
15247         if(this.editable !== false && !e.isSpecialKey()){
15248             this.lastKey = e.getKey();
15249             this.dqTask.delay(this.queryDelay);
15250         }
15251     },
15252
15253     // private
15254     validateBlur : function(){
15255         return !this.list || !this.list.isVisible();   
15256     },
15257
15258     // private
15259     initQuery : function(){
15260         
15261         var v = this.getRawValue();
15262         
15263         if(this.tickable && this.editable){
15264             v = this.tickableInputEl().getValue();
15265         }
15266         
15267         this.doQuery(v);
15268     },
15269
15270     // private
15271     doForce : function(){
15272         if(this.inputEl().dom.value.length > 0){
15273             this.inputEl().dom.value =
15274                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
15275              
15276         }
15277     },
15278
15279     /**
15280      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
15281      * query allowing the query action to be canceled if needed.
15282      * @param {String} query The SQL query to execute
15283      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
15284      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
15285      * saved in the current store (defaults to false)
15286      */
15287     doQuery : function(q, forceAll){
15288         
15289         if(q === undefined || q === null){
15290             q = '';
15291         }
15292         var qe = {
15293             query: q,
15294             forceAll: forceAll,
15295             combo: this,
15296             cancel:false
15297         };
15298         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
15299             return false;
15300         }
15301         q = qe.query;
15302         
15303         forceAll = qe.forceAll;
15304         if(forceAll === true || (q.length >= this.minChars)){
15305             
15306             this.hasQuery = true;
15307             
15308             if(this.lastQuery != q || this.alwaysQuery){
15309                 this.lastQuery = q;
15310                 if(this.mode == 'local'){
15311                     this.selectedIndex = -1;
15312                     if(forceAll){
15313                         this.store.clearFilter();
15314                     }else{
15315                         
15316                         if(this.specialFilter){
15317                             this.fireEvent('specialfilter', this);
15318                             this.onLoad();
15319                             return;
15320                         }
15321                         
15322                         this.store.filter(this.displayField, q);
15323                     }
15324                     
15325                     this.store.fireEvent("datachanged", this.store);
15326                     
15327                     this.onLoad();
15328                     
15329                     
15330                 }else{
15331                     
15332                     this.store.baseParams[this.queryParam] = q;
15333                     
15334                     var options = {params : this.getParams(q)};
15335                     
15336                     if(this.loadNext){
15337                         options.add = true;
15338                         options.params.start = this.page * this.pageSize;
15339                     }
15340                     
15341                     this.store.load(options);
15342                     
15343                     /*
15344                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
15345                      *  we should expand the list on onLoad
15346                      *  so command out it
15347                      */
15348 //                    this.expand();
15349                 }
15350             }else{
15351                 this.selectedIndex = -1;
15352                 this.onLoad();   
15353             }
15354         }
15355         
15356         this.loadNext = false;
15357     },
15358     
15359     // private
15360     getParams : function(q){
15361         var p = {};
15362         //p[this.queryParam] = q;
15363         
15364         if(this.pageSize){
15365             p.start = 0;
15366             p.limit = this.pageSize;
15367         }
15368         return p;
15369     },
15370
15371     /**
15372      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
15373      */
15374     collapse : function(){
15375         if(!this.isExpanded()){
15376             return;
15377         }
15378         
15379         this.list.hide();
15380         
15381         this.hasFocus = false;
15382         
15383         if(this.tickable){
15384             this.okBtn.hide();
15385             this.cancelBtn.hide();
15386             this.trigger.show();
15387             
15388             if(this.editable){
15389                 this.tickableInputEl().dom.value = '';
15390                 this.tickableInputEl().blur();
15391             }
15392             
15393         }
15394         
15395         Roo.get(document).un('mousedown', this.collapseIf, this);
15396         Roo.get(document).un('mousewheel', this.collapseIf, this);
15397         if (!this.editable) {
15398             Roo.get(document).un('keydown', this.listKeyPress, this);
15399         }
15400         this.fireEvent('collapse', this);
15401         
15402         this.validate();
15403     },
15404
15405     // private
15406     collapseIf : function(e){
15407         var in_combo  = e.within(this.el);
15408         var in_list =  e.within(this.list);
15409         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
15410         
15411         if (in_combo || in_list || is_list) {
15412             //e.stopPropagation();
15413             return;
15414         }
15415         
15416         if(this.tickable){
15417             this.onTickableFooterButtonClick(e, false, false);
15418         }
15419
15420         this.collapse();
15421         
15422     },
15423
15424     /**
15425      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
15426      */
15427     expand : function(){
15428        
15429         if(this.isExpanded() || !this.hasFocus){
15430             return;
15431         }
15432         
15433         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
15434         this.list.setWidth(lw);
15435         
15436         Roo.log('expand');
15437         
15438         this.list.show();
15439         
15440         this.restrictHeight();
15441         
15442         if(this.tickable){
15443             
15444             this.tickItems = Roo.apply([], this.item);
15445             
15446             this.okBtn.show();
15447             this.cancelBtn.show();
15448             this.trigger.hide();
15449             
15450             if(this.editable){
15451                 this.tickableInputEl().focus();
15452             }
15453             
15454         }
15455         
15456         Roo.get(document).on('mousedown', this.collapseIf, this);
15457         Roo.get(document).on('mousewheel', this.collapseIf, this);
15458         if (!this.editable) {
15459             Roo.get(document).on('keydown', this.listKeyPress, this);
15460         }
15461         
15462         this.fireEvent('expand', this);
15463     },
15464
15465     // private
15466     // Implements the default empty TriggerField.onTriggerClick function
15467     onTriggerClick : function(e)
15468     {
15469         Roo.log('trigger click');
15470         
15471         if(this.disabled || !this.triggerList){
15472             return;
15473         }
15474         
15475         this.page = 0;
15476         this.loadNext = false;
15477         
15478         if(this.isExpanded()){
15479             this.collapse();
15480             if (!this.blockFocus) {
15481                 this.inputEl().focus();
15482             }
15483             
15484         }else {
15485             this.hasFocus = true;
15486             if(this.triggerAction == 'all') {
15487                 this.doQuery(this.allQuery, true);
15488             } else {
15489                 this.doQuery(this.getRawValue());
15490             }
15491             if (!this.blockFocus) {
15492                 this.inputEl().focus();
15493             }
15494         }
15495     },
15496     
15497     onTickableTriggerClick : function(e)
15498     {
15499         if(this.disabled){
15500             return;
15501         }
15502         
15503         this.page = 0;
15504         this.loadNext = false;
15505         this.hasFocus = true;
15506         
15507         if(this.triggerAction == 'all') {
15508             this.doQuery(this.allQuery, true);
15509         } else {
15510             this.doQuery(this.getRawValue());
15511         }
15512     },
15513     
15514     onSearchFieldClick : function(e)
15515     {
15516         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
15517             this.onTickableFooterButtonClick(e, false, false);
15518             return;
15519         }
15520         
15521         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15522             return;
15523         }
15524         
15525         this.page = 0;
15526         this.loadNext = false;
15527         this.hasFocus = true;
15528         
15529         if(this.triggerAction == 'all') {
15530             this.doQuery(this.allQuery, true);
15531         } else {
15532             this.doQuery(this.getRawValue());
15533         }
15534     },
15535     
15536     listKeyPress : function(e)
15537     {
15538         //Roo.log('listkeypress');
15539         // scroll to first matching element based on key pres..
15540         if (e.isSpecialKey()) {
15541             return false;
15542         }
15543         var k = String.fromCharCode(e.getKey()).toUpperCase();
15544         //Roo.log(k);
15545         var match  = false;
15546         var csel = this.view.getSelectedNodes();
15547         var cselitem = false;
15548         if (csel.length) {
15549             var ix = this.view.indexOf(csel[0]);
15550             cselitem  = this.store.getAt(ix);
15551             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15552                 cselitem = false;
15553             }
15554             
15555         }
15556         
15557         this.store.each(function(v) { 
15558             if (cselitem) {
15559                 // start at existing selection.
15560                 if (cselitem.id == v.id) {
15561                     cselitem = false;
15562                 }
15563                 return true;
15564             }
15565                 
15566             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15567                 match = this.store.indexOf(v);
15568                 return false;
15569             }
15570             return true;
15571         }, this);
15572         
15573         if (match === false) {
15574             return true; // no more action?
15575         }
15576         // scroll to?
15577         this.view.select(match);
15578         var sn = Roo.get(this.view.getSelectedNodes()[0]);
15579         sn.scrollIntoView(sn.dom.parentNode, false);
15580     },
15581     
15582     onViewScroll : function(e, t){
15583         
15584         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){
15585             return;
15586         }
15587         
15588         this.hasQuery = true;
15589         
15590         this.loading = this.list.select('.loading', true).first();
15591         
15592         if(this.loading === null){
15593             this.list.createChild({
15594                 tag: 'div',
15595                 cls: 'loading roo-select2-more-results roo-select2-active',
15596                 html: 'Loading more results...'
15597             });
15598             
15599             this.loading = this.list.select('.loading', true).first();
15600             
15601             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15602             
15603             this.loading.hide();
15604         }
15605         
15606         this.loading.show();
15607         
15608         var _combo = this;
15609         
15610         this.page++;
15611         this.loadNext = true;
15612         
15613         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15614         
15615         return;
15616     },
15617     
15618     addItem : function(o)
15619     {   
15620         var dv = ''; // display value
15621         
15622         if (this.displayField) {
15623             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15624         } else {
15625             // this is an error condition!!!
15626             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15627         }
15628         
15629         if(!dv.length){
15630             return;
15631         }
15632         
15633         var choice = this.choices.createChild({
15634             tag: 'li',
15635             cls: 'roo-select2-search-choice',
15636             cn: [
15637                 {
15638                     tag: 'div',
15639                     html: dv
15640                 },
15641                 {
15642                     tag: 'a',
15643                     href: '#',
15644                     cls: 'roo-select2-search-choice-close fa fa-times',
15645                     tabindex: '-1'
15646                 }
15647             ]
15648             
15649         }, this.searchField);
15650         
15651         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15652         
15653         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15654         
15655         this.item.push(o);
15656         
15657         this.lastData = o;
15658         
15659         this.syncValue();
15660         
15661         this.inputEl().dom.value = '';
15662         
15663         this.validate();
15664     },
15665     
15666     onRemoveItem : function(e, _self, o)
15667     {
15668         e.preventDefault();
15669         
15670         this.lastItem = Roo.apply([], this.item);
15671         
15672         var index = this.item.indexOf(o.data) * 1;
15673         
15674         if( index < 0){
15675             Roo.log('not this item?!');
15676             return;
15677         }
15678         
15679         this.item.splice(index, 1);
15680         o.item.remove();
15681         
15682         this.syncValue();
15683         
15684         this.fireEvent('remove', this, e);
15685         
15686         this.validate();
15687         
15688     },
15689     
15690     syncValue : function()
15691     {
15692         if(!this.item.length){
15693             this.clearValue();
15694             return;
15695         }
15696             
15697         var value = [];
15698         var _this = this;
15699         Roo.each(this.item, function(i){
15700             if(_this.valueField){
15701                 value.push(i[_this.valueField]);
15702                 return;
15703             }
15704
15705             value.push(i);
15706         });
15707
15708         this.value = value.join(',');
15709
15710         if(this.hiddenField){
15711             this.hiddenField.dom.value = this.value;
15712         }
15713         
15714         this.store.fireEvent("datachanged", this.store);
15715         
15716         this.validate();
15717     },
15718     
15719     clearItem : function()
15720     {
15721         if(!this.multiple){
15722             return;
15723         }
15724         
15725         this.item = [];
15726         
15727         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15728            c.remove();
15729         });
15730         
15731         this.syncValue();
15732         
15733         this.validate();
15734         
15735         if(this.tickable && !Roo.isTouch){
15736             this.view.refresh();
15737         }
15738     },
15739     
15740     inputEl: function ()
15741     {
15742         if(Roo.isIOS && this.useNativeIOS){
15743             return this.el.select('select.roo-ios-select', true).first();
15744         }
15745         
15746         if(Roo.isTouch && this.mobileTouchView){
15747             return this.el.select('input.form-control',true).first();
15748         }
15749         
15750         if(this.tickable){
15751             return this.searchField;
15752         }
15753         
15754         return this.el.select('input.form-control',true).first();
15755     },
15756     
15757     onTickableFooterButtonClick : function(e, btn, el)
15758     {
15759         e.preventDefault();
15760         
15761         this.lastItem = Roo.apply([], this.item);
15762         
15763         if(btn && btn.name == 'cancel'){
15764             this.tickItems = Roo.apply([], this.item);
15765             this.collapse();
15766             return;
15767         }
15768         
15769         this.clearItem();
15770         
15771         var _this = this;
15772         
15773         Roo.each(this.tickItems, function(o){
15774             _this.addItem(o);
15775         });
15776         
15777         this.collapse();
15778         
15779     },
15780     
15781     validate : function()
15782     {
15783         if(this.getVisibilityEl().hasClass('hidden')){
15784             return true;
15785         }
15786         
15787         var v = this.getRawValue();
15788         
15789         if(this.multiple){
15790             v = this.getValue();
15791         }
15792         
15793         if(this.disabled || this.allowBlank || v.length){
15794             this.markValid();
15795             return true;
15796         }
15797         
15798         this.markInvalid();
15799         return false;
15800     },
15801     
15802     tickableInputEl : function()
15803     {
15804         if(!this.tickable || !this.editable){
15805             return this.inputEl();
15806         }
15807         
15808         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15809     },
15810     
15811     
15812     getAutoCreateTouchView : function()
15813     {
15814         var id = Roo.id();
15815         
15816         var cfg = {
15817             cls: 'form-group' //input-group
15818         };
15819         
15820         var input =  {
15821             tag: 'input',
15822             id : id,
15823             type : this.inputType,
15824             cls : 'form-control x-combo-noedit',
15825             autocomplete: 'new-password',
15826             placeholder : this.placeholder || '',
15827             readonly : true
15828         };
15829         
15830         if (this.name) {
15831             input.name = this.name;
15832         }
15833         
15834         if (this.size) {
15835             input.cls += ' input-' + this.size;
15836         }
15837         
15838         if (this.disabled) {
15839             input.disabled = true;
15840         }
15841         
15842         var inputblock = {
15843             cls : '',
15844             cn : [
15845                 input
15846             ]
15847         };
15848         
15849         if(this.before){
15850             inputblock.cls += ' input-group';
15851             
15852             inputblock.cn.unshift({
15853                 tag :'span',
15854                 cls : 'input-group-addon input-group-prepend input-group-text',
15855                 html : this.before
15856             });
15857         }
15858         
15859         if(this.removable && !this.multiple){
15860             inputblock.cls += ' roo-removable';
15861             
15862             inputblock.cn.push({
15863                 tag: 'button',
15864                 html : 'x',
15865                 cls : 'roo-combo-removable-btn close'
15866             });
15867         }
15868
15869         if(this.hasFeedback && !this.allowBlank){
15870             
15871             inputblock.cls += ' has-feedback';
15872             
15873             inputblock.cn.push({
15874                 tag: 'span',
15875                 cls: 'glyphicon form-control-feedback'
15876             });
15877             
15878         }
15879         
15880         if (this.after) {
15881             
15882             inputblock.cls += (this.before) ? '' : ' input-group';
15883             
15884             inputblock.cn.push({
15885                 tag :'span',
15886                 cls : 'input-group-addon input-group-append input-group-text',
15887                 html : this.after
15888             });
15889         }
15890
15891         
15892         var ibwrap = inputblock;
15893         
15894         if(this.multiple){
15895             ibwrap = {
15896                 tag: 'ul',
15897                 cls: 'roo-select2-choices',
15898                 cn:[
15899                     {
15900                         tag: 'li',
15901                         cls: 'roo-select2-search-field',
15902                         cn: [
15903
15904                             inputblock
15905                         ]
15906                     }
15907                 ]
15908             };
15909         
15910             
15911         }
15912         
15913         var combobox = {
15914             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15915             cn: [
15916                 {
15917                     tag: 'input',
15918                     type : 'hidden',
15919                     cls: 'form-hidden-field'
15920                 },
15921                 ibwrap
15922             ]
15923         };
15924         
15925         if(!this.multiple && this.showToggleBtn){
15926             
15927             var caret = {
15928                 cls: 'caret'
15929             };
15930             
15931             if (this.caret != false) {
15932                 caret = {
15933                      tag: 'i',
15934                      cls: 'fa fa-' + this.caret
15935                 };
15936                 
15937             }
15938             
15939             combobox.cn.push({
15940                 tag :'span',
15941                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15942                 cn : [
15943                     Roo.bootstrap.version == 3 ? caret : '',
15944                     {
15945                         tag: 'span',
15946                         cls: 'combobox-clear',
15947                         cn  : [
15948                             {
15949                                 tag : 'i',
15950                                 cls: 'icon-remove'
15951                             }
15952                         ]
15953                     }
15954                 ]
15955
15956             })
15957         }
15958         
15959         if(this.multiple){
15960             combobox.cls += ' roo-select2-container-multi';
15961         }
15962         
15963         var align = this.labelAlign || this.parentLabelAlign();
15964         
15965         if (align ==='left' && this.fieldLabel.length) {
15966
15967             cfg.cn = [
15968                 {
15969                    tag : 'i',
15970                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15971                    tooltip : 'This field is required'
15972                 },
15973                 {
15974                     tag: 'label',
15975                     cls : 'control-label col-form-label',
15976                     html : this.fieldLabel
15977
15978                 },
15979                 {
15980                     cls : '', 
15981                     cn: [
15982                         combobox
15983                     ]
15984                 }
15985             ];
15986             
15987             var labelCfg = cfg.cn[1];
15988             var contentCfg = cfg.cn[2];
15989             
15990
15991             if(this.indicatorpos == 'right'){
15992                 cfg.cn = [
15993                     {
15994                         tag: 'label',
15995                         'for' :  id,
15996                         cls : 'control-label col-form-label',
15997                         cn : [
15998                             {
15999                                 tag : 'span',
16000                                 html : this.fieldLabel
16001                             },
16002                             {
16003                                 tag : 'i',
16004                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
16005                                 tooltip : 'This field is required'
16006                             }
16007                         ]
16008                     },
16009                     {
16010                         cls : "",
16011                         cn: [
16012                             combobox
16013                         ]
16014                     }
16015
16016                 ];
16017                 
16018                 labelCfg = cfg.cn[0];
16019                 contentCfg = cfg.cn[1];
16020             }
16021             
16022            
16023             
16024             if(this.labelWidth > 12){
16025                 labelCfg.style = "width: " + this.labelWidth + 'px';
16026             }
16027             
16028             if(this.labelWidth < 13 && this.labelmd == 0){
16029                 this.labelmd = this.labelWidth;
16030             }
16031             
16032             if(this.labellg > 0){
16033                 labelCfg.cls += ' col-lg-' + this.labellg;
16034                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16035             }
16036             
16037             if(this.labelmd > 0){
16038                 labelCfg.cls += ' col-md-' + this.labelmd;
16039                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16040             }
16041             
16042             if(this.labelsm > 0){
16043                 labelCfg.cls += ' col-sm-' + this.labelsm;
16044                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16045             }
16046             
16047             if(this.labelxs > 0){
16048                 labelCfg.cls += ' col-xs-' + this.labelxs;
16049                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16050             }
16051                 
16052                 
16053         } else if ( this.fieldLabel.length) {
16054             cfg.cn = [
16055                 {
16056                    tag : 'i',
16057                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
16058                    tooltip : 'This field is required'
16059                 },
16060                 {
16061                     tag: 'label',
16062                     cls : 'control-label',
16063                     html : this.fieldLabel
16064
16065                 },
16066                 {
16067                     cls : '', 
16068                     cn: [
16069                         combobox
16070                     ]
16071                 }
16072             ];
16073             
16074             if(this.indicatorpos == 'right'){
16075                 cfg.cn = [
16076                     {
16077                         tag: 'label',
16078                         cls : 'control-label',
16079                         html : this.fieldLabel,
16080                         cn : [
16081                             {
16082                                tag : 'i',
16083                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
16084                                tooltip : 'This field is required'
16085                             }
16086                         ]
16087                     },
16088                     {
16089                         cls : '', 
16090                         cn: [
16091                             combobox
16092                         ]
16093                     }
16094                 ];
16095             }
16096         } else {
16097             cfg.cn = combobox;    
16098         }
16099         
16100         
16101         var settings = this;
16102         
16103         ['xs','sm','md','lg'].map(function(size){
16104             if (settings[size]) {
16105                 cfg.cls += ' col-' + size + '-' + settings[size];
16106             }
16107         });
16108         
16109         return cfg;
16110     },
16111     
16112     initTouchView : function()
16113     {
16114         this.renderTouchView();
16115         
16116         this.touchViewEl.on('scroll', function(){
16117             this.el.dom.scrollTop = 0;
16118         }, this);
16119         
16120         this.originalValue = this.getValue();
16121         
16122         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
16123         
16124         this.inputEl().on("click", this.showTouchView, this);
16125         if (this.triggerEl) {
16126             this.triggerEl.on("click", this.showTouchView, this);
16127         }
16128         
16129         
16130         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
16131         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
16132         
16133         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
16134         
16135         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
16136         this.store.on('load', this.onTouchViewLoad, this);
16137         this.store.on('loadexception', this.onTouchViewLoadException, this);
16138         
16139         if(this.hiddenName){
16140             
16141             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16142             
16143             this.hiddenField.dom.value =
16144                 this.hiddenValue !== undefined ? this.hiddenValue :
16145                 this.value !== undefined ? this.value : '';
16146         
16147             this.el.dom.removeAttribute('name');
16148             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16149         }
16150         
16151         if(this.multiple){
16152             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16153             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16154         }
16155         
16156         if(this.removable && !this.multiple){
16157             var close = this.closeTriggerEl();
16158             if(close){
16159                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
16160                 close.on('click', this.removeBtnClick, this, close);
16161             }
16162         }
16163         /*
16164          * fix the bug in Safari iOS8
16165          */
16166         this.inputEl().on("focus", function(e){
16167             document.activeElement.blur();
16168         }, this);
16169         
16170         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
16171         
16172         return;
16173         
16174         
16175     },
16176     
16177     renderTouchView : function()
16178     {
16179         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
16180         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16181         
16182         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
16183         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16184         
16185         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
16186         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16187         this.touchViewBodyEl.setStyle('overflow', 'auto');
16188         
16189         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
16190         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16191         
16192         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
16193         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16194         
16195     },
16196     
16197     showTouchView : function()
16198     {
16199         if(this.disabled){
16200             return;
16201         }
16202         
16203         this.touchViewHeaderEl.hide();
16204
16205         if(this.modalTitle.length){
16206             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
16207             this.touchViewHeaderEl.show();
16208         }
16209
16210         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
16211         this.touchViewEl.show();
16212
16213         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
16214         
16215         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
16216         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
16217
16218         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16219
16220         if(this.modalTitle.length){
16221             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16222         }
16223         
16224         this.touchViewBodyEl.setHeight(bodyHeight);
16225
16226         if(this.animate){
16227             var _this = this;
16228             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
16229         }else{
16230             this.touchViewEl.addClass('in');
16231         }
16232         
16233         if(this._touchViewMask){
16234             Roo.get(document.body).addClass("x-body-masked");
16235             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
16236             this._touchViewMask.setStyle('z-index', 10000);
16237             this._touchViewMask.addClass('show');
16238         }
16239         
16240         this.doTouchViewQuery();
16241         
16242     },
16243     
16244     hideTouchView : function()
16245     {
16246         this.touchViewEl.removeClass('in');
16247
16248         if(this.animate){
16249             var _this = this;
16250             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
16251         }else{
16252             this.touchViewEl.setStyle('display', 'none');
16253         }
16254         
16255         if(this._touchViewMask){
16256             this._touchViewMask.removeClass('show');
16257             Roo.get(document.body).removeClass("x-body-masked");
16258         }
16259     },
16260     
16261     setTouchViewValue : function()
16262     {
16263         if(this.multiple){
16264             this.clearItem();
16265         
16266             var _this = this;
16267
16268             Roo.each(this.tickItems, function(o){
16269                 this.addItem(o);
16270             }, this);
16271         }
16272         
16273         this.hideTouchView();
16274     },
16275     
16276     doTouchViewQuery : function()
16277     {
16278         var qe = {
16279             query: '',
16280             forceAll: true,
16281             combo: this,
16282             cancel:false
16283         };
16284         
16285         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
16286             return false;
16287         }
16288         
16289         if(!this.alwaysQuery || this.mode == 'local'){
16290             this.onTouchViewLoad();
16291             return;
16292         }
16293         
16294         this.store.load();
16295     },
16296     
16297     onTouchViewBeforeLoad : function(combo,opts)
16298     {
16299         return;
16300     },
16301
16302     // private
16303     onTouchViewLoad : function()
16304     {
16305         if(this.store.getCount() < 1){
16306             this.onTouchViewEmptyResults();
16307             return;
16308         }
16309         
16310         this.clearTouchView();
16311         
16312         var rawValue = this.getRawValue();
16313         
16314         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
16315         
16316         this.tickItems = [];
16317         
16318         this.store.data.each(function(d, rowIndex){
16319             var row = this.touchViewListGroup.createChild(template);
16320             
16321             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
16322                 row.addClass(d.data.cls);
16323             }
16324             
16325             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16326                 var cfg = {
16327                     data : d.data,
16328                     html : d.data[this.displayField]
16329                 };
16330                 
16331                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
16332                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
16333                 }
16334             }
16335             row.removeClass('selected');
16336             if(!this.multiple && this.valueField &&
16337                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
16338             {
16339                 // radio buttons..
16340                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16341                 row.addClass('selected');
16342             }
16343             
16344             if(this.multiple && this.valueField &&
16345                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
16346             {
16347                 
16348                 // checkboxes...
16349                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16350                 this.tickItems.push(d.data);
16351             }
16352             
16353             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
16354             
16355         }, this);
16356         
16357         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
16358         
16359         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16360
16361         if(this.modalTitle.length){
16362             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16363         }
16364
16365         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
16366         
16367         if(this.mobile_restrict_height && listHeight < bodyHeight){
16368             this.touchViewBodyEl.setHeight(listHeight);
16369         }
16370         
16371         var _this = this;
16372         
16373         if(firstChecked && listHeight > bodyHeight){
16374             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
16375         }
16376         
16377     },
16378     
16379     onTouchViewLoadException : function()
16380     {
16381         this.hideTouchView();
16382     },
16383     
16384     onTouchViewEmptyResults : function()
16385     {
16386         this.clearTouchView();
16387         
16388         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
16389         
16390         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
16391         
16392     },
16393     
16394     clearTouchView : function()
16395     {
16396         this.touchViewListGroup.dom.innerHTML = '';
16397     },
16398     
16399     onTouchViewClick : function(e, el, o)
16400     {
16401         e.preventDefault();
16402         
16403         var row = o.row;
16404         var rowIndex = o.rowIndex;
16405         
16406         var r = this.store.getAt(rowIndex);
16407         
16408         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
16409             
16410             if(!this.multiple){
16411                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
16412                     c.dom.removeAttribute('checked');
16413                 }, this);
16414
16415                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16416
16417                 this.setFromData(r.data);
16418
16419                 var close = this.closeTriggerEl();
16420
16421                 if(close){
16422                     close.show();
16423                 }
16424
16425                 this.hideTouchView();
16426
16427                 this.fireEvent('select', this, r, rowIndex);
16428
16429                 return;
16430             }
16431
16432             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
16433                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
16434                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
16435                 return;
16436             }
16437
16438             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16439             this.addItem(r.data);
16440             this.tickItems.push(r.data);
16441         }
16442     },
16443     
16444     getAutoCreateNativeIOS : function()
16445     {
16446         var cfg = {
16447             cls: 'form-group' //input-group,
16448         };
16449         
16450         var combobox =  {
16451             tag: 'select',
16452             cls : 'roo-ios-select'
16453         };
16454         
16455         if (this.name) {
16456             combobox.name = this.name;
16457         }
16458         
16459         if (this.disabled) {
16460             combobox.disabled = true;
16461         }
16462         
16463         var settings = this;
16464         
16465         ['xs','sm','md','lg'].map(function(size){
16466             if (settings[size]) {
16467                 cfg.cls += ' col-' + size + '-' + settings[size];
16468             }
16469         });
16470         
16471         cfg.cn = combobox;
16472         
16473         return cfg;
16474         
16475     },
16476     
16477     initIOSView : function()
16478     {
16479         this.store.on('load', this.onIOSViewLoad, this);
16480         
16481         return;
16482     },
16483     
16484     onIOSViewLoad : function()
16485     {
16486         if(this.store.getCount() < 1){
16487             return;
16488         }
16489         
16490         this.clearIOSView();
16491         
16492         if(this.allowBlank) {
16493             
16494             var default_text = '-- SELECT --';
16495             
16496             if(this.placeholder.length){
16497                 default_text = this.placeholder;
16498             }
16499             
16500             if(this.emptyTitle.length){
16501                 default_text += ' - ' + this.emptyTitle + ' -';
16502             }
16503             
16504             var opt = this.inputEl().createChild({
16505                 tag: 'option',
16506                 value : 0,
16507                 html : default_text
16508             });
16509             
16510             var o = {};
16511             o[this.valueField] = 0;
16512             o[this.displayField] = default_text;
16513             
16514             this.ios_options.push({
16515                 data : o,
16516                 el : opt
16517             });
16518             
16519         }
16520         
16521         this.store.data.each(function(d, rowIndex){
16522             
16523             var html = '';
16524             
16525             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16526                 html = d.data[this.displayField];
16527             }
16528             
16529             var value = '';
16530             
16531             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16532                 value = d.data[this.valueField];
16533             }
16534             
16535             var option = {
16536                 tag: 'option',
16537                 value : value,
16538                 html : html
16539             };
16540             
16541             if(this.value == d.data[this.valueField]){
16542                 option['selected'] = true;
16543             }
16544             
16545             var opt = this.inputEl().createChild(option);
16546             
16547             this.ios_options.push({
16548                 data : d.data,
16549                 el : opt
16550             });
16551             
16552         }, this);
16553         
16554         this.inputEl().on('change', function(){
16555            this.fireEvent('select', this);
16556         }, this);
16557         
16558     },
16559     
16560     clearIOSView: function()
16561     {
16562         this.inputEl().dom.innerHTML = '';
16563         
16564         this.ios_options = [];
16565     },
16566     
16567     setIOSValue: function(v)
16568     {
16569         this.value = v;
16570         
16571         if(!this.ios_options){
16572             return;
16573         }
16574         
16575         Roo.each(this.ios_options, function(opts){
16576            
16577            opts.el.dom.removeAttribute('selected');
16578            
16579            if(opts.data[this.valueField] != v){
16580                return;
16581            }
16582            
16583            opts.el.dom.setAttribute('selected', true);
16584            
16585         }, this);
16586     }
16587
16588     /** 
16589     * @cfg {Boolean} grow 
16590     * @hide 
16591     */
16592     /** 
16593     * @cfg {Number} growMin 
16594     * @hide 
16595     */
16596     /** 
16597     * @cfg {Number} growMax 
16598     * @hide 
16599     */
16600     /**
16601      * @hide
16602      * @method autoSize
16603      */
16604 });
16605
16606 Roo.apply(Roo.bootstrap.ComboBox,  {
16607     
16608     header : {
16609         tag: 'div',
16610         cls: 'modal-header',
16611         cn: [
16612             {
16613                 tag: 'h4',
16614                 cls: 'modal-title'
16615             }
16616         ]
16617     },
16618     
16619     body : {
16620         tag: 'div',
16621         cls: 'modal-body',
16622         cn: [
16623             {
16624                 tag: 'ul',
16625                 cls: 'list-group'
16626             }
16627         ]
16628     },
16629     
16630     listItemRadio : {
16631         tag: 'li',
16632         cls: 'list-group-item',
16633         cn: [
16634             {
16635                 tag: 'span',
16636                 cls: 'roo-combobox-list-group-item-value'
16637             },
16638             {
16639                 tag: 'div',
16640                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16641                 cn: [
16642                     {
16643                         tag: 'input',
16644                         type: 'radio'
16645                     },
16646                     {
16647                         tag: 'label'
16648                     }
16649                 ]
16650             }
16651         ]
16652     },
16653     
16654     listItemCheckbox : {
16655         tag: 'li',
16656         cls: 'list-group-item',
16657         cn: [
16658             {
16659                 tag: 'span',
16660                 cls: 'roo-combobox-list-group-item-value'
16661             },
16662             {
16663                 tag: 'div',
16664                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16665                 cn: [
16666                     {
16667                         tag: 'input',
16668                         type: 'checkbox'
16669                     },
16670                     {
16671                         tag: 'label'
16672                     }
16673                 ]
16674             }
16675         ]
16676     },
16677     
16678     emptyResult : {
16679         tag: 'div',
16680         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16681     },
16682     
16683     footer : {
16684         tag: 'div',
16685         cls: 'modal-footer',
16686         cn: [
16687             {
16688                 tag: 'div',
16689                 cls: 'row',
16690                 cn: [
16691                     {
16692                         tag: 'div',
16693                         cls: 'col-xs-6 text-left',
16694                         cn: {
16695                             tag: 'button',
16696                             cls: 'btn btn-danger roo-touch-view-cancel',
16697                             html: 'Cancel'
16698                         }
16699                     },
16700                     {
16701                         tag: 'div',
16702                         cls: 'col-xs-6 text-right',
16703                         cn: {
16704                             tag: 'button',
16705                             cls: 'btn btn-success roo-touch-view-ok',
16706                             html: 'OK'
16707                         }
16708                     }
16709                 ]
16710             }
16711         ]
16712         
16713     }
16714 });
16715
16716 Roo.apply(Roo.bootstrap.ComboBox,  {
16717     
16718     touchViewTemplate : {
16719         tag: 'div',
16720         cls: 'modal fade roo-combobox-touch-view',
16721         cn: [
16722             {
16723                 tag: 'div',
16724                 cls: 'modal-dialog',
16725                 style : 'position:fixed', // we have to fix position....
16726                 cn: [
16727                     {
16728                         tag: 'div',
16729                         cls: 'modal-content',
16730                         cn: [
16731                             Roo.bootstrap.ComboBox.header,
16732                             Roo.bootstrap.ComboBox.body,
16733                             Roo.bootstrap.ComboBox.footer
16734                         ]
16735                     }
16736                 ]
16737             }
16738         ]
16739     }
16740 });/*
16741  * Based on:
16742  * Ext JS Library 1.1.1
16743  * Copyright(c) 2006-2007, Ext JS, LLC.
16744  *
16745  * Originally Released Under LGPL - original licence link has changed is not relivant.
16746  *
16747  * Fork - LGPL
16748  * <script type="text/javascript">
16749  */
16750
16751 /**
16752  * @class Roo.View
16753  * @extends Roo.util.Observable
16754  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16755  * This class also supports single and multi selection modes. <br>
16756  * Create a data model bound view:
16757  <pre><code>
16758  var store = new Roo.data.Store(...);
16759
16760  var view = new Roo.View({
16761     el : "my-element",
16762     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16763  
16764     singleSelect: true,
16765     selectedClass: "ydataview-selected",
16766     store: store
16767  });
16768
16769  // listen for node click?
16770  view.on("click", function(vw, index, node, e){
16771  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16772  });
16773
16774  // load XML data
16775  dataModel.load("foobar.xml");
16776  </code></pre>
16777  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16778  * <br><br>
16779  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16780  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16781  * 
16782  * Note: old style constructor is still suported (container, template, config)
16783  * 
16784  * @constructor
16785  * Create a new View
16786  * @param {Object} config The config object
16787  * 
16788  */
16789 Roo.View = function(config, depreciated_tpl, depreciated_config){
16790     
16791     this.parent = false;
16792     
16793     if (typeof(depreciated_tpl) == 'undefined') {
16794         // new way.. - universal constructor.
16795         Roo.apply(this, config);
16796         this.el  = Roo.get(this.el);
16797     } else {
16798         // old format..
16799         this.el  = Roo.get(config);
16800         this.tpl = depreciated_tpl;
16801         Roo.apply(this, depreciated_config);
16802     }
16803     this.wrapEl  = this.el.wrap().wrap();
16804     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16805     
16806     
16807     if(typeof(this.tpl) == "string"){
16808         this.tpl = new Roo.Template(this.tpl);
16809     } else {
16810         // support xtype ctors..
16811         this.tpl = new Roo.factory(this.tpl, Roo);
16812     }
16813     
16814     
16815     this.tpl.compile();
16816     
16817     /** @private */
16818     this.addEvents({
16819         /**
16820          * @event beforeclick
16821          * Fires before a click is processed. Returns false to cancel the default action.
16822          * @param {Roo.View} this
16823          * @param {Number} index The index of the target node
16824          * @param {HTMLElement} node The target node
16825          * @param {Roo.EventObject} e The raw event object
16826          */
16827             "beforeclick" : true,
16828         /**
16829          * @event click
16830          * Fires when a template node is clicked.
16831          * @param {Roo.View} this
16832          * @param {Number} index The index of the target node
16833          * @param {HTMLElement} node The target node
16834          * @param {Roo.EventObject} e The raw event object
16835          */
16836             "click" : true,
16837         /**
16838          * @event dblclick
16839          * Fires when a template node is double clicked.
16840          * @param {Roo.View} this
16841          * @param {Number} index The index of the target node
16842          * @param {HTMLElement} node The target node
16843          * @param {Roo.EventObject} e The raw event object
16844          */
16845             "dblclick" : true,
16846         /**
16847          * @event contextmenu
16848          * Fires when a template node is right clicked.
16849          * @param {Roo.View} this
16850          * @param {Number} index The index of the target node
16851          * @param {HTMLElement} node The target node
16852          * @param {Roo.EventObject} e The raw event object
16853          */
16854             "contextmenu" : true,
16855         /**
16856          * @event selectionchange
16857          * Fires when the selected nodes change.
16858          * @param {Roo.View} this
16859          * @param {Array} selections Array of the selected nodes
16860          */
16861             "selectionchange" : true,
16862     
16863         /**
16864          * @event beforeselect
16865          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16866          * @param {Roo.View} this
16867          * @param {HTMLElement} node The node to be selected
16868          * @param {Array} selections Array of currently selected nodes
16869          */
16870             "beforeselect" : true,
16871         /**
16872          * @event preparedata
16873          * Fires on every row to render, to allow you to change the data.
16874          * @param {Roo.View} this
16875          * @param {Object} data to be rendered (change this)
16876          */
16877           "preparedata" : true
16878           
16879           
16880         });
16881
16882
16883
16884     this.el.on({
16885         "click": this.onClick,
16886         "dblclick": this.onDblClick,
16887         "contextmenu": this.onContextMenu,
16888         scope:this
16889     });
16890
16891     this.selections = [];
16892     this.nodes = [];
16893     this.cmp = new Roo.CompositeElementLite([]);
16894     if(this.store){
16895         this.store = Roo.factory(this.store, Roo.data);
16896         this.setStore(this.store, true);
16897     }
16898     
16899     if ( this.footer && this.footer.xtype) {
16900            
16901          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16902         
16903         this.footer.dataSource = this.store;
16904         this.footer.container = fctr;
16905         this.footer = Roo.factory(this.footer, Roo);
16906         fctr.insertFirst(this.el);
16907         
16908         // this is a bit insane - as the paging toolbar seems to detach the el..
16909 //        dom.parentNode.parentNode.parentNode
16910          // they get detached?
16911     }
16912     
16913     
16914     Roo.View.superclass.constructor.call(this);
16915     
16916     
16917 };
16918
16919 Roo.extend(Roo.View, Roo.util.Observable, {
16920     
16921      /**
16922      * @cfg {Roo.data.Store} store Data store to load data from.
16923      */
16924     store : false,
16925     
16926     /**
16927      * @cfg {String|Roo.Element} el The container element.
16928      */
16929     el : '',
16930     
16931     /**
16932      * @cfg {String|Roo.Template} tpl The template used by this View 
16933      */
16934     tpl : false,
16935     /**
16936      * @cfg {String} dataName the named area of the template to use as the data area
16937      *                          Works with domtemplates roo-name="name"
16938      */
16939     dataName: false,
16940     /**
16941      * @cfg {String} selectedClass The css class to add to selected nodes
16942      */
16943     selectedClass : "x-view-selected",
16944      /**
16945      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16946      */
16947     emptyText : "",
16948     
16949     /**
16950      * @cfg {String} text to display on mask (default Loading)
16951      */
16952     mask : false,
16953     /**
16954      * @cfg {Boolean} multiSelect Allow multiple selection
16955      */
16956     multiSelect : false,
16957     /**
16958      * @cfg {Boolean} singleSelect Allow single selection
16959      */
16960     singleSelect:  false,
16961     
16962     /**
16963      * @cfg {Boolean} toggleSelect - selecting 
16964      */
16965     toggleSelect : false,
16966     
16967     /**
16968      * @cfg {Boolean} tickable - selecting 
16969      */
16970     tickable : false,
16971     
16972     /**
16973      * Returns the element this view is bound to.
16974      * @return {Roo.Element}
16975      */
16976     getEl : function(){
16977         return this.wrapEl;
16978     },
16979     
16980     
16981
16982     /**
16983      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16984      */
16985     refresh : function(){
16986         //Roo.log('refresh');
16987         var t = this.tpl;
16988         
16989         // if we are using something like 'domtemplate', then
16990         // the what gets used is:
16991         // t.applySubtemplate(NAME, data, wrapping data..)
16992         // the outer template then get' applied with
16993         //     the store 'extra data'
16994         // and the body get's added to the
16995         //      roo-name="data" node?
16996         //      <span class='roo-tpl-{name}'></span> ?????
16997         
16998         
16999         
17000         this.clearSelections();
17001         this.el.update("");
17002         var html = [];
17003         var records = this.store.getRange();
17004         if(records.length < 1) {
17005             
17006             // is this valid??  = should it render a template??
17007             
17008             this.el.update(this.emptyText);
17009             return;
17010         }
17011         var el = this.el;
17012         if (this.dataName) {
17013             this.el.update(t.apply(this.store.meta)); //????
17014             el = this.el.child('.roo-tpl-' + this.dataName);
17015         }
17016         
17017         for(var i = 0, len = records.length; i < len; i++){
17018             var data = this.prepareData(records[i].data, i, records[i]);
17019             this.fireEvent("preparedata", this, data, i, records[i]);
17020             
17021             var d = Roo.apply({}, data);
17022             
17023             if(this.tickable){
17024                 Roo.apply(d, {'roo-id' : Roo.id()});
17025                 
17026                 var _this = this;
17027             
17028                 Roo.each(this.parent.item, function(item){
17029                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
17030                         return;
17031                     }
17032                     Roo.apply(d, {'roo-data-checked' : 'checked'});
17033                 });
17034             }
17035             
17036             html[html.length] = Roo.util.Format.trim(
17037                 this.dataName ?
17038                     t.applySubtemplate(this.dataName, d, this.store.meta) :
17039                     t.apply(d)
17040             );
17041         }
17042         
17043         
17044         
17045         el.update(html.join(""));
17046         this.nodes = el.dom.childNodes;
17047         this.updateIndexes(0);
17048     },
17049     
17050
17051     /**
17052      * Function to override to reformat the data that is sent to
17053      * the template for each node.
17054      * DEPRICATED - use the preparedata event handler.
17055      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
17056      * a JSON object for an UpdateManager bound view).
17057      */
17058     prepareData : function(data, index, record)
17059     {
17060         this.fireEvent("preparedata", this, data, index, record);
17061         return data;
17062     },
17063
17064     onUpdate : function(ds, record){
17065         // Roo.log('on update');   
17066         this.clearSelections();
17067         var index = this.store.indexOf(record);
17068         var n = this.nodes[index];
17069         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
17070         n.parentNode.removeChild(n);
17071         this.updateIndexes(index, index);
17072     },
17073
17074     
17075     
17076 // --------- FIXME     
17077     onAdd : function(ds, records, index)
17078     {
17079         //Roo.log(['on Add', ds, records, index] );        
17080         this.clearSelections();
17081         if(this.nodes.length == 0){
17082             this.refresh();
17083             return;
17084         }
17085         var n = this.nodes[index];
17086         for(var i = 0, len = records.length; i < len; i++){
17087             var d = this.prepareData(records[i].data, i, records[i]);
17088             if(n){
17089                 this.tpl.insertBefore(n, d);
17090             }else{
17091                 
17092                 this.tpl.append(this.el, d);
17093             }
17094         }
17095         this.updateIndexes(index);
17096     },
17097
17098     onRemove : function(ds, record, index){
17099        // Roo.log('onRemove');
17100         this.clearSelections();
17101         var el = this.dataName  ?
17102             this.el.child('.roo-tpl-' + this.dataName) :
17103             this.el; 
17104         
17105         el.dom.removeChild(this.nodes[index]);
17106         this.updateIndexes(index);
17107     },
17108
17109     /**
17110      * Refresh an individual node.
17111      * @param {Number} index
17112      */
17113     refreshNode : function(index){
17114         this.onUpdate(this.store, this.store.getAt(index));
17115     },
17116
17117     updateIndexes : function(startIndex, endIndex){
17118         var ns = this.nodes;
17119         startIndex = startIndex || 0;
17120         endIndex = endIndex || ns.length - 1;
17121         for(var i = startIndex; i <= endIndex; i++){
17122             ns[i].nodeIndex = i;
17123         }
17124     },
17125
17126     /**
17127      * Changes the data store this view uses and refresh the view.
17128      * @param {Store} store
17129      */
17130     setStore : function(store, initial){
17131         if(!initial && this.store){
17132             this.store.un("datachanged", this.refresh);
17133             this.store.un("add", this.onAdd);
17134             this.store.un("remove", this.onRemove);
17135             this.store.un("update", this.onUpdate);
17136             this.store.un("clear", this.refresh);
17137             this.store.un("beforeload", this.onBeforeLoad);
17138             this.store.un("load", this.onLoad);
17139             this.store.un("loadexception", this.onLoad);
17140         }
17141         if(store){
17142           
17143             store.on("datachanged", this.refresh, this);
17144             store.on("add", this.onAdd, this);
17145             store.on("remove", this.onRemove, this);
17146             store.on("update", this.onUpdate, this);
17147             store.on("clear", this.refresh, this);
17148             store.on("beforeload", this.onBeforeLoad, this);
17149             store.on("load", this.onLoad, this);
17150             store.on("loadexception", this.onLoad, this);
17151         }
17152         
17153         if(store){
17154             this.refresh();
17155         }
17156     },
17157     /**
17158      * onbeforeLoad - masks the loading area.
17159      *
17160      */
17161     onBeforeLoad : function(store,opts)
17162     {
17163          //Roo.log('onBeforeLoad');   
17164         if (!opts.add) {
17165             this.el.update("");
17166         }
17167         this.el.mask(this.mask ? this.mask : "Loading" ); 
17168     },
17169     onLoad : function ()
17170     {
17171         this.el.unmask();
17172     },
17173     
17174
17175     /**
17176      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
17177      * @param {HTMLElement} node
17178      * @return {HTMLElement} The template node
17179      */
17180     findItemFromChild : function(node){
17181         var el = this.dataName  ?
17182             this.el.child('.roo-tpl-' + this.dataName,true) :
17183             this.el.dom; 
17184         
17185         if(!node || node.parentNode == el){
17186                     return node;
17187             }
17188             var p = node.parentNode;
17189             while(p && p != el){
17190             if(p.parentNode == el){
17191                 return p;
17192             }
17193             p = p.parentNode;
17194         }
17195             return null;
17196     },
17197
17198     /** @ignore */
17199     onClick : function(e){
17200         var item = this.findItemFromChild(e.getTarget());
17201         if(item){
17202             var index = this.indexOf(item);
17203             if(this.onItemClick(item, index, e) !== false){
17204                 this.fireEvent("click", this, index, item, e);
17205             }
17206         }else{
17207             this.clearSelections();
17208         }
17209     },
17210
17211     /** @ignore */
17212     onContextMenu : function(e){
17213         var item = this.findItemFromChild(e.getTarget());
17214         if(item){
17215             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
17216         }
17217     },
17218
17219     /** @ignore */
17220     onDblClick : function(e){
17221         var item = this.findItemFromChild(e.getTarget());
17222         if(item){
17223             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
17224         }
17225     },
17226
17227     onItemClick : function(item, index, e)
17228     {
17229         if(this.fireEvent("beforeclick", this, index, item, e) === false){
17230             return false;
17231         }
17232         if (this.toggleSelect) {
17233             var m = this.isSelected(item) ? 'unselect' : 'select';
17234             //Roo.log(m);
17235             var _t = this;
17236             _t[m](item, true, false);
17237             return true;
17238         }
17239         if(this.multiSelect || this.singleSelect){
17240             if(this.multiSelect && e.shiftKey && this.lastSelection){
17241                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
17242             }else{
17243                 this.select(item, this.multiSelect && e.ctrlKey);
17244                 this.lastSelection = item;
17245             }
17246             
17247             if(!this.tickable){
17248                 e.preventDefault();
17249             }
17250             
17251         }
17252         return true;
17253     },
17254
17255     /**
17256      * Get the number of selected nodes.
17257      * @return {Number}
17258      */
17259     getSelectionCount : function(){
17260         return this.selections.length;
17261     },
17262
17263     /**
17264      * Get the currently selected nodes.
17265      * @return {Array} An array of HTMLElements
17266      */
17267     getSelectedNodes : function(){
17268         return this.selections;
17269     },
17270
17271     /**
17272      * Get the indexes of the selected nodes.
17273      * @return {Array}
17274      */
17275     getSelectedIndexes : function(){
17276         var indexes = [], s = this.selections;
17277         for(var i = 0, len = s.length; i < len; i++){
17278             indexes.push(s[i].nodeIndex);
17279         }
17280         return indexes;
17281     },
17282
17283     /**
17284      * Clear all selections
17285      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
17286      */
17287     clearSelections : function(suppressEvent){
17288         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
17289             this.cmp.elements = this.selections;
17290             this.cmp.removeClass(this.selectedClass);
17291             this.selections = [];
17292             if(!suppressEvent){
17293                 this.fireEvent("selectionchange", this, this.selections);
17294             }
17295         }
17296     },
17297
17298     /**
17299      * Returns true if the passed node is selected
17300      * @param {HTMLElement/Number} node The node or node index
17301      * @return {Boolean}
17302      */
17303     isSelected : function(node){
17304         var s = this.selections;
17305         if(s.length < 1){
17306             return false;
17307         }
17308         node = this.getNode(node);
17309         return s.indexOf(node) !== -1;
17310     },
17311
17312     /**
17313      * Selects nodes.
17314      * @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
17315      * @param {Boolean} keepExisting (optional) true to keep existing selections
17316      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17317      */
17318     select : function(nodeInfo, keepExisting, suppressEvent){
17319         if(nodeInfo instanceof Array){
17320             if(!keepExisting){
17321                 this.clearSelections(true);
17322             }
17323             for(var i = 0, len = nodeInfo.length; i < len; i++){
17324                 this.select(nodeInfo[i], true, true);
17325             }
17326             return;
17327         } 
17328         var node = this.getNode(nodeInfo);
17329         if(!node || this.isSelected(node)){
17330             return; // already selected.
17331         }
17332         if(!keepExisting){
17333             this.clearSelections(true);
17334         }
17335         
17336         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
17337             Roo.fly(node).addClass(this.selectedClass);
17338             this.selections.push(node);
17339             if(!suppressEvent){
17340                 this.fireEvent("selectionchange", this, this.selections);
17341             }
17342         }
17343         
17344         
17345     },
17346       /**
17347      * Unselects nodes.
17348      * @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
17349      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
17350      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17351      */
17352     unselect : function(nodeInfo, keepExisting, suppressEvent)
17353     {
17354         if(nodeInfo instanceof Array){
17355             Roo.each(this.selections, function(s) {
17356                 this.unselect(s, nodeInfo);
17357             }, this);
17358             return;
17359         }
17360         var node = this.getNode(nodeInfo);
17361         if(!node || !this.isSelected(node)){
17362             //Roo.log("not selected");
17363             return; // not selected.
17364         }
17365         // fireevent???
17366         var ns = [];
17367         Roo.each(this.selections, function(s) {
17368             if (s == node ) {
17369                 Roo.fly(node).removeClass(this.selectedClass);
17370
17371                 return;
17372             }
17373             ns.push(s);
17374         },this);
17375         
17376         this.selections= ns;
17377         this.fireEvent("selectionchange", this, this.selections);
17378     },
17379
17380     /**
17381      * Gets a template node.
17382      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17383      * @return {HTMLElement} The node or null if it wasn't found
17384      */
17385     getNode : function(nodeInfo){
17386         if(typeof nodeInfo == "string"){
17387             return document.getElementById(nodeInfo);
17388         }else if(typeof nodeInfo == "number"){
17389             return this.nodes[nodeInfo];
17390         }
17391         return nodeInfo;
17392     },
17393
17394     /**
17395      * Gets a range template nodes.
17396      * @param {Number} startIndex
17397      * @param {Number} endIndex
17398      * @return {Array} An array of nodes
17399      */
17400     getNodes : function(start, end){
17401         var ns = this.nodes;
17402         start = start || 0;
17403         end = typeof end == "undefined" ? ns.length - 1 : end;
17404         var nodes = [];
17405         if(start <= end){
17406             for(var i = start; i <= end; i++){
17407                 nodes.push(ns[i]);
17408             }
17409         } else{
17410             for(var i = start; i >= end; i--){
17411                 nodes.push(ns[i]);
17412             }
17413         }
17414         return nodes;
17415     },
17416
17417     /**
17418      * Finds the index of the passed node
17419      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17420      * @return {Number} The index of the node or -1
17421      */
17422     indexOf : function(node){
17423         node = this.getNode(node);
17424         if(typeof node.nodeIndex == "number"){
17425             return node.nodeIndex;
17426         }
17427         var ns = this.nodes;
17428         for(var i = 0, len = ns.length; i < len; i++){
17429             if(ns[i] == node){
17430                 return i;
17431             }
17432         }
17433         return -1;
17434     }
17435 });
17436 /*
17437  * - LGPL
17438  *
17439  * based on jquery fullcalendar
17440  * 
17441  */
17442
17443 Roo.bootstrap = Roo.bootstrap || {};
17444 /**
17445  * @class Roo.bootstrap.Calendar
17446  * @extends Roo.bootstrap.Component
17447  * Bootstrap Calendar class
17448  * @cfg {Boolean} loadMask (true|false) default false
17449  * @cfg {Object} header generate the user specific header of the calendar, default false
17450
17451  * @constructor
17452  * Create a new Container
17453  * @param {Object} config The config object
17454  */
17455
17456
17457
17458 Roo.bootstrap.Calendar = function(config){
17459     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
17460      this.addEvents({
17461         /**
17462              * @event select
17463              * Fires when a date is selected
17464              * @param {DatePicker} this
17465              * @param {Date} date The selected date
17466              */
17467         'select': true,
17468         /**
17469              * @event monthchange
17470              * Fires when the displayed month changes 
17471              * @param {DatePicker} this
17472              * @param {Date} date The selected month
17473              */
17474         'monthchange': true,
17475         /**
17476              * @event evententer
17477              * Fires when mouse over an event
17478              * @param {Calendar} this
17479              * @param {event} Event
17480              */
17481         'evententer': true,
17482         /**
17483              * @event eventleave
17484              * Fires when the mouse leaves an
17485              * @param {Calendar} this
17486              * @param {event}
17487              */
17488         'eventleave': true,
17489         /**
17490              * @event eventclick
17491              * Fires when the mouse click an
17492              * @param {Calendar} this
17493              * @param {event}
17494              */
17495         'eventclick': true
17496         
17497     });
17498
17499 };
17500
17501 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
17502     
17503      /**
17504      * @cfg {Number} startDay
17505      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
17506      */
17507     startDay : 0,
17508     
17509     loadMask : false,
17510     
17511     header : false,
17512       
17513     getAutoCreate : function(){
17514         
17515         
17516         var fc_button = function(name, corner, style, content ) {
17517             return Roo.apply({},{
17518                 tag : 'span',
17519                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
17520                          (corner.length ?
17521                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17522                             ''
17523                         ),
17524                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17525                 unselectable: 'on'
17526             });
17527         };
17528         
17529         var header = {};
17530         
17531         if(!this.header){
17532             header = {
17533                 tag : 'table',
17534                 cls : 'fc-header',
17535                 style : 'width:100%',
17536                 cn : [
17537                     {
17538                         tag: 'tr',
17539                         cn : [
17540                             {
17541                                 tag : 'td',
17542                                 cls : 'fc-header-left',
17543                                 cn : [
17544                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
17545                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
17546                                     { tag: 'span', cls: 'fc-header-space' },
17547                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
17548
17549
17550                                 ]
17551                             },
17552
17553                             {
17554                                 tag : 'td',
17555                                 cls : 'fc-header-center',
17556                                 cn : [
17557                                     {
17558                                         tag: 'span',
17559                                         cls: 'fc-header-title',
17560                                         cn : {
17561                                             tag: 'H2',
17562                                             html : 'month / year'
17563                                         }
17564                                     }
17565
17566                                 ]
17567                             },
17568                             {
17569                                 tag : 'td',
17570                                 cls : 'fc-header-right',
17571                                 cn : [
17572                               /*      fc_button('month', 'left', '', 'month' ),
17573                                     fc_button('week', '', '', 'week' ),
17574                                     fc_button('day', 'right', '', 'day' )
17575                                 */    
17576
17577                                 ]
17578                             }
17579
17580                         ]
17581                     }
17582                 ]
17583             };
17584         }
17585         
17586         header = this.header;
17587         
17588        
17589         var cal_heads = function() {
17590             var ret = [];
17591             // fixme - handle this.
17592             
17593             for (var i =0; i < Date.dayNames.length; i++) {
17594                 var d = Date.dayNames[i];
17595                 ret.push({
17596                     tag: 'th',
17597                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17598                     html : d.substring(0,3)
17599                 });
17600                 
17601             }
17602             ret[0].cls += ' fc-first';
17603             ret[6].cls += ' fc-last';
17604             return ret;
17605         };
17606         var cal_cell = function(n) {
17607             return  {
17608                 tag: 'td',
17609                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17610                 cn : [
17611                     {
17612                         cn : [
17613                             {
17614                                 cls: 'fc-day-number',
17615                                 html: 'D'
17616                             },
17617                             {
17618                                 cls: 'fc-day-content',
17619                              
17620                                 cn : [
17621                                      {
17622                                         style: 'position: relative;' // height: 17px;
17623                                     }
17624                                 ]
17625                             }
17626                             
17627                             
17628                         ]
17629                     }
17630                 ]
17631                 
17632             }
17633         };
17634         var cal_rows = function() {
17635             
17636             var ret = [];
17637             for (var r = 0; r < 6; r++) {
17638                 var row= {
17639                     tag : 'tr',
17640                     cls : 'fc-week',
17641                     cn : []
17642                 };
17643                 
17644                 for (var i =0; i < Date.dayNames.length; i++) {
17645                     var d = Date.dayNames[i];
17646                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17647
17648                 }
17649                 row.cn[0].cls+=' fc-first';
17650                 row.cn[0].cn[0].style = 'min-height:90px';
17651                 row.cn[6].cls+=' fc-last';
17652                 ret.push(row);
17653                 
17654             }
17655             ret[0].cls += ' fc-first';
17656             ret[4].cls += ' fc-prev-last';
17657             ret[5].cls += ' fc-last';
17658             return ret;
17659             
17660         };
17661         
17662         var cal_table = {
17663             tag: 'table',
17664             cls: 'fc-border-separate',
17665             style : 'width:100%',
17666             cellspacing  : 0,
17667             cn : [
17668                 { 
17669                     tag: 'thead',
17670                     cn : [
17671                         { 
17672                             tag: 'tr',
17673                             cls : 'fc-first fc-last',
17674                             cn : cal_heads()
17675                         }
17676                     ]
17677                 },
17678                 { 
17679                     tag: 'tbody',
17680                     cn : cal_rows()
17681                 }
17682                   
17683             ]
17684         };
17685          
17686          var cfg = {
17687             cls : 'fc fc-ltr',
17688             cn : [
17689                 header,
17690                 {
17691                     cls : 'fc-content',
17692                     style : "position: relative;",
17693                     cn : [
17694                         {
17695                             cls : 'fc-view fc-view-month fc-grid',
17696                             style : 'position: relative',
17697                             unselectable : 'on',
17698                             cn : [
17699                                 {
17700                                     cls : 'fc-event-container',
17701                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17702                                 },
17703                                 cal_table
17704                             ]
17705                         }
17706                     ]
17707     
17708                 }
17709            ] 
17710             
17711         };
17712         
17713          
17714         
17715         return cfg;
17716     },
17717     
17718     
17719     initEvents : function()
17720     {
17721         if(!this.store){
17722             throw "can not find store for calendar";
17723         }
17724         
17725         var mark = {
17726             tag: "div",
17727             cls:"x-dlg-mask",
17728             style: "text-align:center",
17729             cn: [
17730                 {
17731                     tag: "div",
17732                     style: "background-color:white;width:50%;margin:250 auto",
17733                     cn: [
17734                         {
17735                             tag: "img",
17736                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17737                         },
17738                         {
17739                             tag: "span",
17740                             html: "Loading"
17741                         }
17742                         
17743                     ]
17744                 }
17745             ]
17746         };
17747         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17748         
17749         var size = this.el.select('.fc-content', true).first().getSize();
17750         this.maskEl.setSize(size.width, size.height);
17751         this.maskEl.enableDisplayMode("block");
17752         if(!this.loadMask){
17753             this.maskEl.hide();
17754         }
17755         
17756         this.store = Roo.factory(this.store, Roo.data);
17757         this.store.on('load', this.onLoad, this);
17758         this.store.on('beforeload', this.onBeforeLoad, this);
17759         
17760         this.resize();
17761         
17762         this.cells = this.el.select('.fc-day',true);
17763         //Roo.log(this.cells);
17764         this.textNodes = this.el.query('.fc-day-number');
17765         this.cells.addClassOnOver('fc-state-hover');
17766         
17767         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17768         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17769         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17770         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17771         
17772         this.on('monthchange', this.onMonthChange, this);
17773         
17774         this.update(new Date().clearTime());
17775     },
17776     
17777     resize : function() {
17778         var sz  = this.el.getSize();
17779         
17780         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17781         this.el.select('.fc-day-content div',true).setHeight(34);
17782     },
17783     
17784     
17785     // private
17786     showPrevMonth : function(e){
17787         this.update(this.activeDate.add("mo", -1));
17788     },
17789     showToday : function(e){
17790         this.update(new Date().clearTime());
17791     },
17792     // private
17793     showNextMonth : function(e){
17794         this.update(this.activeDate.add("mo", 1));
17795     },
17796
17797     // private
17798     showPrevYear : function(){
17799         this.update(this.activeDate.add("y", -1));
17800     },
17801
17802     // private
17803     showNextYear : function(){
17804         this.update(this.activeDate.add("y", 1));
17805     },
17806
17807     
17808    // private
17809     update : function(date)
17810     {
17811         var vd = this.activeDate;
17812         this.activeDate = date;
17813 //        if(vd && this.el){
17814 //            var t = date.getTime();
17815 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17816 //                Roo.log('using add remove');
17817 //                
17818 //                this.fireEvent('monthchange', this, date);
17819 //                
17820 //                this.cells.removeClass("fc-state-highlight");
17821 //                this.cells.each(function(c){
17822 //                   if(c.dateValue == t){
17823 //                       c.addClass("fc-state-highlight");
17824 //                       setTimeout(function(){
17825 //                            try{c.dom.firstChild.focus();}catch(e){}
17826 //                       }, 50);
17827 //                       return false;
17828 //                   }
17829 //                   return true;
17830 //                });
17831 //                return;
17832 //            }
17833 //        }
17834         
17835         var days = date.getDaysInMonth();
17836         
17837         var firstOfMonth = date.getFirstDateOfMonth();
17838         var startingPos = firstOfMonth.getDay()-this.startDay;
17839         
17840         if(startingPos < this.startDay){
17841             startingPos += 7;
17842         }
17843         
17844         var pm = date.add(Date.MONTH, -1);
17845         var prevStart = pm.getDaysInMonth()-startingPos;
17846 //        
17847         this.cells = this.el.select('.fc-day',true);
17848         this.textNodes = this.el.query('.fc-day-number');
17849         this.cells.addClassOnOver('fc-state-hover');
17850         
17851         var cells = this.cells.elements;
17852         var textEls = this.textNodes;
17853         
17854         Roo.each(cells, function(cell){
17855             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17856         });
17857         
17858         days += startingPos;
17859
17860         // convert everything to numbers so it's fast
17861         var day = 86400000;
17862         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17863         //Roo.log(d);
17864         //Roo.log(pm);
17865         //Roo.log(prevStart);
17866         
17867         var today = new Date().clearTime().getTime();
17868         var sel = date.clearTime().getTime();
17869         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17870         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17871         var ddMatch = this.disabledDatesRE;
17872         var ddText = this.disabledDatesText;
17873         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17874         var ddaysText = this.disabledDaysText;
17875         var format = this.format;
17876         
17877         var setCellClass = function(cal, cell){
17878             cell.row = 0;
17879             cell.events = [];
17880             cell.more = [];
17881             //Roo.log('set Cell Class');
17882             cell.title = "";
17883             var t = d.getTime();
17884             
17885             //Roo.log(d);
17886             
17887             cell.dateValue = t;
17888             if(t == today){
17889                 cell.className += " fc-today";
17890                 cell.className += " fc-state-highlight";
17891                 cell.title = cal.todayText;
17892             }
17893             if(t == sel){
17894                 // disable highlight in other month..
17895                 //cell.className += " fc-state-highlight";
17896                 
17897             }
17898             // disabling
17899             if(t < min) {
17900                 cell.className = " fc-state-disabled";
17901                 cell.title = cal.minText;
17902                 return;
17903             }
17904             if(t > max) {
17905                 cell.className = " fc-state-disabled";
17906                 cell.title = cal.maxText;
17907                 return;
17908             }
17909             if(ddays){
17910                 if(ddays.indexOf(d.getDay()) != -1){
17911                     cell.title = ddaysText;
17912                     cell.className = " fc-state-disabled";
17913                 }
17914             }
17915             if(ddMatch && format){
17916                 var fvalue = d.dateFormat(format);
17917                 if(ddMatch.test(fvalue)){
17918                     cell.title = ddText.replace("%0", fvalue);
17919                     cell.className = " fc-state-disabled";
17920                 }
17921             }
17922             
17923             if (!cell.initialClassName) {
17924                 cell.initialClassName = cell.dom.className;
17925             }
17926             
17927             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17928         };
17929
17930         var i = 0;
17931         
17932         for(; i < startingPos; i++) {
17933             textEls[i].innerHTML = (++prevStart);
17934             d.setDate(d.getDate()+1);
17935             
17936             cells[i].className = "fc-past fc-other-month";
17937             setCellClass(this, cells[i]);
17938         }
17939         
17940         var intDay = 0;
17941         
17942         for(; i < days; i++){
17943             intDay = i - startingPos + 1;
17944             textEls[i].innerHTML = (intDay);
17945             d.setDate(d.getDate()+1);
17946             
17947             cells[i].className = ''; // "x-date-active";
17948             setCellClass(this, cells[i]);
17949         }
17950         var extraDays = 0;
17951         
17952         for(; i < 42; i++) {
17953             textEls[i].innerHTML = (++extraDays);
17954             d.setDate(d.getDate()+1);
17955             
17956             cells[i].className = "fc-future fc-other-month";
17957             setCellClass(this, cells[i]);
17958         }
17959         
17960         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17961         
17962         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17963         
17964         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17965         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17966         
17967         if(totalRows != 6){
17968             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17969             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17970         }
17971         
17972         this.fireEvent('monthchange', this, date);
17973         
17974         
17975         /*
17976         if(!this.internalRender){
17977             var main = this.el.dom.firstChild;
17978             var w = main.offsetWidth;
17979             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17980             Roo.fly(main).setWidth(w);
17981             this.internalRender = true;
17982             // opera does not respect the auto grow header center column
17983             // then, after it gets a width opera refuses to recalculate
17984             // without a second pass
17985             if(Roo.isOpera && !this.secondPass){
17986                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17987                 this.secondPass = true;
17988                 this.update.defer(10, this, [date]);
17989             }
17990         }
17991         */
17992         
17993     },
17994     
17995     findCell : function(dt) {
17996         dt = dt.clearTime().getTime();
17997         var ret = false;
17998         this.cells.each(function(c){
17999             //Roo.log("check " +c.dateValue + '?=' + dt);
18000             if(c.dateValue == dt){
18001                 ret = c;
18002                 return false;
18003             }
18004             return true;
18005         });
18006         
18007         return ret;
18008     },
18009     
18010     findCells : function(ev) {
18011         var s = ev.start.clone().clearTime().getTime();
18012        // Roo.log(s);
18013         var e= ev.end.clone().clearTime().getTime();
18014        // Roo.log(e);
18015         var ret = [];
18016         this.cells.each(function(c){
18017              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
18018             
18019             if(c.dateValue > e){
18020                 return ;
18021             }
18022             if(c.dateValue < s){
18023                 return ;
18024             }
18025             ret.push(c);
18026         });
18027         
18028         return ret;    
18029     },
18030     
18031 //    findBestRow: function(cells)
18032 //    {
18033 //        var ret = 0;
18034 //        
18035 //        for (var i =0 ; i < cells.length;i++) {
18036 //            ret  = Math.max(cells[i].rows || 0,ret);
18037 //        }
18038 //        return ret;
18039 //        
18040 //    },
18041     
18042     
18043     addItem : function(ev)
18044     {
18045         // look for vertical location slot in
18046         var cells = this.findCells(ev);
18047         
18048 //        ev.row = this.findBestRow(cells);
18049         
18050         // work out the location.
18051         
18052         var crow = false;
18053         var rows = [];
18054         for(var i =0; i < cells.length; i++) {
18055             
18056             cells[i].row = cells[0].row;
18057             
18058             if(i == 0){
18059                 cells[i].row = cells[i].row + 1;
18060             }
18061             
18062             if (!crow) {
18063                 crow = {
18064                     start : cells[i],
18065                     end :  cells[i]
18066                 };
18067                 continue;
18068             }
18069             if (crow.start.getY() == cells[i].getY()) {
18070                 // on same row.
18071                 crow.end = cells[i];
18072                 continue;
18073             }
18074             // different row.
18075             rows.push(crow);
18076             crow = {
18077                 start: cells[i],
18078                 end : cells[i]
18079             };
18080             
18081         }
18082         
18083         rows.push(crow);
18084         ev.els = [];
18085         ev.rows = rows;
18086         ev.cells = cells;
18087         
18088         cells[0].events.push(ev);
18089         
18090         this.calevents.push(ev);
18091     },
18092     
18093     clearEvents: function() {
18094         
18095         if(!this.calevents){
18096             return;
18097         }
18098         
18099         Roo.each(this.cells.elements, function(c){
18100             c.row = 0;
18101             c.events = [];
18102             c.more = [];
18103         });
18104         
18105         Roo.each(this.calevents, function(e) {
18106             Roo.each(e.els, function(el) {
18107                 el.un('mouseenter' ,this.onEventEnter, this);
18108                 el.un('mouseleave' ,this.onEventLeave, this);
18109                 el.remove();
18110             },this);
18111         },this);
18112         
18113         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
18114             e.remove();
18115         });
18116         
18117     },
18118     
18119     renderEvents: function()
18120     {   
18121         var _this = this;
18122         
18123         this.cells.each(function(c) {
18124             
18125             if(c.row < 5){
18126                 return;
18127             }
18128             
18129             var ev = c.events;
18130             
18131             var r = 4;
18132             if(c.row != c.events.length){
18133                 r = 4 - (4 - (c.row - c.events.length));
18134             }
18135             
18136             c.events = ev.slice(0, r);
18137             c.more = ev.slice(r);
18138             
18139             if(c.more.length && c.more.length == 1){
18140                 c.events.push(c.more.pop());
18141             }
18142             
18143             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
18144             
18145         });
18146             
18147         this.cells.each(function(c) {
18148             
18149             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
18150             
18151             
18152             for (var e = 0; e < c.events.length; e++){
18153                 var ev = c.events[e];
18154                 var rows = ev.rows;
18155                 
18156                 for(var i = 0; i < rows.length; i++) {
18157                 
18158                     // how many rows should it span..
18159
18160                     var  cfg = {
18161                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
18162                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
18163
18164                         unselectable : "on",
18165                         cn : [
18166                             {
18167                                 cls: 'fc-event-inner',
18168                                 cn : [
18169     //                                {
18170     //                                  tag:'span',
18171     //                                  cls: 'fc-event-time',
18172     //                                  html : cells.length > 1 ? '' : ev.time
18173     //                                },
18174                                     {
18175                                       tag:'span',
18176                                       cls: 'fc-event-title',
18177                                       html : String.format('{0}', ev.title)
18178                                     }
18179
18180
18181                                 ]
18182                             },
18183                             {
18184                                 cls: 'ui-resizable-handle ui-resizable-e',
18185                                 html : '&nbsp;&nbsp;&nbsp'
18186                             }
18187
18188                         ]
18189                     };
18190
18191                     if (i == 0) {
18192                         cfg.cls += ' fc-event-start';
18193                     }
18194                     if ((i+1) == rows.length) {
18195                         cfg.cls += ' fc-event-end';
18196                     }
18197
18198                     var ctr = _this.el.select('.fc-event-container',true).first();
18199                     var cg = ctr.createChild(cfg);
18200
18201                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
18202                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
18203
18204                     var r = (c.more.length) ? 1 : 0;
18205                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
18206                     cg.setWidth(ebox.right - sbox.x -2);
18207
18208                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
18209                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
18210                     cg.on('click', _this.onEventClick, _this, ev);
18211
18212                     ev.els.push(cg);
18213                     
18214                 }
18215                 
18216             }
18217             
18218             
18219             if(c.more.length){
18220                 var  cfg = {
18221                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
18222                     style : 'position: absolute',
18223                     unselectable : "on",
18224                     cn : [
18225                         {
18226                             cls: 'fc-event-inner',
18227                             cn : [
18228                                 {
18229                                   tag:'span',
18230                                   cls: 'fc-event-title',
18231                                   html : 'More'
18232                                 }
18233
18234
18235                             ]
18236                         },
18237                         {
18238                             cls: 'ui-resizable-handle ui-resizable-e',
18239                             html : '&nbsp;&nbsp;&nbsp'
18240                         }
18241
18242                     ]
18243                 };
18244
18245                 var ctr = _this.el.select('.fc-event-container',true).first();
18246                 var cg = ctr.createChild(cfg);
18247
18248                 var sbox = c.select('.fc-day-content',true).first().getBox();
18249                 var ebox = c.select('.fc-day-content',true).first().getBox();
18250                 //Roo.log(cg);
18251                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
18252                 cg.setWidth(ebox.right - sbox.x -2);
18253
18254                 cg.on('click', _this.onMoreEventClick, _this, c.more);
18255                 
18256             }
18257             
18258         });
18259         
18260         
18261         
18262     },
18263     
18264     onEventEnter: function (e, el,event,d) {
18265         this.fireEvent('evententer', this, el, event);
18266     },
18267     
18268     onEventLeave: function (e, el,event,d) {
18269         this.fireEvent('eventleave', this, el, event);
18270     },
18271     
18272     onEventClick: function (e, el,event,d) {
18273         this.fireEvent('eventclick', this, el, event);
18274     },
18275     
18276     onMonthChange: function () {
18277         this.store.load();
18278     },
18279     
18280     onMoreEventClick: function(e, el, more)
18281     {
18282         var _this = this;
18283         
18284         this.calpopover.placement = 'right';
18285         this.calpopover.setTitle('More');
18286         
18287         this.calpopover.setContent('');
18288         
18289         var ctr = this.calpopover.el.select('.popover-content', true).first();
18290         
18291         Roo.each(more, function(m){
18292             var cfg = {
18293                 cls : 'fc-event-hori fc-event-draggable',
18294                 html : m.title
18295             };
18296             var cg = ctr.createChild(cfg);
18297             
18298             cg.on('click', _this.onEventClick, _this, m);
18299         });
18300         
18301         this.calpopover.show(el);
18302         
18303         
18304     },
18305     
18306     onLoad: function () 
18307     {   
18308         this.calevents = [];
18309         var cal = this;
18310         
18311         if(this.store.getCount() > 0){
18312             this.store.data.each(function(d){
18313                cal.addItem({
18314                     id : d.data.id,
18315                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
18316                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
18317                     time : d.data.start_time,
18318                     title : d.data.title,
18319                     description : d.data.description,
18320                     venue : d.data.venue
18321                 });
18322             });
18323         }
18324         
18325         this.renderEvents();
18326         
18327         if(this.calevents.length && this.loadMask){
18328             this.maskEl.hide();
18329         }
18330     },
18331     
18332     onBeforeLoad: function()
18333     {
18334         this.clearEvents();
18335         if(this.loadMask){
18336             this.maskEl.show();
18337         }
18338     }
18339 });
18340
18341  
18342  /*
18343  * - LGPL
18344  *
18345  * element
18346  * 
18347  */
18348
18349 /**
18350  * @class Roo.bootstrap.Popover
18351  * @extends Roo.bootstrap.Component
18352  * Bootstrap Popover class
18353  * @cfg {String} html contents of the popover   (or false to use children..)
18354  * @cfg {String} title of popover (or false to hide)
18355  * @cfg {String} placement how it is placed
18356  * @cfg {String} trigger click || hover (or false to trigger manually)
18357  * @cfg {String} over what (parent or false to trigger manually.)
18358  * @cfg {Number} delay - delay before showing
18359  
18360  * @constructor
18361  * Create a new Popover
18362  * @param {Object} config The config object
18363  */
18364
18365 Roo.bootstrap.Popover = function(config){
18366     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
18367     
18368     this.addEvents({
18369         // raw events
18370          /**
18371          * @event show
18372          * After the popover show
18373          * 
18374          * @param {Roo.bootstrap.Popover} this
18375          */
18376         "show" : true,
18377         /**
18378          * @event hide
18379          * After the popover hide
18380          * 
18381          * @param {Roo.bootstrap.Popover} this
18382          */
18383         "hide" : true
18384     });
18385 };
18386
18387 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
18388     
18389     title: 'Fill in a title',
18390     html: false,
18391     
18392     placement : 'right',
18393     trigger : 'hover', // hover
18394     
18395     delay : 0,
18396     
18397     over: 'parent',
18398     
18399     can_build_overlaid : false,
18400     
18401     getChildContainer : function()
18402     {
18403         return this.el.select('.popover-content',true).first();
18404     },
18405     
18406     getAutoCreate : function(){
18407          
18408         var cfg = {
18409            cls : 'popover roo-dynamic',
18410            style: 'display:block',
18411            cn : [
18412                 {
18413                     cls : 'arrow'
18414                 },
18415                 {
18416                     cls : 'popover-inner',
18417                     cn : [
18418                         {
18419                             tag: 'h3',
18420                             cls: 'popover-title popover-header',
18421                             html : this.title
18422                         },
18423                         {
18424                             cls : 'popover-content popover-body',
18425                             html : this.html
18426                         }
18427                     ]
18428                     
18429                 }
18430            ]
18431         };
18432         
18433         return cfg;
18434     },
18435     setTitle: function(str)
18436     {
18437         this.title = str;
18438         this.el.select('.popover-title',true).first().dom.innerHTML = str;
18439     },
18440     setContent: function(str)
18441     {
18442         this.html = str;
18443         this.el.select('.popover-content',true).first().dom.innerHTML = str;
18444     },
18445     // as it get's added to the bottom of the page.
18446     onRender : function(ct, position)
18447     {
18448         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
18449         if(!this.el){
18450             var cfg = Roo.apply({},  this.getAutoCreate());
18451             cfg.id = Roo.id();
18452             
18453             if (this.cls) {
18454                 cfg.cls += ' ' + this.cls;
18455             }
18456             if (this.style) {
18457                 cfg.style = this.style;
18458             }
18459             //Roo.log("adding to ");
18460             this.el = Roo.get(document.body).createChild(cfg, position);
18461 //            Roo.log(this.el);
18462         }
18463         this.initEvents();
18464     },
18465     
18466     initEvents : function()
18467     {
18468         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
18469         this.el.enableDisplayMode('block');
18470         this.el.hide();
18471         if (this.over === false) {
18472             return; 
18473         }
18474         if (this.triggers === false) {
18475             return;
18476         }
18477         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18478         var triggers = this.trigger ? this.trigger.split(' ') : [];
18479         Roo.each(triggers, function(trigger) {
18480         
18481             if (trigger == 'click') {
18482                 on_el.on('click', this.toggle, this);
18483             } else if (trigger != 'manual') {
18484                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
18485                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
18486       
18487                 on_el.on(eventIn  ,this.enter, this);
18488                 on_el.on(eventOut, this.leave, this);
18489             }
18490         }, this);
18491         
18492     },
18493     
18494     
18495     // private
18496     timeout : null,
18497     hoverState : null,
18498     
18499     toggle : function () {
18500         this.hoverState == 'in' ? this.leave() : this.enter();
18501     },
18502     
18503     enter : function () {
18504         
18505         clearTimeout(this.timeout);
18506     
18507         this.hoverState = 'in';
18508     
18509         if (!this.delay || !this.delay.show) {
18510             this.show();
18511             return;
18512         }
18513         var _t = this;
18514         this.timeout = setTimeout(function () {
18515             if (_t.hoverState == 'in') {
18516                 _t.show();
18517             }
18518         }, this.delay.show)
18519     },
18520     
18521     leave : function() {
18522         clearTimeout(this.timeout);
18523     
18524         this.hoverState = 'out';
18525     
18526         if (!this.delay || !this.delay.hide) {
18527             this.hide();
18528             return;
18529         }
18530         var _t = this;
18531         this.timeout = setTimeout(function () {
18532             if (_t.hoverState == 'out') {
18533                 _t.hide();
18534             }
18535         }, this.delay.hide)
18536     },
18537     
18538     show : function (on_el)
18539     {
18540         if (!on_el) {
18541             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18542         }
18543         
18544         // set content.
18545         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18546         if (this.html !== false) {
18547             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18548         }
18549         this.el.removeClass([
18550             'fade','top','bottom', 'left', 'right','in',
18551             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18552         ]);
18553         if (!this.title.length) {
18554             this.el.select('.popover-title',true).hide();
18555         }
18556         
18557         var placement = typeof this.placement == 'function' ?
18558             this.placement.call(this, this.el, on_el) :
18559             this.placement;
18560             
18561         var autoToken = /\s?auto?\s?/i;
18562         var autoPlace = autoToken.test(placement);
18563         if (autoPlace) {
18564             placement = placement.replace(autoToken, '') || 'top';
18565         }
18566         
18567         //this.el.detach()
18568         //this.el.setXY([0,0]);
18569         this.el.show();
18570         this.el.dom.style.display='block';
18571         this.el.addClass(placement);
18572         
18573         //this.el.appendTo(on_el);
18574         
18575         var p = this.getPosition();
18576         var box = this.el.getBox();
18577         
18578         if (autoPlace) {
18579             // fixme..
18580         }
18581         var align = Roo.bootstrap.Popover.alignment[placement];
18582         
18583 //        Roo.log(align);
18584         this.el.alignTo(on_el, align[0],align[1]);
18585         //var arrow = this.el.select('.arrow',true).first();
18586         //arrow.set(align[2], 
18587         
18588         this.el.addClass('in');
18589         
18590         
18591         if (this.el.hasClass('fade')) {
18592             // fade it?
18593         }
18594         
18595         this.hoverState = 'in';
18596         
18597         this.fireEvent('show', this);
18598         
18599     },
18600     hide : function()
18601     {
18602         this.el.setXY([0,0]);
18603         this.el.removeClass('in');
18604         this.el.hide();
18605         this.hoverState = null;
18606         
18607         this.fireEvent('hide', this);
18608     }
18609     
18610 });
18611
18612 Roo.bootstrap.Popover.alignment = {
18613     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18614     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18615     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18616     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18617 };
18618
18619  /*
18620  * - LGPL
18621  *
18622  * Progress
18623  * 
18624  */
18625
18626 /**
18627  * @class Roo.bootstrap.Progress
18628  * @extends Roo.bootstrap.Component
18629  * Bootstrap Progress class
18630  * @cfg {Boolean} striped striped of the progress bar
18631  * @cfg {Boolean} active animated of the progress bar
18632  * 
18633  * 
18634  * @constructor
18635  * Create a new Progress
18636  * @param {Object} config The config object
18637  */
18638
18639 Roo.bootstrap.Progress = function(config){
18640     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18641 };
18642
18643 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18644     
18645     striped : false,
18646     active: false,
18647     
18648     getAutoCreate : function(){
18649         var cfg = {
18650             tag: 'div',
18651             cls: 'progress'
18652         };
18653         
18654         
18655         if(this.striped){
18656             cfg.cls += ' progress-striped';
18657         }
18658       
18659         if(this.active){
18660             cfg.cls += ' active';
18661         }
18662         
18663         
18664         return cfg;
18665     }
18666    
18667 });
18668
18669  
18670
18671  /*
18672  * - LGPL
18673  *
18674  * ProgressBar
18675  * 
18676  */
18677
18678 /**
18679  * @class Roo.bootstrap.ProgressBar
18680  * @extends Roo.bootstrap.Component
18681  * Bootstrap ProgressBar class
18682  * @cfg {Number} aria_valuenow aria-value now
18683  * @cfg {Number} aria_valuemin aria-value min
18684  * @cfg {Number} aria_valuemax aria-value max
18685  * @cfg {String} label label for the progress bar
18686  * @cfg {String} panel (success | info | warning | danger )
18687  * @cfg {String} role role of the progress bar
18688  * @cfg {String} sr_only text
18689  * 
18690  * 
18691  * @constructor
18692  * Create a new ProgressBar
18693  * @param {Object} config The config object
18694  */
18695
18696 Roo.bootstrap.ProgressBar = function(config){
18697     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18698 };
18699
18700 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18701     
18702     aria_valuenow : 0,
18703     aria_valuemin : 0,
18704     aria_valuemax : 100,
18705     label : false,
18706     panel : false,
18707     role : false,
18708     sr_only: false,
18709     
18710     getAutoCreate : function()
18711     {
18712         
18713         var cfg = {
18714             tag: 'div',
18715             cls: 'progress-bar',
18716             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18717         };
18718         
18719         if(this.sr_only){
18720             cfg.cn = {
18721                 tag: 'span',
18722                 cls: 'sr-only',
18723                 html: this.sr_only
18724             }
18725         }
18726         
18727         if(this.role){
18728             cfg.role = this.role;
18729         }
18730         
18731         if(this.aria_valuenow){
18732             cfg['aria-valuenow'] = this.aria_valuenow;
18733         }
18734         
18735         if(this.aria_valuemin){
18736             cfg['aria-valuemin'] = this.aria_valuemin;
18737         }
18738         
18739         if(this.aria_valuemax){
18740             cfg['aria-valuemax'] = this.aria_valuemax;
18741         }
18742         
18743         if(this.label && !this.sr_only){
18744             cfg.html = this.label;
18745         }
18746         
18747         if(this.panel){
18748             cfg.cls += ' progress-bar-' + this.panel;
18749         }
18750         
18751         return cfg;
18752     },
18753     
18754     update : function(aria_valuenow)
18755     {
18756         this.aria_valuenow = aria_valuenow;
18757         
18758         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18759     }
18760    
18761 });
18762
18763  
18764
18765  /*
18766  * - LGPL
18767  *
18768  * column
18769  * 
18770  */
18771
18772 /**
18773  * @class Roo.bootstrap.TabGroup
18774  * @extends Roo.bootstrap.Column
18775  * Bootstrap Column class
18776  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18777  * @cfg {Boolean} carousel true to make the group behave like a carousel
18778  * @cfg {Boolean} bullets show bullets for the panels
18779  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18780  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18781  * @cfg {Boolean} showarrow (true|false) show arrow default true
18782  * 
18783  * @constructor
18784  * Create a new TabGroup
18785  * @param {Object} config The config object
18786  */
18787
18788 Roo.bootstrap.TabGroup = function(config){
18789     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18790     if (!this.navId) {
18791         this.navId = Roo.id();
18792     }
18793     this.tabs = [];
18794     Roo.bootstrap.TabGroup.register(this);
18795     
18796 };
18797
18798 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18799     
18800     carousel : false,
18801     transition : false,
18802     bullets : 0,
18803     timer : 0,
18804     autoslide : false,
18805     slideFn : false,
18806     slideOnTouch : false,
18807     showarrow : true,
18808     
18809     getAutoCreate : function()
18810     {
18811         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18812         
18813         cfg.cls += ' tab-content';
18814         
18815         if (this.carousel) {
18816             cfg.cls += ' carousel slide';
18817             
18818             cfg.cn = [{
18819                cls : 'carousel-inner',
18820                cn : []
18821             }];
18822         
18823             if(this.bullets  && !Roo.isTouch){
18824                 
18825                 var bullets = {
18826                     cls : 'carousel-bullets',
18827                     cn : []
18828                 };
18829                
18830                 if(this.bullets_cls){
18831                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18832                 }
18833                 
18834                 bullets.cn.push({
18835                     cls : 'clear'
18836                 });
18837                 
18838                 cfg.cn[0].cn.push(bullets);
18839             }
18840             
18841             if(this.showarrow){
18842                 cfg.cn[0].cn.push({
18843                     tag : 'div',
18844                     class : 'carousel-arrow',
18845                     cn : [
18846                         {
18847                             tag : 'div',
18848                             class : 'carousel-prev',
18849                             cn : [
18850                                 {
18851                                     tag : 'i',
18852                                     class : 'fa fa-chevron-left'
18853                                 }
18854                             ]
18855                         },
18856                         {
18857                             tag : 'div',
18858                             class : 'carousel-next',
18859                             cn : [
18860                                 {
18861                                     tag : 'i',
18862                                     class : 'fa fa-chevron-right'
18863                                 }
18864                             ]
18865                         }
18866                     ]
18867                 });
18868             }
18869             
18870         }
18871         
18872         return cfg;
18873     },
18874     
18875     initEvents:  function()
18876     {
18877 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18878 //            this.el.on("touchstart", this.onTouchStart, this);
18879 //        }
18880         
18881         if(this.autoslide){
18882             var _this = this;
18883             
18884             this.slideFn = window.setInterval(function() {
18885                 _this.showPanelNext();
18886             }, this.timer);
18887         }
18888         
18889         if(this.showarrow){
18890             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18891             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18892         }
18893         
18894         
18895     },
18896     
18897 //    onTouchStart : function(e, el, o)
18898 //    {
18899 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18900 //            return;
18901 //        }
18902 //        
18903 //        this.showPanelNext();
18904 //    },
18905     
18906     
18907     getChildContainer : function()
18908     {
18909         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18910     },
18911     
18912     /**
18913     * register a Navigation item
18914     * @param {Roo.bootstrap.NavItem} the navitem to add
18915     */
18916     register : function(item)
18917     {
18918         this.tabs.push( item);
18919         item.navId = this.navId; // not really needed..
18920         this.addBullet();
18921     
18922     },
18923     
18924     getActivePanel : function()
18925     {
18926         var r = false;
18927         Roo.each(this.tabs, function(t) {
18928             if (t.active) {
18929                 r = t;
18930                 return false;
18931             }
18932             return null;
18933         });
18934         return r;
18935         
18936     },
18937     getPanelByName : function(n)
18938     {
18939         var r = false;
18940         Roo.each(this.tabs, function(t) {
18941             if (t.tabId == n) {
18942                 r = t;
18943                 return false;
18944             }
18945             return null;
18946         });
18947         return r;
18948     },
18949     indexOfPanel : function(p)
18950     {
18951         var r = false;
18952         Roo.each(this.tabs, function(t,i) {
18953             if (t.tabId == p.tabId) {
18954                 r = i;
18955                 return false;
18956             }
18957             return null;
18958         });
18959         return r;
18960     },
18961     /**
18962      * show a specific panel
18963      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18964      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18965      */
18966     showPanel : function (pan)
18967     {
18968         if(this.transition || typeof(pan) == 'undefined'){
18969             Roo.log("waiting for the transitionend");
18970             return false;
18971         }
18972         
18973         if (typeof(pan) == 'number') {
18974             pan = this.tabs[pan];
18975         }
18976         
18977         if (typeof(pan) == 'string') {
18978             pan = this.getPanelByName(pan);
18979         }
18980         
18981         var cur = this.getActivePanel();
18982         
18983         if(!pan || !cur){
18984             Roo.log('pan or acitve pan is undefined');
18985             return false;
18986         }
18987         
18988         if (pan.tabId == this.getActivePanel().tabId) {
18989             return true;
18990         }
18991         
18992         if (false === cur.fireEvent('beforedeactivate')) {
18993             return false;
18994         }
18995         
18996         if(this.bullets > 0 && !Roo.isTouch){
18997             this.setActiveBullet(this.indexOfPanel(pan));
18998         }
18999         
19000         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
19001             
19002             //class="carousel-item carousel-item-next carousel-item-left"
19003             
19004             this.transition = true;
19005             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
19006             var lr = dir == 'next' ? 'left' : 'right';
19007             pan.el.addClass(dir); // or prev
19008             pan.el.addClass('carousel-item-' + dir); // or prev
19009             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
19010             cur.el.addClass(lr); // or right
19011             pan.el.addClass(lr);
19012             cur.el.addClass('carousel-item-' +lr); // or right
19013             pan.el.addClass('carousel-item-' +lr);
19014             
19015             
19016             var _this = this;
19017             cur.el.on('transitionend', function() {
19018                 Roo.log("trans end?");
19019                 
19020                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
19021                 pan.setActive(true);
19022                 
19023                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
19024                 cur.setActive(false);
19025                 
19026                 _this.transition = false;
19027                 
19028             }, this, { single:  true } );
19029             
19030             return true;
19031         }
19032         
19033         cur.setActive(false);
19034         pan.setActive(true);
19035         
19036         return true;
19037         
19038     },
19039     showPanelNext : function()
19040     {
19041         var i = this.indexOfPanel(this.getActivePanel());
19042         
19043         if (i >= this.tabs.length - 1 && !this.autoslide) {
19044             return;
19045         }
19046         
19047         if (i >= this.tabs.length - 1 && this.autoslide) {
19048             i = -1;
19049         }
19050         
19051         this.showPanel(this.tabs[i+1]);
19052     },
19053     
19054     showPanelPrev : function()
19055     {
19056         var i = this.indexOfPanel(this.getActivePanel());
19057         
19058         if (i  < 1 && !this.autoslide) {
19059             return;
19060         }
19061         
19062         if (i < 1 && this.autoslide) {
19063             i = this.tabs.length;
19064         }
19065         
19066         this.showPanel(this.tabs[i-1]);
19067     },
19068     
19069     
19070     addBullet: function()
19071     {
19072         if(!this.bullets || Roo.isTouch){
19073             return;
19074         }
19075         var ctr = this.el.select('.carousel-bullets',true).first();
19076         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
19077         var bullet = ctr.createChild({
19078             cls : 'bullet bullet-' + i
19079         },ctr.dom.lastChild);
19080         
19081         
19082         var _this = this;
19083         
19084         bullet.on('click', (function(e, el, o, ii, t){
19085
19086             e.preventDefault();
19087
19088             this.showPanel(ii);
19089
19090             if(this.autoslide && this.slideFn){
19091                 clearInterval(this.slideFn);
19092                 this.slideFn = window.setInterval(function() {
19093                     _this.showPanelNext();
19094                 }, this.timer);
19095             }
19096
19097         }).createDelegate(this, [i, bullet], true));
19098                 
19099         
19100     },
19101      
19102     setActiveBullet : function(i)
19103     {
19104         if(Roo.isTouch){
19105             return;
19106         }
19107         
19108         Roo.each(this.el.select('.bullet', true).elements, function(el){
19109             el.removeClass('selected');
19110         });
19111
19112         var bullet = this.el.select('.bullet-' + i, true).first();
19113         
19114         if(!bullet){
19115             return;
19116         }
19117         
19118         bullet.addClass('selected');
19119     }
19120     
19121     
19122   
19123 });
19124
19125  
19126
19127  
19128  
19129 Roo.apply(Roo.bootstrap.TabGroup, {
19130     
19131     groups: {},
19132      /**
19133     * register a Navigation Group
19134     * @param {Roo.bootstrap.NavGroup} the navgroup to add
19135     */
19136     register : function(navgrp)
19137     {
19138         this.groups[navgrp.navId] = navgrp;
19139         
19140     },
19141     /**
19142     * fetch a Navigation Group based on the navigation ID
19143     * if one does not exist , it will get created.
19144     * @param {string} the navgroup to add
19145     * @returns {Roo.bootstrap.NavGroup} the navgroup 
19146     */
19147     get: function(navId) {
19148         if (typeof(this.groups[navId]) == 'undefined') {
19149             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
19150         }
19151         return this.groups[navId] ;
19152     }
19153     
19154     
19155     
19156 });
19157
19158  /*
19159  * - LGPL
19160  *
19161  * TabPanel
19162  * 
19163  */
19164
19165 /**
19166  * @class Roo.bootstrap.TabPanel
19167  * @extends Roo.bootstrap.Component
19168  * Bootstrap TabPanel class
19169  * @cfg {Boolean} active panel active
19170  * @cfg {String} html panel content
19171  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
19172  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
19173  * @cfg {String} href click to link..
19174  * 
19175  * 
19176  * @constructor
19177  * Create a new TabPanel
19178  * @param {Object} config The config object
19179  */
19180
19181 Roo.bootstrap.TabPanel = function(config){
19182     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
19183     this.addEvents({
19184         /**
19185              * @event changed
19186              * Fires when the active status changes
19187              * @param {Roo.bootstrap.TabPanel} this
19188              * @param {Boolean} state the new state
19189             
19190          */
19191         'changed': true,
19192         /**
19193              * @event beforedeactivate
19194              * Fires before a tab is de-activated - can be used to do validation on a form.
19195              * @param {Roo.bootstrap.TabPanel} this
19196              * @return {Boolean} false if there is an error
19197             
19198          */
19199         'beforedeactivate': true
19200      });
19201     
19202     this.tabId = this.tabId || Roo.id();
19203   
19204 };
19205
19206 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
19207     
19208     active: false,
19209     html: false,
19210     tabId: false,
19211     navId : false,
19212     href : '',
19213     
19214     getAutoCreate : function(){
19215         
19216         
19217         var cfg = {
19218             tag: 'div',
19219             // item is needed for carousel - not sure if it has any effect otherwise
19220             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
19221             html: this.html || ''
19222         };
19223         
19224         if(this.active){
19225             cfg.cls += ' active';
19226         }
19227         
19228         if(this.tabId){
19229             cfg.tabId = this.tabId;
19230         }
19231         
19232         
19233         
19234         return cfg;
19235     },
19236     
19237     initEvents:  function()
19238     {
19239         var p = this.parent();
19240         
19241         this.navId = this.navId || p.navId;
19242         
19243         if (typeof(this.navId) != 'undefined') {
19244             // not really needed.. but just in case.. parent should be a NavGroup.
19245             var tg = Roo.bootstrap.TabGroup.get(this.navId);
19246             
19247             tg.register(this);
19248             
19249             var i = tg.tabs.length - 1;
19250             
19251             if(this.active && tg.bullets > 0 && i < tg.bullets){
19252                 tg.setActiveBullet(i);
19253             }
19254         }
19255         
19256         this.el.on('click', this.onClick, this);
19257         
19258         if(Roo.isTouch){
19259             this.el.on("touchstart", this.onTouchStart, this);
19260             this.el.on("touchmove", this.onTouchMove, this);
19261             this.el.on("touchend", this.onTouchEnd, this);
19262         }
19263         
19264     },
19265     
19266     onRender : function(ct, position)
19267     {
19268         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
19269     },
19270     
19271     setActive : function(state)
19272     {
19273         Roo.log("panel - set active " + this.tabId + "=" + state);
19274         
19275         this.active = state;
19276         if (!state) {
19277             this.el.removeClass('active');
19278             
19279         } else  if (!this.el.hasClass('active')) {
19280             this.el.addClass('active');
19281         }
19282         
19283         this.fireEvent('changed', this, state);
19284     },
19285     
19286     onClick : function(e)
19287     {
19288         e.preventDefault();
19289         
19290         if(!this.href.length){
19291             return;
19292         }
19293         
19294         window.location.href = this.href;
19295     },
19296     
19297     startX : 0,
19298     startY : 0,
19299     endX : 0,
19300     endY : 0,
19301     swiping : false,
19302     
19303     onTouchStart : function(e)
19304     {
19305         this.swiping = false;
19306         
19307         this.startX = e.browserEvent.touches[0].clientX;
19308         this.startY = e.browserEvent.touches[0].clientY;
19309     },
19310     
19311     onTouchMove : function(e)
19312     {
19313         this.swiping = true;
19314         
19315         this.endX = e.browserEvent.touches[0].clientX;
19316         this.endY = e.browserEvent.touches[0].clientY;
19317     },
19318     
19319     onTouchEnd : function(e)
19320     {
19321         if(!this.swiping){
19322             this.onClick(e);
19323             return;
19324         }
19325         
19326         var tabGroup = this.parent();
19327         
19328         if(this.endX > this.startX){ // swiping right
19329             tabGroup.showPanelPrev();
19330             return;
19331         }
19332         
19333         if(this.startX > this.endX){ // swiping left
19334             tabGroup.showPanelNext();
19335             return;
19336         }
19337     }
19338     
19339     
19340 });
19341  
19342
19343  
19344
19345  /*
19346  * - LGPL
19347  *
19348  * DateField
19349  * 
19350  */
19351
19352 /**
19353  * @class Roo.bootstrap.DateField
19354  * @extends Roo.bootstrap.Input
19355  * Bootstrap DateField class
19356  * @cfg {Number} weekStart default 0
19357  * @cfg {String} viewMode default empty, (months|years)
19358  * @cfg {String} minViewMode default empty, (months|years)
19359  * @cfg {Number} startDate default -Infinity
19360  * @cfg {Number} endDate default Infinity
19361  * @cfg {Boolean} todayHighlight default false
19362  * @cfg {Boolean} todayBtn default false
19363  * @cfg {Boolean} calendarWeeks default false
19364  * @cfg {Object} daysOfWeekDisabled default empty
19365  * @cfg {Boolean} singleMode default false (true | false)
19366  * 
19367  * @cfg {Boolean} keyboardNavigation default true
19368  * @cfg {String} language default en
19369  * 
19370  * @constructor
19371  * Create a new DateField
19372  * @param {Object} config The config object
19373  */
19374
19375 Roo.bootstrap.DateField = function(config){
19376     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
19377      this.addEvents({
19378             /**
19379              * @event show
19380              * Fires when this field show.
19381              * @param {Roo.bootstrap.DateField} this
19382              * @param {Mixed} date The date value
19383              */
19384             show : true,
19385             /**
19386              * @event show
19387              * Fires when this field hide.
19388              * @param {Roo.bootstrap.DateField} this
19389              * @param {Mixed} date The date value
19390              */
19391             hide : true,
19392             /**
19393              * @event select
19394              * Fires when select a date.
19395              * @param {Roo.bootstrap.DateField} this
19396              * @param {Mixed} date The date value
19397              */
19398             select : true,
19399             /**
19400              * @event beforeselect
19401              * Fires when before select a date.
19402              * @param {Roo.bootstrap.DateField} this
19403              * @param {Mixed} date The date value
19404              */
19405             beforeselect : true
19406         });
19407 };
19408
19409 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
19410     
19411     /**
19412      * @cfg {String} format
19413      * The default date format string which can be overriden for localization support.  The format must be
19414      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
19415      */
19416     format : "m/d/y",
19417     /**
19418      * @cfg {String} altFormats
19419      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
19420      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
19421      */
19422     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
19423     
19424     weekStart : 0,
19425     
19426     viewMode : '',
19427     
19428     minViewMode : '',
19429     
19430     todayHighlight : false,
19431     
19432     todayBtn: false,
19433     
19434     language: 'en',
19435     
19436     keyboardNavigation: true,
19437     
19438     calendarWeeks: false,
19439     
19440     startDate: -Infinity,
19441     
19442     endDate: Infinity,
19443     
19444     daysOfWeekDisabled: [],
19445     
19446     _events: [],
19447     
19448     singleMode : false,
19449     
19450     UTCDate: function()
19451     {
19452         return new Date(Date.UTC.apply(Date, arguments));
19453     },
19454     
19455     UTCToday: function()
19456     {
19457         var today = new Date();
19458         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
19459     },
19460     
19461     getDate: function() {
19462             var d = this.getUTCDate();
19463             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
19464     },
19465     
19466     getUTCDate: function() {
19467             return this.date;
19468     },
19469     
19470     setDate: function(d) {
19471             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
19472     },
19473     
19474     setUTCDate: function(d) {
19475             this.date = d;
19476             this.setValue(this.formatDate(this.date));
19477     },
19478         
19479     onRender: function(ct, position)
19480     {
19481         
19482         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
19483         
19484         this.language = this.language || 'en';
19485         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
19486         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
19487         
19488         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
19489         this.format = this.format || 'm/d/y';
19490         this.isInline = false;
19491         this.isInput = true;
19492         this.component = this.el.select('.add-on', true).first() || false;
19493         this.component = (this.component && this.component.length === 0) ? false : this.component;
19494         this.hasInput = this.component && this.inputEl().length;
19495         
19496         if (typeof(this.minViewMode === 'string')) {
19497             switch (this.minViewMode) {
19498                 case 'months':
19499                     this.minViewMode = 1;
19500                     break;
19501                 case 'years':
19502                     this.minViewMode = 2;
19503                     break;
19504                 default:
19505                     this.minViewMode = 0;
19506                     break;
19507             }
19508         }
19509         
19510         if (typeof(this.viewMode === 'string')) {
19511             switch (this.viewMode) {
19512                 case 'months':
19513                     this.viewMode = 1;
19514                     break;
19515                 case 'years':
19516                     this.viewMode = 2;
19517                     break;
19518                 default:
19519                     this.viewMode = 0;
19520                     break;
19521             }
19522         }
19523                 
19524         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19525         
19526 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19527         
19528         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19529         
19530         this.picker().on('mousedown', this.onMousedown, this);
19531         this.picker().on('click', this.onClick, this);
19532         
19533         this.picker().addClass('datepicker-dropdown');
19534         
19535         this.startViewMode = this.viewMode;
19536         
19537         if(this.singleMode){
19538             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19539                 v.setVisibilityMode(Roo.Element.DISPLAY);
19540                 v.hide();
19541             });
19542             
19543             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19544                 v.setStyle('width', '189px');
19545             });
19546         }
19547         
19548         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19549             if(!this.calendarWeeks){
19550                 v.remove();
19551                 return;
19552             }
19553             
19554             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19555             v.attr('colspan', function(i, val){
19556                 return parseInt(val) + 1;
19557             });
19558         });
19559                         
19560         
19561         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19562         
19563         this.setStartDate(this.startDate);
19564         this.setEndDate(this.endDate);
19565         
19566         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19567         
19568         this.fillDow();
19569         this.fillMonths();
19570         this.update();
19571         this.showMode();
19572         
19573         if(this.isInline) {
19574             this.showPopup();
19575         }
19576     },
19577     
19578     picker : function()
19579     {
19580         return this.pickerEl;
19581 //        return this.el.select('.datepicker', true).first();
19582     },
19583     
19584     fillDow: function()
19585     {
19586         var dowCnt = this.weekStart;
19587         
19588         var dow = {
19589             tag: 'tr',
19590             cn: [
19591                 
19592             ]
19593         };
19594         
19595         if(this.calendarWeeks){
19596             dow.cn.push({
19597                 tag: 'th',
19598                 cls: 'cw',
19599                 html: '&nbsp;'
19600             })
19601         }
19602         
19603         while (dowCnt < this.weekStart + 7) {
19604             dow.cn.push({
19605                 tag: 'th',
19606                 cls: 'dow',
19607                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19608             });
19609         }
19610         
19611         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19612     },
19613     
19614     fillMonths: function()
19615     {    
19616         var i = 0;
19617         var months = this.picker().select('>.datepicker-months td', true).first();
19618         
19619         months.dom.innerHTML = '';
19620         
19621         while (i < 12) {
19622             var month = {
19623                 tag: 'span',
19624                 cls: 'month',
19625                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19626             };
19627             
19628             months.createChild(month);
19629         }
19630         
19631     },
19632     
19633     update: function()
19634     {
19635         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;
19636         
19637         if (this.date < this.startDate) {
19638             this.viewDate = new Date(this.startDate);
19639         } else if (this.date > this.endDate) {
19640             this.viewDate = new Date(this.endDate);
19641         } else {
19642             this.viewDate = new Date(this.date);
19643         }
19644         
19645         this.fill();
19646     },
19647     
19648     fill: function() 
19649     {
19650         var d = new Date(this.viewDate),
19651                 year = d.getUTCFullYear(),
19652                 month = d.getUTCMonth(),
19653                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19654                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19655                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19656                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19657                 currentDate = this.date && this.date.valueOf(),
19658                 today = this.UTCToday();
19659         
19660         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19661         
19662 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19663         
19664 //        this.picker.select('>tfoot th.today').
19665 //                                              .text(dates[this.language].today)
19666 //                                              .toggle(this.todayBtn !== false);
19667     
19668         this.updateNavArrows();
19669         this.fillMonths();
19670                                                 
19671         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19672         
19673         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19674          
19675         prevMonth.setUTCDate(day);
19676         
19677         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19678         
19679         var nextMonth = new Date(prevMonth);
19680         
19681         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19682         
19683         nextMonth = nextMonth.valueOf();
19684         
19685         var fillMonths = false;
19686         
19687         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19688         
19689         while(prevMonth.valueOf() <= nextMonth) {
19690             var clsName = '';
19691             
19692             if (prevMonth.getUTCDay() === this.weekStart) {
19693                 if(fillMonths){
19694                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19695                 }
19696                     
19697                 fillMonths = {
19698                     tag: 'tr',
19699                     cn: []
19700                 };
19701                 
19702                 if(this.calendarWeeks){
19703                     // ISO 8601: First week contains first thursday.
19704                     // ISO also states week starts on Monday, but we can be more abstract here.
19705                     var
19706                     // Start of current week: based on weekstart/current date
19707                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19708                     // Thursday of this week
19709                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19710                     // First Thursday of year, year from thursday
19711                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19712                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19713                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19714                     
19715                     fillMonths.cn.push({
19716                         tag: 'td',
19717                         cls: 'cw',
19718                         html: calWeek
19719                     });
19720                 }
19721             }
19722             
19723             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19724                 clsName += ' old';
19725             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19726                 clsName += ' new';
19727             }
19728             if (this.todayHighlight &&
19729                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19730                 prevMonth.getUTCMonth() == today.getMonth() &&
19731                 prevMonth.getUTCDate() == today.getDate()) {
19732                 clsName += ' today';
19733             }
19734             
19735             if (currentDate && prevMonth.valueOf() === currentDate) {
19736                 clsName += ' active';
19737             }
19738             
19739             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19740                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19741                     clsName += ' disabled';
19742             }
19743             
19744             fillMonths.cn.push({
19745                 tag: 'td',
19746                 cls: 'day ' + clsName,
19747                 html: prevMonth.getDate()
19748             });
19749             
19750             prevMonth.setDate(prevMonth.getDate()+1);
19751         }
19752           
19753         var currentYear = this.date && this.date.getUTCFullYear();
19754         var currentMonth = this.date && this.date.getUTCMonth();
19755         
19756         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19757         
19758         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19759             v.removeClass('active');
19760             
19761             if(currentYear === year && k === currentMonth){
19762                 v.addClass('active');
19763             }
19764             
19765             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19766                 v.addClass('disabled');
19767             }
19768             
19769         });
19770         
19771         
19772         year = parseInt(year/10, 10) * 10;
19773         
19774         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19775         
19776         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19777         
19778         year -= 1;
19779         for (var i = -1; i < 11; i++) {
19780             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19781                 tag: 'span',
19782                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19783                 html: year
19784             });
19785             
19786             year += 1;
19787         }
19788     },
19789     
19790     showMode: function(dir) 
19791     {
19792         if (dir) {
19793             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19794         }
19795         
19796         Roo.each(this.picker().select('>div',true).elements, function(v){
19797             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19798             v.hide();
19799         });
19800         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19801     },
19802     
19803     place: function()
19804     {
19805         if(this.isInline) {
19806             return;
19807         }
19808         
19809         this.picker().removeClass(['bottom', 'top']);
19810         
19811         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19812             /*
19813              * place to the top of element!
19814              *
19815              */
19816             
19817             this.picker().addClass('top');
19818             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19819             
19820             return;
19821         }
19822         
19823         this.picker().addClass('bottom');
19824         
19825         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19826     },
19827     
19828     parseDate : function(value)
19829     {
19830         if(!value || value instanceof Date){
19831             return value;
19832         }
19833         var v = Date.parseDate(value, this.format);
19834         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19835             v = Date.parseDate(value, 'Y-m-d');
19836         }
19837         if(!v && this.altFormats){
19838             if(!this.altFormatsArray){
19839                 this.altFormatsArray = this.altFormats.split("|");
19840             }
19841             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19842                 v = Date.parseDate(value, this.altFormatsArray[i]);
19843             }
19844         }
19845         return v;
19846     },
19847     
19848     formatDate : function(date, fmt)
19849     {   
19850         return (!date || !(date instanceof Date)) ?
19851         date : date.dateFormat(fmt || this.format);
19852     },
19853     
19854     onFocus : function()
19855     {
19856         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19857         this.showPopup();
19858     },
19859     
19860     onBlur : function()
19861     {
19862         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19863         
19864         var d = this.inputEl().getValue();
19865         
19866         this.setValue(d);
19867                 
19868         this.hidePopup();
19869     },
19870     
19871     showPopup : function()
19872     {
19873         this.picker().show();
19874         this.update();
19875         this.place();
19876         
19877         this.fireEvent('showpopup', this, this.date);
19878     },
19879     
19880     hidePopup : function()
19881     {
19882         if(this.isInline) {
19883             return;
19884         }
19885         this.picker().hide();
19886         this.viewMode = this.startViewMode;
19887         this.showMode();
19888         
19889         this.fireEvent('hidepopup', this, this.date);
19890         
19891     },
19892     
19893     onMousedown: function(e)
19894     {
19895         e.stopPropagation();
19896         e.preventDefault();
19897     },
19898     
19899     keyup: function(e)
19900     {
19901         Roo.bootstrap.DateField.superclass.keyup.call(this);
19902         this.update();
19903     },
19904
19905     setValue: function(v)
19906     {
19907         if(this.fireEvent('beforeselect', this, v) !== false){
19908             var d = new Date(this.parseDate(v) ).clearTime();
19909         
19910             if(isNaN(d.getTime())){
19911                 this.date = this.viewDate = '';
19912                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19913                 return;
19914             }
19915
19916             v = this.formatDate(d);
19917
19918             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19919
19920             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19921
19922             this.update();
19923
19924             this.fireEvent('select', this, this.date);
19925         }
19926     },
19927     
19928     getValue: function()
19929     {
19930         return this.formatDate(this.date);
19931     },
19932     
19933     fireKey: function(e)
19934     {
19935         if (!this.picker().isVisible()){
19936             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19937                 this.showPopup();
19938             }
19939             return;
19940         }
19941         
19942         var dateChanged = false,
19943         dir, day, month,
19944         newDate, newViewDate;
19945         
19946         switch(e.keyCode){
19947             case 27: // escape
19948                 this.hidePopup();
19949                 e.preventDefault();
19950                 break;
19951             case 37: // left
19952             case 39: // right
19953                 if (!this.keyboardNavigation) {
19954                     break;
19955                 }
19956                 dir = e.keyCode == 37 ? -1 : 1;
19957                 
19958                 if (e.ctrlKey){
19959                     newDate = this.moveYear(this.date, dir);
19960                     newViewDate = this.moveYear(this.viewDate, dir);
19961                 } else if (e.shiftKey){
19962                     newDate = this.moveMonth(this.date, dir);
19963                     newViewDate = this.moveMonth(this.viewDate, dir);
19964                 } else {
19965                     newDate = new Date(this.date);
19966                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19967                     newViewDate = new Date(this.viewDate);
19968                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19969                 }
19970                 if (this.dateWithinRange(newDate)){
19971                     this.date = newDate;
19972                     this.viewDate = newViewDate;
19973                     this.setValue(this.formatDate(this.date));
19974 //                    this.update();
19975                     e.preventDefault();
19976                     dateChanged = true;
19977                 }
19978                 break;
19979             case 38: // up
19980             case 40: // down
19981                 if (!this.keyboardNavigation) {
19982                     break;
19983                 }
19984                 dir = e.keyCode == 38 ? -1 : 1;
19985                 if (e.ctrlKey){
19986                     newDate = this.moveYear(this.date, dir);
19987                     newViewDate = this.moveYear(this.viewDate, dir);
19988                 } else if (e.shiftKey){
19989                     newDate = this.moveMonth(this.date, dir);
19990                     newViewDate = this.moveMonth(this.viewDate, dir);
19991                 } else {
19992                     newDate = new Date(this.date);
19993                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19994                     newViewDate = new Date(this.viewDate);
19995                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19996                 }
19997                 if (this.dateWithinRange(newDate)){
19998                     this.date = newDate;
19999                     this.viewDate = newViewDate;
20000                     this.setValue(this.formatDate(this.date));
20001 //                    this.update();
20002                     e.preventDefault();
20003                     dateChanged = true;
20004                 }
20005                 break;
20006             case 13: // enter
20007                 this.setValue(this.formatDate(this.date));
20008                 this.hidePopup();
20009                 e.preventDefault();
20010                 break;
20011             case 9: // tab
20012                 this.setValue(this.formatDate(this.date));
20013                 this.hidePopup();
20014                 break;
20015             case 16: // shift
20016             case 17: // ctrl
20017             case 18: // alt
20018                 break;
20019             default :
20020                 this.hidePopup();
20021                 
20022         }
20023     },
20024     
20025     
20026     onClick: function(e) 
20027     {
20028         e.stopPropagation();
20029         e.preventDefault();
20030         
20031         var target = e.getTarget();
20032         
20033         if(target.nodeName.toLowerCase() === 'i'){
20034             target = Roo.get(target).dom.parentNode;
20035         }
20036         
20037         var nodeName = target.nodeName;
20038         var className = target.className;
20039         var html = target.innerHTML;
20040         //Roo.log(nodeName);
20041         
20042         switch(nodeName.toLowerCase()) {
20043             case 'th':
20044                 switch(className) {
20045                     case 'switch':
20046                         this.showMode(1);
20047                         break;
20048                     case 'prev':
20049                     case 'next':
20050                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
20051                         switch(this.viewMode){
20052                                 case 0:
20053                                         this.viewDate = this.moveMonth(this.viewDate, dir);
20054                                         break;
20055                                 case 1:
20056                                 case 2:
20057                                         this.viewDate = this.moveYear(this.viewDate, dir);
20058                                         break;
20059                         }
20060                         this.fill();
20061                         break;
20062                     case 'today':
20063                         var date = new Date();
20064                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
20065 //                        this.fill()
20066                         this.setValue(this.formatDate(this.date));
20067                         
20068                         this.hidePopup();
20069                         break;
20070                 }
20071                 break;
20072             case 'span':
20073                 if (className.indexOf('disabled') < 0) {
20074                     this.viewDate.setUTCDate(1);
20075                     if (className.indexOf('month') > -1) {
20076                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
20077                     } else {
20078                         var year = parseInt(html, 10) || 0;
20079                         this.viewDate.setUTCFullYear(year);
20080                         
20081                     }
20082                     
20083                     if(this.singleMode){
20084                         this.setValue(this.formatDate(this.viewDate));
20085                         this.hidePopup();
20086                         return;
20087                     }
20088                     
20089                     this.showMode(-1);
20090                     this.fill();
20091                 }
20092                 break;
20093                 
20094             case 'td':
20095                 //Roo.log(className);
20096                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
20097                     var day = parseInt(html, 10) || 1;
20098                     var year = this.viewDate.getUTCFullYear(),
20099                         month = this.viewDate.getUTCMonth();
20100
20101                     if (className.indexOf('old') > -1) {
20102                         if(month === 0 ){
20103                             month = 11;
20104                             year -= 1;
20105                         }else{
20106                             month -= 1;
20107                         }
20108                     } else if (className.indexOf('new') > -1) {
20109                         if (month == 11) {
20110                             month = 0;
20111                             year += 1;
20112                         } else {
20113                             month += 1;
20114                         }
20115                     }
20116                     //Roo.log([year,month,day]);
20117                     this.date = this.UTCDate(year, month, day,0,0,0,0);
20118                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
20119 //                    this.fill();
20120                     //Roo.log(this.formatDate(this.date));
20121                     this.setValue(this.formatDate(this.date));
20122                     this.hidePopup();
20123                 }
20124                 break;
20125         }
20126     },
20127     
20128     setStartDate: function(startDate)
20129     {
20130         this.startDate = startDate || -Infinity;
20131         if (this.startDate !== -Infinity) {
20132             this.startDate = this.parseDate(this.startDate);
20133         }
20134         this.update();
20135         this.updateNavArrows();
20136     },
20137
20138     setEndDate: function(endDate)
20139     {
20140         this.endDate = endDate || Infinity;
20141         if (this.endDate !== Infinity) {
20142             this.endDate = this.parseDate(this.endDate);
20143         }
20144         this.update();
20145         this.updateNavArrows();
20146     },
20147     
20148     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
20149     {
20150         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
20151         if (typeof(this.daysOfWeekDisabled) !== 'object') {
20152             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
20153         }
20154         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
20155             return parseInt(d, 10);
20156         });
20157         this.update();
20158         this.updateNavArrows();
20159     },
20160     
20161     updateNavArrows: function() 
20162     {
20163         if(this.singleMode){
20164             return;
20165         }
20166         
20167         var d = new Date(this.viewDate),
20168         year = d.getUTCFullYear(),
20169         month = d.getUTCMonth();
20170         
20171         Roo.each(this.picker().select('.prev', true).elements, function(v){
20172             v.show();
20173             switch (this.viewMode) {
20174                 case 0:
20175
20176                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
20177                         v.hide();
20178                     }
20179                     break;
20180                 case 1:
20181                 case 2:
20182                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
20183                         v.hide();
20184                     }
20185                     break;
20186             }
20187         });
20188         
20189         Roo.each(this.picker().select('.next', true).elements, function(v){
20190             v.show();
20191             switch (this.viewMode) {
20192                 case 0:
20193
20194                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
20195                         v.hide();
20196                     }
20197                     break;
20198                 case 1:
20199                 case 2:
20200                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
20201                         v.hide();
20202                     }
20203                     break;
20204             }
20205         })
20206     },
20207     
20208     moveMonth: function(date, dir)
20209     {
20210         if (!dir) {
20211             return date;
20212         }
20213         var new_date = new Date(date.valueOf()),
20214         day = new_date.getUTCDate(),
20215         month = new_date.getUTCMonth(),
20216         mag = Math.abs(dir),
20217         new_month, test;
20218         dir = dir > 0 ? 1 : -1;
20219         if (mag == 1){
20220             test = dir == -1
20221             // If going back one month, make sure month is not current month
20222             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
20223             ? function(){
20224                 return new_date.getUTCMonth() == month;
20225             }
20226             // If going forward one month, make sure month is as expected
20227             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
20228             : function(){
20229                 return new_date.getUTCMonth() != new_month;
20230             };
20231             new_month = month + dir;
20232             new_date.setUTCMonth(new_month);
20233             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
20234             if (new_month < 0 || new_month > 11) {
20235                 new_month = (new_month + 12) % 12;
20236             }
20237         } else {
20238             // For magnitudes >1, move one month at a time...
20239             for (var i=0; i<mag; i++) {
20240                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
20241                 new_date = this.moveMonth(new_date, dir);
20242             }
20243             // ...then reset the day, keeping it in the new month
20244             new_month = new_date.getUTCMonth();
20245             new_date.setUTCDate(day);
20246             test = function(){
20247                 return new_month != new_date.getUTCMonth();
20248             };
20249         }
20250         // Common date-resetting loop -- if date is beyond end of month, make it
20251         // end of month
20252         while (test()){
20253             new_date.setUTCDate(--day);
20254             new_date.setUTCMonth(new_month);
20255         }
20256         return new_date;
20257     },
20258
20259     moveYear: function(date, dir)
20260     {
20261         return this.moveMonth(date, dir*12);
20262     },
20263
20264     dateWithinRange: function(date)
20265     {
20266         return date >= this.startDate && date <= this.endDate;
20267     },
20268
20269     
20270     remove: function() 
20271     {
20272         this.picker().remove();
20273     },
20274     
20275     validateValue : function(value)
20276     {
20277         if(this.getVisibilityEl().hasClass('hidden')){
20278             return true;
20279         }
20280         
20281         if(value.length < 1)  {
20282             if(this.allowBlank){
20283                 return true;
20284             }
20285             return false;
20286         }
20287         
20288         if(value.length < this.minLength){
20289             return false;
20290         }
20291         if(value.length > this.maxLength){
20292             return false;
20293         }
20294         if(this.vtype){
20295             var vt = Roo.form.VTypes;
20296             if(!vt[this.vtype](value, this)){
20297                 return false;
20298             }
20299         }
20300         if(typeof this.validator == "function"){
20301             var msg = this.validator(value);
20302             if(msg !== true){
20303                 return false;
20304             }
20305         }
20306         
20307         if(this.regex && !this.regex.test(value)){
20308             return false;
20309         }
20310         
20311         if(typeof(this.parseDate(value)) == 'undefined'){
20312             return false;
20313         }
20314         
20315         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
20316             return false;
20317         }      
20318         
20319         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
20320             return false;
20321         } 
20322         
20323         
20324         return true;
20325     },
20326     
20327     reset : function()
20328     {
20329         this.date = this.viewDate = '';
20330         
20331         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20332     }
20333    
20334 });
20335
20336 Roo.apply(Roo.bootstrap.DateField,  {
20337     
20338     head : {
20339         tag: 'thead',
20340         cn: [
20341         {
20342             tag: 'tr',
20343             cn: [
20344             {
20345                 tag: 'th',
20346                 cls: 'prev',
20347                 html: '<i class="fa fa-arrow-left"/>'
20348             },
20349             {
20350                 tag: 'th',
20351                 cls: 'switch',
20352                 colspan: '5'
20353             },
20354             {
20355                 tag: 'th',
20356                 cls: 'next',
20357                 html: '<i class="fa fa-arrow-right"/>'
20358             }
20359
20360             ]
20361         }
20362         ]
20363     },
20364     
20365     content : {
20366         tag: 'tbody',
20367         cn: [
20368         {
20369             tag: 'tr',
20370             cn: [
20371             {
20372                 tag: 'td',
20373                 colspan: '7'
20374             }
20375             ]
20376         }
20377         ]
20378     },
20379     
20380     footer : {
20381         tag: 'tfoot',
20382         cn: [
20383         {
20384             tag: 'tr',
20385             cn: [
20386             {
20387                 tag: 'th',
20388                 colspan: '7',
20389                 cls: 'today'
20390             }
20391                     
20392             ]
20393         }
20394         ]
20395     },
20396     
20397     dates:{
20398         en: {
20399             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
20400             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
20401             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
20402             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20403             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
20404             today: "Today"
20405         }
20406     },
20407     
20408     modes: [
20409     {
20410         clsName: 'days',
20411         navFnc: 'Month',
20412         navStep: 1
20413     },
20414     {
20415         clsName: 'months',
20416         navFnc: 'FullYear',
20417         navStep: 1
20418     },
20419     {
20420         clsName: 'years',
20421         navFnc: 'FullYear',
20422         navStep: 10
20423     }]
20424 });
20425
20426 Roo.apply(Roo.bootstrap.DateField,  {
20427   
20428     template : {
20429         tag: 'div',
20430         cls: 'datepicker dropdown-menu roo-dynamic',
20431         cn: [
20432         {
20433             tag: 'div',
20434             cls: 'datepicker-days',
20435             cn: [
20436             {
20437                 tag: 'table',
20438                 cls: 'table-condensed',
20439                 cn:[
20440                 Roo.bootstrap.DateField.head,
20441                 {
20442                     tag: 'tbody'
20443                 },
20444                 Roo.bootstrap.DateField.footer
20445                 ]
20446             }
20447             ]
20448         },
20449         {
20450             tag: 'div',
20451             cls: 'datepicker-months',
20452             cn: [
20453             {
20454                 tag: 'table',
20455                 cls: 'table-condensed',
20456                 cn:[
20457                 Roo.bootstrap.DateField.head,
20458                 Roo.bootstrap.DateField.content,
20459                 Roo.bootstrap.DateField.footer
20460                 ]
20461             }
20462             ]
20463         },
20464         {
20465             tag: 'div',
20466             cls: 'datepicker-years',
20467             cn: [
20468             {
20469                 tag: 'table',
20470                 cls: 'table-condensed',
20471                 cn:[
20472                 Roo.bootstrap.DateField.head,
20473                 Roo.bootstrap.DateField.content,
20474                 Roo.bootstrap.DateField.footer
20475                 ]
20476             }
20477             ]
20478         }
20479         ]
20480     }
20481 });
20482
20483  
20484
20485  /*
20486  * - LGPL
20487  *
20488  * TimeField
20489  * 
20490  */
20491
20492 /**
20493  * @class Roo.bootstrap.TimeField
20494  * @extends Roo.bootstrap.Input
20495  * Bootstrap DateField class
20496  * 
20497  * 
20498  * @constructor
20499  * Create a new TimeField
20500  * @param {Object} config The config object
20501  */
20502
20503 Roo.bootstrap.TimeField = function(config){
20504     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
20505     this.addEvents({
20506             /**
20507              * @event show
20508              * Fires when this field show.
20509              * @param {Roo.bootstrap.DateField} thisthis
20510              * @param {Mixed} date The date value
20511              */
20512             show : true,
20513             /**
20514              * @event show
20515              * Fires when this field hide.
20516              * @param {Roo.bootstrap.DateField} this
20517              * @param {Mixed} date The date value
20518              */
20519             hide : true,
20520             /**
20521              * @event select
20522              * Fires when select a date.
20523              * @param {Roo.bootstrap.DateField} this
20524              * @param {Mixed} date The date value
20525              */
20526             select : true
20527         });
20528 };
20529
20530 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
20531     
20532     /**
20533      * @cfg {String} format
20534      * The default time format string which can be overriden for localization support.  The format must be
20535      * valid according to {@link Date#parseDate} (defaults to 'H:i').
20536      */
20537     format : "H:i",
20538        
20539     onRender: function(ct, position)
20540     {
20541         
20542         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20543                 
20544         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20545         
20546         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20547         
20548         this.pop = this.picker().select('>.datepicker-time',true).first();
20549         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20550         
20551         this.picker().on('mousedown', this.onMousedown, this);
20552         this.picker().on('click', this.onClick, this);
20553         
20554         this.picker().addClass('datepicker-dropdown');
20555     
20556         this.fillTime();
20557         this.update();
20558             
20559         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20560         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20561         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20562         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20563         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20564         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20565
20566     },
20567     
20568     fireKey: function(e){
20569         if (!this.picker().isVisible()){
20570             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20571                 this.show();
20572             }
20573             return;
20574         }
20575
20576         e.preventDefault();
20577         
20578         switch(e.keyCode){
20579             case 27: // escape
20580                 this.hide();
20581                 break;
20582             case 37: // left
20583             case 39: // right
20584                 this.onTogglePeriod();
20585                 break;
20586             case 38: // up
20587                 this.onIncrementMinutes();
20588                 break;
20589             case 40: // down
20590                 this.onDecrementMinutes();
20591                 break;
20592             case 13: // enter
20593             case 9: // tab
20594                 this.setTime();
20595                 break;
20596         }
20597     },
20598     
20599     onClick: function(e) {
20600         e.stopPropagation();
20601         e.preventDefault();
20602     },
20603     
20604     picker : function()
20605     {
20606         return this.el.select('.datepicker', true).first();
20607     },
20608     
20609     fillTime: function()
20610     {    
20611         var time = this.pop.select('tbody', true).first();
20612         
20613         time.dom.innerHTML = '';
20614         
20615         time.createChild({
20616             tag: 'tr',
20617             cn: [
20618                 {
20619                     tag: 'td',
20620                     cn: [
20621                         {
20622                             tag: 'a',
20623                             href: '#',
20624                             cls: 'btn',
20625                             cn: [
20626                                 {
20627                                     tag: 'span',
20628                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20629                                 }
20630                             ]
20631                         } 
20632                     ]
20633                 },
20634                 {
20635                     tag: 'td',
20636                     cls: 'separator'
20637                 },
20638                 {
20639                     tag: 'td',
20640                     cn: [
20641                         {
20642                             tag: 'a',
20643                             href: '#',
20644                             cls: 'btn',
20645                             cn: [
20646                                 {
20647                                     tag: 'span',
20648                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20649                                 }
20650                             ]
20651                         }
20652                     ]
20653                 },
20654                 {
20655                     tag: 'td',
20656                     cls: 'separator'
20657                 }
20658             ]
20659         });
20660         
20661         time.createChild({
20662             tag: 'tr',
20663             cn: [
20664                 {
20665                     tag: 'td',
20666                     cn: [
20667                         {
20668                             tag: 'span',
20669                             cls: 'timepicker-hour',
20670                             html: '00'
20671                         }  
20672                     ]
20673                 },
20674                 {
20675                     tag: 'td',
20676                     cls: 'separator',
20677                     html: ':'
20678                 },
20679                 {
20680                     tag: 'td',
20681                     cn: [
20682                         {
20683                             tag: 'span',
20684                             cls: 'timepicker-minute',
20685                             html: '00'
20686                         }  
20687                     ]
20688                 },
20689                 {
20690                     tag: 'td',
20691                     cls: 'separator'
20692                 },
20693                 {
20694                     tag: 'td',
20695                     cn: [
20696                         {
20697                             tag: 'button',
20698                             type: 'button',
20699                             cls: 'btn btn-primary period',
20700                             html: 'AM'
20701                             
20702                         }
20703                     ]
20704                 }
20705             ]
20706         });
20707         
20708         time.createChild({
20709             tag: 'tr',
20710             cn: [
20711                 {
20712                     tag: 'td',
20713                     cn: [
20714                         {
20715                             tag: 'a',
20716                             href: '#',
20717                             cls: 'btn',
20718                             cn: [
20719                                 {
20720                                     tag: 'span',
20721                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20722                                 }
20723                             ]
20724                         }
20725                     ]
20726                 },
20727                 {
20728                     tag: 'td',
20729                     cls: 'separator'
20730                 },
20731                 {
20732                     tag: 'td',
20733                     cn: [
20734                         {
20735                             tag: 'a',
20736                             href: '#',
20737                             cls: 'btn',
20738                             cn: [
20739                                 {
20740                                     tag: 'span',
20741                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20742                                 }
20743                             ]
20744                         }
20745                     ]
20746                 },
20747                 {
20748                     tag: 'td',
20749                     cls: 'separator'
20750                 }
20751             ]
20752         });
20753         
20754     },
20755     
20756     update: function()
20757     {
20758         
20759         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20760         
20761         this.fill();
20762     },
20763     
20764     fill: function() 
20765     {
20766         var hours = this.time.getHours();
20767         var minutes = this.time.getMinutes();
20768         var period = 'AM';
20769         
20770         if(hours > 11){
20771             period = 'PM';
20772         }
20773         
20774         if(hours == 0){
20775             hours = 12;
20776         }
20777         
20778         
20779         if(hours > 12){
20780             hours = hours - 12;
20781         }
20782         
20783         if(hours < 10){
20784             hours = '0' + hours;
20785         }
20786         
20787         if(minutes < 10){
20788             minutes = '0' + minutes;
20789         }
20790         
20791         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20792         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20793         this.pop.select('button', true).first().dom.innerHTML = period;
20794         
20795     },
20796     
20797     place: function()
20798     {   
20799         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20800         
20801         var cls = ['bottom'];
20802         
20803         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20804             cls.pop();
20805             cls.push('top');
20806         }
20807         
20808         cls.push('right');
20809         
20810         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20811             cls.pop();
20812             cls.push('left');
20813         }
20814         
20815         this.picker().addClass(cls.join('-'));
20816         
20817         var _this = this;
20818         
20819         Roo.each(cls, function(c){
20820             if(c == 'bottom'){
20821                 _this.picker().setTop(_this.inputEl().getHeight());
20822                 return;
20823             }
20824             if(c == 'top'){
20825                 _this.picker().setTop(0 - _this.picker().getHeight());
20826                 return;
20827             }
20828             
20829             if(c == 'left'){
20830                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20831                 return;
20832             }
20833             if(c == 'right'){
20834                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20835                 return;
20836             }
20837         });
20838         
20839     },
20840   
20841     onFocus : function()
20842     {
20843         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20844         this.show();
20845     },
20846     
20847     onBlur : function()
20848     {
20849         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20850         this.hide();
20851     },
20852     
20853     show : function()
20854     {
20855         this.picker().show();
20856         this.pop.show();
20857         this.update();
20858         this.place();
20859         
20860         this.fireEvent('show', this, this.date);
20861     },
20862     
20863     hide : function()
20864     {
20865         this.picker().hide();
20866         this.pop.hide();
20867         
20868         this.fireEvent('hide', this, this.date);
20869     },
20870     
20871     setTime : function()
20872     {
20873         this.hide();
20874         this.setValue(this.time.format(this.format));
20875         
20876         this.fireEvent('select', this, this.date);
20877         
20878         
20879     },
20880     
20881     onMousedown: function(e){
20882         e.stopPropagation();
20883         e.preventDefault();
20884     },
20885     
20886     onIncrementHours: function()
20887     {
20888         Roo.log('onIncrementHours');
20889         this.time = this.time.add(Date.HOUR, 1);
20890         this.update();
20891         
20892     },
20893     
20894     onDecrementHours: function()
20895     {
20896         Roo.log('onDecrementHours');
20897         this.time = this.time.add(Date.HOUR, -1);
20898         this.update();
20899     },
20900     
20901     onIncrementMinutes: function()
20902     {
20903         Roo.log('onIncrementMinutes');
20904         this.time = this.time.add(Date.MINUTE, 1);
20905         this.update();
20906     },
20907     
20908     onDecrementMinutes: function()
20909     {
20910         Roo.log('onDecrementMinutes');
20911         this.time = this.time.add(Date.MINUTE, -1);
20912         this.update();
20913     },
20914     
20915     onTogglePeriod: function()
20916     {
20917         Roo.log('onTogglePeriod');
20918         this.time = this.time.add(Date.HOUR, 12);
20919         this.update();
20920     }
20921     
20922    
20923 });
20924
20925 Roo.apply(Roo.bootstrap.TimeField,  {
20926     
20927     content : {
20928         tag: 'tbody',
20929         cn: [
20930             {
20931                 tag: 'tr',
20932                 cn: [
20933                 {
20934                     tag: 'td',
20935                     colspan: '7'
20936                 }
20937                 ]
20938             }
20939         ]
20940     },
20941     
20942     footer : {
20943         tag: 'tfoot',
20944         cn: [
20945             {
20946                 tag: 'tr',
20947                 cn: [
20948                 {
20949                     tag: 'th',
20950                     colspan: '7',
20951                     cls: '',
20952                     cn: [
20953                         {
20954                             tag: 'button',
20955                             cls: 'btn btn-info ok',
20956                             html: 'OK'
20957                         }
20958                     ]
20959                 }
20960
20961                 ]
20962             }
20963         ]
20964     }
20965 });
20966
20967 Roo.apply(Roo.bootstrap.TimeField,  {
20968   
20969     template : {
20970         tag: 'div',
20971         cls: 'datepicker dropdown-menu',
20972         cn: [
20973             {
20974                 tag: 'div',
20975                 cls: 'datepicker-time',
20976                 cn: [
20977                 {
20978                     tag: 'table',
20979                     cls: 'table-condensed',
20980                     cn:[
20981                     Roo.bootstrap.TimeField.content,
20982                     Roo.bootstrap.TimeField.footer
20983                     ]
20984                 }
20985                 ]
20986             }
20987         ]
20988     }
20989 });
20990
20991  
20992
20993  /*
20994  * - LGPL
20995  *
20996  * MonthField
20997  * 
20998  */
20999
21000 /**
21001  * @class Roo.bootstrap.MonthField
21002  * @extends Roo.bootstrap.Input
21003  * Bootstrap MonthField class
21004  * 
21005  * @cfg {String} language default en
21006  * 
21007  * @constructor
21008  * Create a new MonthField
21009  * @param {Object} config The config object
21010  */
21011
21012 Roo.bootstrap.MonthField = function(config){
21013     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
21014     
21015     this.addEvents({
21016         /**
21017          * @event show
21018          * Fires when this field show.
21019          * @param {Roo.bootstrap.MonthField} this
21020          * @param {Mixed} date The date value
21021          */
21022         show : true,
21023         /**
21024          * @event show
21025          * Fires when this field hide.
21026          * @param {Roo.bootstrap.MonthField} this
21027          * @param {Mixed} date The date value
21028          */
21029         hide : true,
21030         /**
21031          * @event select
21032          * Fires when select a date.
21033          * @param {Roo.bootstrap.MonthField} this
21034          * @param {String} oldvalue The old value
21035          * @param {String} newvalue The new value
21036          */
21037         select : true
21038     });
21039 };
21040
21041 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
21042     
21043     onRender: function(ct, position)
21044     {
21045         
21046         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
21047         
21048         this.language = this.language || 'en';
21049         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
21050         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
21051         
21052         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
21053         this.isInline = false;
21054         this.isInput = true;
21055         this.component = this.el.select('.add-on', true).first() || false;
21056         this.component = (this.component && this.component.length === 0) ? false : this.component;
21057         this.hasInput = this.component && this.inputEL().length;
21058         
21059         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
21060         
21061         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21062         
21063         this.picker().on('mousedown', this.onMousedown, this);
21064         this.picker().on('click', this.onClick, this);
21065         
21066         this.picker().addClass('datepicker-dropdown');
21067         
21068         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21069             v.setStyle('width', '189px');
21070         });
21071         
21072         this.fillMonths();
21073         
21074         this.update();
21075         
21076         if(this.isInline) {
21077             this.show();
21078         }
21079         
21080     },
21081     
21082     setValue: function(v, suppressEvent)
21083     {   
21084         var o = this.getValue();
21085         
21086         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
21087         
21088         this.update();
21089
21090         if(suppressEvent !== true){
21091             this.fireEvent('select', this, o, v);
21092         }
21093         
21094     },
21095     
21096     getValue: function()
21097     {
21098         return this.value;
21099     },
21100     
21101     onClick: function(e) 
21102     {
21103         e.stopPropagation();
21104         e.preventDefault();
21105         
21106         var target = e.getTarget();
21107         
21108         if(target.nodeName.toLowerCase() === 'i'){
21109             target = Roo.get(target).dom.parentNode;
21110         }
21111         
21112         var nodeName = target.nodeName;
21113         var className = target.className;
21114         var html = target.innerHTML;
21115         
21116         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
21117             return;
21118         }
21119         
21120         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
21121         
21122         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21123         
21124         this.hide();
21125                         
21126     },
21127     
21128     picker : function()
21129     {
21130         return this.pickerEl;
21131     },
21132     
21133     fillMonths: function()
21134     {    
21135         var i = 0;
21136         var months = this.picker().select('>.datepicker-months td', true).first();
21137         
21138         months.dom.innerHTML = '';
21139         
21140         while (i < 12) {
21141             var month = {
21142                 tag: 'span',
21143                 cls: 'month',
21144                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
21145             };
21146             
21147             months.createChild(month);
21148         }
21149         
21150     },
21151     
21152     update: function()
21153     {
21154         var _this = this;
21155         
21156         if(typeof(this.vIndex) == 'undefined' && this.value.length){
21157             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
21158         }
21159         
21160         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
21161             e.removeClass('active');
21162             
21163             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
21164                 e.addClass('active');
21165             }
21166         })
21167     },
21168     
21169     place: function()
21170     {
21171         if(this.isInline) {
21172             return;
21173         }
21174         
21175         this.picker().removeClass(['bottom', 'top']);
21176         
21177         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21178             /*
21179              * place to the top of element!
21180              *
21181              */
21182             
21183             this.picker().addClass('top');
21184             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21185             
21186             return;
21187         }
21188         
21189         this.picker().addClass('bottom');
21190         
21191         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21192     },
21193     
21194     onFocus : function()
21195     {
21196         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
21197         this.show();
21198     },
21199     
21200     onBlur : function()
21201     {
21202         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
21203         
21204         var d = this.inputEl().getValue();
21205         
21206         this.setValue(d);
21207                 
21208         this.hide();
21209     },
21210     
21211     show : function()
21212     {
21213         this.picker().show();
21214         this.picker().select('>.datepicker-months', true).first().show();
21215         this.update();
21216         this.place();
21217         
21218         this.fireEvent('show', this, this.date);
21219     },
21220     
21221     hide : function()
21222     {
21223         if(this.isInline) {
21224             return;
21225         }
21226         this.picker().hide();
21227         this.fireEvent('hide', this, this.date);
21228         
21229     },
21230     
21231     onMousedown: function(e)
21232     {
21233         e.stopPropagation();
21234         e.preventDefault();
21235     },
21236     
21237     keyup: function(e)
21238     {
21239         Roo.bootstrap.MonthField.superclass.keyup.call(this);
21240         this.update();
21241     },
21242
21243     fireKey: function(e)
21244     {
21245         if (!this.picker().isVisible()){
21246             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
21247                 this.show();
21248             }
21249             return;
21250         }
21251         
21252         var dir;
21253         
21254         switch(e.keyCode){
21255             case 27: // escape
21256                 this.hide();
21257                 e.preventDefault();
21258                 break;
21259             case 37: // left
21260             case 39: // right
21261                 dir = e.keyCode == 37 ? -1 : 1;
21262                 
21263                 this.vIndex = this.vIndex + dir;
21264                 
21265                 if(this.vIndex < 0){
21266                     this.vIndex = 0;
21267                 }
21268                 
21269                 if(this.vIndex > 11){
21270                     this.vIndex = 11;
21271                 }
21272                 
21273                 if(isNaN(this.vIndex)){
21274                     this.vIndex = 0;
21275                 }
21276                 
21277                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21278                 
21279                 break;
21280             case 38: // up
21281             case 40: // down
21282                 
21283                 dir = e.keyCode == 38 ? -1 : 1;
21284                 
21285                 this.vIndex = this.vIndex + dir * 4;
21286                 
21287                 if(this.vIndex < 0){
21288                     this.vIndex = 0;
21289                 }
21290                 
21291                 if(this.vIndex > 11){
21292                     this.vIndex = 11;
21293                 }
21294                 
21295                 if(isNaN(this.vIndex)){
21296                     this.vIndex = 0;
21297                 }
21298                 
21299                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21300                 break;
21301                 
21302             case 13: // enter
21303                 
21304                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21305                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21306                 }
21307                 
21308                 this.hide();
21309                 e.preventDefault();
21310                 break;
21311             case 9: // tab
21312                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21313                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21314                 }
21315                 this.hide();
21316                 break;
21317             case 16: // shift
21318             case 17: // ctrl
21319             case 18: // alt
21320                 break;
21321             default :
21322                 this.hide();
21323                 
21324         }
21325     },
21326     
21327     remove: function() 
21328     {
21329         this.picker().remove();
21330     }
21331    
21332 });
21333
21334 Roo.apply(Roo.bootstrap.MonthField,  {
21335     
21336     content : {
21337         tag: 'tbody',
21338         cn: [
21339         {
21340             tag: 'tr',
21341             cn: [
21342             {
21343                 tag: 'td',
21344                 colspan: '7'
21345             }
21346             ]
21347         }
21348         ]
21349     },
21350     
21351     dates:{
21352         en: {
21353             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21354             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
21355         }
21356     }
21357 });
21358
21359 Roo.apply(Roo.bootstrap.MonthField,  {
21360   
21361     template : {
21362         tag: 'div',
21363         cls: 'datepicker dropdown-menu roo-dynamic',
21364         cn: [
21365             {
21366                 tag: 'div',
21367                 cls: 'datepicker-months',
21368                 cn: [
21369                 {
21370                     tag: 'table',
21371                     cls: 'table-condensed',
21372                     cn:[
21373                         Roo.bootstrap.DateField.content
21374                     ]
21375                 }
21376                 ]
21377             }
21378         ]
21379     }
21380 });
21381
21382  
21383
21384  
21385  /*
21386  * - LGPL
21387  *
21388  * CheckBox
21389  * 
21390  */
21391
21392 /**
21393  * @class Roo.bootstrap.CheckBox
21394  * @extends Roo.bootstrap.Input
21395  * Bootstrap CheckBox class
21396  * 
21397  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
21398  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
21399  * @cfg {String} boxLabel The text that appears beside the checkbox
21400  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
21401  * @cfg {Boolean} checked initnal the element
21402  * @cfg {Boolean} inline inline the element (default false)
21403  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
21404  * @cfg {String} tooltip label tooltip
21405  * 
21406  * @constructor
21407  * Create a new CheckBox
21408  * @param {Object} config The config object
21409  */
21410
21411 Roo.bootstrap.CheckBox = function(config){
21412     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
21413    
21414     this.addEvents({
21415         /**
21416         * @event check
21417         * Fires when the element is checked or unchecked.
21418         * @param {Roo.bootstrap.CheckBox} this This input
21419         * @param {Boolean} checked The new checked value
21420         */
21421        check : true,
21422        /**
21423         * @event click
21424         * Fires when the element is click.
21425         * @param {Roo.bootstrap.CheckBox} this This input
21426         */
21427        click : true
21428     });
21429     
21430 };
21431
21432 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
21433   
21434     inputType: 'checkbox',
21435     inputValue: 1,
21436     valueOff: 0,
21437     boxLabel: false,
21438     checked: false,
21439     weight : false,
21440     inline: false,
21441     tooltip : '',
21442     
21443     // checkbox success does not make any sense really.. 
21444     invalidClass : "",
21445     validClass : "",
21446     
21447     
21448     getAutoCreate : function()
21449     {
21450         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
21451         
21452         var id = Roo.id();
21453         
21454         var cfg = {};
21455         
21456         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
21457         
21458         if(this.inline){
21459             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
21460         }
21461         
21462         var input =  {
21463             tag: 'input',
21464             id : id,
21465             type : this.inputType,
21466             value : this.inputValue,
21467             cls : 'roo-' + this.inputType, //'form-box',
21468             placeholder : this.placeholder || ''
21469             
21470         };
21471         
21472         if(this.inputType != 'radio'){
21473             var hidden =  {
21474                 tag: 'input',
21475                 type : 'hidden',
21476                 cls : 'roo-hidden-value',
21477                 value : this.checked ? this.inputValue : this.valueOff
21478             };
21479         }
21480         
21481             
21482         if (this.weight) { // Validity check?
21483             cfg.cls += " " + this.inputType + "-" + this.weight;
21484         }
21485         
21486         if (this.disabled) {
21487             input.disabled=true;
21488         }
21489         
21490         if(this.checked){
21491             input.checked = this.checked;
21492         }
21493         
21494         if (this.name) {
21495             
21496             input.name = this.name;
21497             
21498             if(this.inputType != 'radio'){
21499                 hidden.name = this.name;
21500                 input.name = '_hidden_' + this.name;
21501             }
21502         }
21503         
21504         if (this.size) {
21505             input.cls += ' input-' + this.size;
21506         }
21507         
21508         var settings=this;
21509         
21510         ['xs','sm','md','lg'].map(function(size){
21511             if (settings[size]) {
21512                 cfg.cls += ' col-' + size + '-' + settings[size];
21513             }
21514         });
21515         
21516         var inputblock = input;
21517          
21518         if (this.before || this.after) {
21519             
21520             inputblock = {
21521                 cls : 'input-group',
21522                 cn :  [] 
21523             };
21524             
21525             if (this.before) {
21526                 inputblock.cn.push({
21527                     tag :'span',
21528                     cls : 'input-group-addon',
21529                     html : this.before
21530                 });
21531             }
21532             
21533             inputblock.cn.push(input);
21534             
21535             if(this.inputType != 'radio'){
21536                 inputblock.cn.push(hidden);
21537             }
21538             
21539             if (this.after) {
21540                 inputblock.cn.push({
21541                     tag :'span',
21542                     cls : 'input-group-addon',
21543                     html : this.after
21544                 });
21545             }
21546             
21547         }
21548         var boxLabelCfg = false;
21549         
21550         if(this.boxLabel){
21551            
21552             boxLabelCfg = {
21553                 tag: 'label',
21554                 //'for': id, // box label is handled by onclick - so no for...
21555                 cls: 'box-label',
21556                 html: this.boxLabel
21557             };
21558             if(this.tooltip){
21559                 boxLabelCfg.tooltip = this.tooltip;
21560             }
21561              
21562         }
21563         
21564         
21565         if (align ==='left' && this.fieldLabel.length) {
21566 //                Roo.log("left and has label");
21567             cfg.cn = [
21568                 {
21569                     tag: 'label',
21570                     'for' :  id,
21571                     cls : 'control-label',
21572                     html : this.fieldLabel
21573                 },
21574                 {
21575                     cls : "", 
21576                     cn: [
21577                         inputblock
21578                     ]
21579                 }
21580             ];
21581             
21582             if (boxLabelCfg) {
21583                 cfg.cn[1].cn.push(boxLabelCfg);
21584             }
21585             
21586             if(this.labelWidth > 12){
21587                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21588             }
21589             
21590             if(this.labelWidth < 13 && this.labelmd == 0){
21591                 this.labelmd = this.labelWidth;
21592             }
21593             
21594             if(this.labellg > 0){
21595                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21596                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21597             }
21598             
21599             if(this.labelmd > 0){
21600                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21601                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21602             }
21603             
21604             if(this.labelsm > 0){
21605                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21606                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21607             }
21608             
21609             if(this.labelxs > 0){
21610                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21611                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21612             }
21613             
21614         } else if ( this.fieldLabel.length) {
21615 //                Roo.log(" label");
21616                 cfg.cn = [
21617                    
21618                     {
21619                         tag: this.boxLabel ? 'span' : 'label',
21620                         'for': id,
21621                         cls: 'control-label box-input-label',
21622                         //cls : 'input-group-addon',
21623                         html : this.fieldLabel
21624                     },
21625                     
21626                     inputblock
21627                     
21628                 ];
21629                 if (boxLabelCfg) {
21630                     cfg.cn.push(boxLabelCfg);
21631                 }
21632
21633         } else {
21634             
21635 //                Roo.log(" no label && no align");
21636                 cfg.cn = [  inputblock ] ;
21637                 if (boxLabelCfg) {
21638                     cfg.cn.push(boxLabelCfg);
21639                 }
21640
21641                 
21642         }
21643         
21644        
21645         
21646         if(this.inputType != 'radio'){
21647             cfg.cn.push(hidden);
21648         }
21649         
21650         return cfg;
21651         
21652     },
21653     
21654     /**
21655      * return the real input element.
21656      */
21657     inputEl: function ()
21658     {
21659         return this.el.select('input.roo-' + this.inputType,true).first();
21660     },
21661     hiddenEl: function ()
21662     {
21663         return this.el.select('input.roo-hidden-value',true).first();
21664     },
21665     
21666     labelEl: function()
21667     {
21668         return this.el.select('label.control-label',true).first();
21669     },
21670     /* depricated... */
21671     
21672     label: function()
21673     {
21674         return this.labelEl();
21675     },
21676     
21677     boxLabelEl: function()
21678     {
21679         return this.el.select('label.box-label',true).first();
21680     },
21681     
21682     initEvents : function()
21683     {
21684 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21685         
21686         this.inputEl().on('click', this.onClick,  this);
21687         
21688         if (this.boxLabel) { 
21689             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21690         }
21691         
21692         this.startValue = this.getValue();
21693         
21694         if(this.groupId){
21695             Roo.bootstrap.CheckBox.register(this);
21696         }
21697     },
21698     
21699     onClick : function(e)
21700     {   
21701         if(this.fireEvent('click', this, e) !== false){
21702             this.setChecked(!this.checked);
21703         }
21704         
21705     },
21706     
21707     setChecked : function(state,suppressEvent)
21708     {
21709         this.startValue = this.getValue();
21710
21711         if(this.inputType == 'radio'){
21712             
21713             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21714                 e.dom.checked = false;
21715             });
21716             
21717             this.inputEl().dom.checked = true;
21718             
21719             this.inputEl().dom.value = this.inputValue;
21720             
21721             if(suppressEvent !== true){
21722                 this.fireEvent('check', this, true);
21723             }
21724             
21725             this.validate();
21726             
21727             return;
21728         }
21729         
21730         this.checked = state;
21731         
21732         this.inputEl().dom.checked = state;
21733         
21734         
21735         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21736         
21737         if(suppressEvent !== true){
21738             this.fireEvent('check', this, state);
21739         }
21740         
21741         this.validate();
21742     },
21743     
21744     getValue : function()
21745     {
21746         if(this.inputType == 'radio'){
21747             return this.getGroupValue();
21748         }
21749         
21750         return this.hiddenEl().dom.value;
21751         
21752     },
21753     
21754     getGroupValue : function()
21755     {
21756         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21757             return '';
21758         }
21759         
21760         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21761     },
21762     
21763     setValue : function(v,suppressEvent)
21764     {
21765         if(this.inputType == 'radio'){
21766             this.setGroupValue(v, suppressEvent);
21767             return;
21768         }
21769         
21770         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21771         
21772         this.validate();
21773     },
21774     
21775     setGroupValue : function(v, suppressEvent)
21776     {
21777         this.startValue = this.getValue();
21778         
21779         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21780             e.dom.checked = false;
21781             
21782             if(e.dom.value == v){
21783                 e.dom.checked = true;
21784             }
21785         });
21786         
21787         if(suppressEvent !== true){
21788             this.fireEvent('check', this, true);
21789         }
21790
21791         this.validate();
21792         
21793         return;
21794     },
21795     
21796     validate : function()
21797     {
21798         if(this.getVisibilityEl().hasClass('hidden')){
21799             return true;
21800         }
21801         
21802         if(
21803                 this.disabled || 
21804                 (this.inputType == 'radio' && this.validateRadio()) ||
21805                 (this.inputType == 'checkbox' && this.validateCheckbox())
21806         ){
21807             this.markValid();
21808             return true;
21809         }
21810         
21811         this.markInvalid();
21812         return false;
21813     },
21814     
21815     validateRadio : function()
21816     {
21817         if(this.getVisibilityEl().hasClass('hidden')){
21818             return true;
21819         }
21820         
21821         if(this.allowBlank){
21822             return true;
21823         }
21824         
21825         var valid = false;
21826         
21827         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21828             if(!e.dom.checked){
21829                 return;
21830             }
21831             
21832             valid = true;
21833             
21834             return false;
21835         });
21836         
21837         return valid;
21838     },
21839     
21840     validateCheckbox : function()
21841     {
21842         if(!this.groupId){
21843             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21844             //return (this.getValue() == this.inputValue) ? true : false;
21845         }
21846         
21847         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21848         
21849         if(!group){
21850             return false;
21851         }
21852         
21853         var r = false;
21854         
21855         for(var i in group){
21856             if(group[i].el.isVisible(true)){
21857                 r = false;
21858                 break;
21859             }
21860             
21861             r = true;
21862         }
21863         
21864         for(var i in group){
21865             if(r){
21866                 break;
21867             }
21868             
21869             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21870         }
21871         
21872         return r;
21873     },
21874     
21875     /**
21876      * Mark this field as valid
21877      */
21878     markValid : function()
21879     {
21880         var _this = this;
21881         
21882         this.fireEvent('valid', this);
21883         
21884         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21885         
21886         if(this.groupId){
21887             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21888         }
21889         
21890         if(label){
21891             label.markValid();
21892         }
21893
21894         if(this.inputType == 'radio'){
21895             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21896                 var fg = e.findParent('.form-group', false, true);
21897                 if (Roo.bootstrap.version == 3) {
21898                     fg.removeClass([_this.invalidClass, _this.validClass]);
21899                     fg.addClass(_this.validClass);
21900                 } else {
21901                     fg.removeClass(['is-valid', 'is-invalid']);
21902                     fg.addClass('is-valid');
21903                 }
21904             });
21905             
21906             return;
21907         }
21908
21909         if(!this.groupId){
21910             var fg = this.el.findParent('.form-group', false, true);
21911             if (Roo.bootstrap.version == 3) {
21912                 fg.removeClass([this.invalidClass, this.validClass]);
21913                 fg.addClass(this.validClass);
21914             } else {
21915                 fg.removeClass(['is-valid', 'is-invalid']);
21916                 fg.addClass('is-valid');
21917             }
21918             return;
21919         }
21920         
21921         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21922         
21923         if(!group){
21924             return;
21925         }
21926         
21927         for(var i in group){
21928             var fg = group[i].el.findParent('.form-group', false, true);
21929             if (Roo.bootstrap.version == 3) {
21930                 fg.removeClass([this.invalidClass, this.validClass]);
21931                 fg.addClass(this.validClass);
21932             } else {
21933                 fg.removeClass(['is-valid', 'is-invalid']);
21934                 fg.addClass('is-valid');
21935             }
21936         }
21937     },
21938     
21939      /**
21940      * Mark this field as invalid
21941      * @param {String} msg The validation message
21942      */
21943     markInvalid : function(msg)
21944     {
21945         if(this.allowBlank){
21946             return;
21947         }
21948         
21949         var _this = this;
21950         
21951         this.fireEvent('invalid', this, msg);
21952         
21953         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21954         
21955         if(this.groupId){
21956             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21957         }
21958         
21959         if(label){
21960             label.markInvalid();
21961         }
21962             
21963         if(this.inputType == 'radio'){
21964             
21965             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21966                 var fg = e.findParent('.form-group', false, true);
21967                 if (Roo.bootstrap.version == 3) {
21968                     fg.removeClass([_this.invalidClass, _this.validClass]);
21969                     fg.addClass(_this.invalidClass);
21970                 } else {
21971                     fg.removeClass(['is-invalid', 'is-valid']);
21972                     fg.addClass('is-invalid');
21973                 }
21974             });
21975             
21976             return;
21977         }
21978         
21979         if(!this.groupId){
21980             var fg = this.el.findParent('.form-group', false, true);
21981             if (Roo.bootstrap.version == 3) {
21982                 fg.removeClass([_this.invalidClass, _this.validClass]);
21983                 fg.addClass(_this.invalidClass);
21984             } else {
21985                 fg.removeClass(['is-invalid', 'is-valid']);
21986                 fg.addClass('is-invalid');
21987             }
21988             return;
21989         }
21990         
21991         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21992         
21993         if(!group){
21994             return;
21995         }
21996         
21997         for(var i in group){
21998             var fg = group[i].el.findParent('.form-group', false, true);
21999             if (Roo.bootstrap.version == 3) {
22000                 fg.removeClass([_this.invalidClass, _this.validClass]);
22001                 fg.addClass(_this.invalidClass);
22002             } else {
22003                 fg.removeClass(['is-invalid', 'is-valid']);
22004                 fg.addClass('is-invalid');
22005             }
22006         }
22007         
22008     },
22009     
22010     clearInvalid : function()
22011     {
22012         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
22013         
22014         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
22015         
22016         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22017         
22018         if (label && label.iconEl) {
22019             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
22020             label.iconEl.removeClass(['is-invalid', 'is-valid']);
22021         }
22022     },
22023     
22024     disable : function()
22025     {
22026         if(this.inputType != 'radio'){
22027             Roo.bootstrap.CheckBox.superclass.disable.call(this);
22028             return;
22029         }
22030         
22031         var _this = this;
22032         
22033         if(this.rendered){
22034             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22035                 _this.getActionEl().addClass(this.disabledClass);
22036                 e.dom.disabled = true;
22037             });
22038         }
22039         
22040         this.disabled = true;
22041         this.fireEvent("disable", this);
22042         return this;
22043     },
22044
22045     enable : function()
22046     {
22047         if(this.inputType != 'radio'){
22048             Roo.bootstrap.CheckBox.superclass.enable.call(this);
22049             return;
22050         }
22051         
22052         var _this = this;
22053         
22054         if(this.rendered){
22055             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22056                 _this.getActionEl().removeClass(this.disabledClass);
22057                 e.dom.disabled = false;
22058             });
22059         }
22060         
22061         this.disabled = false;
22062         this.fireEvent("enable", this);
22063         return this;
22064     },
22065     
22066     setBoxLabel : function(v)
22067     {
22068         this.boxLabel = v;
22069         
22070         if(this.rendered){
22071             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
22072         }
22073     }
22074
22075 });
22076
22077 Roo.apply(Roo.bootstrap.CheckBox, {
22078     
22079     groups: {},
22080     
22081      /**
22082     * register a CheckBox Group
22083     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
22084     */
22085     register : function(checkbox)
22086     {
22087         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
22088             this.groups[checkbox.groupId] = {};
22089         }
22090         
22091         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
22092             return;
22093         }
22094         
22095         this.groups[checkbox.groupId][checkbox.name] = checkbox;
22096         
22097     },
22098     /**
22099     * fetch a CheckBox Group based on the group ID
22100     * @param {string} the group ID
22101     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
22102     */
22103     get: function(groupId) {
22104         if (typeof(this.groups[groupId]) == 'undefined') {
22105             return false;
22106         }
22107         
22108         return this.groups[groupId] ;
22109     }
22110     
22111     
22112 });
22113 /*
22114  * - LGPL
22115  *
22116  * RadioItem
22117  * 
22118  */
22119
22120 /**
22121  * @class Roo.bootstrap.Radio
22122  * @extends Roo.bootstrap.Component
22123  * Bootstrap Radio class
22124  * @cfg {String} boxLabel - the label associated
22125  * @cfg {String} value - the value of radio
22126  * 
22127  * @constructor
22128  * Create a new Radio
22129  * @param {Object} config The config object
22130  */
22131 Roo.bootstrap.Radio = function(config){
22132     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
22133     
22134 };
22135
22136 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
22137     
22138     boxLabel : '',
22139     
22140     value : '',
22141     
22142     getAutoCreate : function()
22143     {
22144         var cfg = {
22145             tag : 'div',
22146             cls : 'form-group radio',
22147             cn : [
22148                 {
22149                     tag : 'label',
22150                     cls : 'box-label',
22151                     html : this.boxLabel
22152                 }
22153             ]
22154         };
22155         
22156         return cfg;
22157     },
22158     
22159     initEvents : function() 
22160     {
22161         this.parent().register(this);
22162         
22163         this.el.on('click', this.onClick, this);
22164         
22165     },
22166     
22167     onClick : function(e)
22168     {
22169         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
22170             this.setChecked(true);
22171         }
22172     },
22173     
22174     setChecked : function(state, suppressEvent)
22175     {
22176         this.parent().setValue(this.value, suppressEvent);
22177         
22178     },
22179     
22180     setBoxLabel : function(v)
22181     {
22182         this.boxLabel = v;
22183         
22184         if(this.rendered){
22185             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
22186         }
22187     }
22188     
22189 });
22190  
22191
22192  /*
22193  * - LGPL
22194  *
22195  * Input
22196  * 
22197  */
22198
22199 /**
22200  * @class Roo.bootstrap.SecurePass
22201  * @extends Roo.bootstrap.Input
22202  * Bootstrap SecurePass class
22203  *
22204  * 
22205  * @constructor
22206  * Create a new SecurePass
22207  * @param {Object} config The config object
22208  */
22209  
22210 Roo.bootstrap.SecurePass = function (config) {
22211     // these go here, so the translation tool can replace them..
22212     this.errors = {
22213         PwdEmpty: "Please type a password, and then retype it to confirm.",
22214         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22215         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22216         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22217         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22218         FNInPwd: "Your password can't contain your first name. Please type a different password.",
22219         LNInPwd: "Your password can't contain your last name. Please type a different password.",
22220         TooWeak: "Your password is Too Weak."
22221     },
22222     this.meterLabel = "Password strength:";
22223     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
22224     this.meterClass = [
22225         "roo-password-meter-tooweak", 
22226         "roo-password-meter-weak", 
22227         "roo-password-meter-medium", 
22228         "roo-password-meter-strong", 
22229         "roo-password-meter-grey"
22230     ];
22231     
22232     this.errors = {};
22233     
22234     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
22235 }
22236
22237 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
22238     /**
22239      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
22240      * {
22241      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
22242      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22243      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22244      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22245      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22246      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
22247      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
22248      * })
22249      */
22250     // private
22251     
22252     meterWidth: 300,
22253     errorMsg :'',    
22254     errors: false,
22255     imageRoot: '/',
22256     /**
22257      * @cfg {String/Object} Label for the strength meter (defaults to
22258      * 'Password strength:')
22259      */
22260     // private
22261     meterLabel: '',
22262     /**
22263      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
22264      * ['Weak', 'Medium', 'Strong'])
22265      */
22266     // private    
22267     pwdStrengths: false,    
22268     // private
22269     strength: 0,
22270     // private
22271     _lastPwd: null,
22272     // private
22273     kCapitalLetter: 0,
22274     kSmallLetter: 1,
22275     kDigit: 2,
22276     kPunctuation: 3,
22277     
22278     insecure: false,
22279     // private
22280     initEvents: function ()
22281     {
22282         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
22283
22284         if (this.el.is('input[type=password]') && Roo.isSafari) {
22285             this.el.on('keydown', this.SafariOnKeyDown, this);
22286         }
22287
22288         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
22289     },
22290     // private
22291     onRender: function (ct, position)
22292     {
22293         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
22294         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
22295         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
22296
22297         this.trigger.createChild({
22298                    cn: [
22299                     {
22300                     //id: 'PwdMeter',
22301                     tag: 'div',
22302                     cls: 'roo-password-meter-grey col-xs-12',
22303                     style: {
22304                         //width: 0,
22305                         //width: this.meterWidth + 'px'                                                
22306                         }
22307                     },
22308                     {                            
22309                          cls: 'roo-password-meter-text'                          
22310                     }
22311                 ]            
22312         });
22313
22314          
22315         if (this.hideTrigger) {
22316             this.trigger.setDisplayed(false);
22317         }
22318         this.setSize(this.width || '', this.height || '');
22319     },
22320     // private
22321     onDestroy: function ()
22322     {
22323         if (this.trigger) {
22324             this.trigger.removeAllListeners();
22325             this.trigger.remove();
22326         }
22327         if (this.wrap) {
22328             this.wrap.remove();
22329         }
22330         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
22331     },
22332     // private
22333     checkStrength: function ()
22334     {
22335         var pwd = this.inputEl().getValue();
22336         if (pwd == this._lastPwd) {
22337             return;
22338         }
22339
22340         var strength;
22341         if (this.ClientSideStrongPassword(pwd)) {
22342             strength = 3;
22343         } else if (this.ClientSideMediumPassword(pwd)) {
22344             strength = 2;
22345         } else if (this.ClientSideWeakPassword(pwd)) {
22346             strength = 1;
22347         } else {
22348             strength = 0;
22349         }
22350         
22351         Roo.log('strength1: ' + strength);
22352         
22353         //var pm = this.trigger.child('div/div/div').dom;
22354         var pm = this.trigger.child('div/div');
22355         pm.removeClass(this.meterClass);
22356         pm.addClass(this.meterClass[strength]);
22357                 
22358         
22359         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22360                 
22361         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
22362         
22363         this._lastPwd = pwd;
22364     },
22365     reset: function ()
22366     {
22367         Roo.bootstrap.SecurePass.superclass.reset.call(this);
22368         
22369         this._lastPwd = '';
22370         
22371         var pm = this.trigger.child('div/div');
22372         pm.removeClass(this.meterClass);
22373         pm.addClass('roo-password-meter-grey');        
22374         
22375         
22376         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22377         
22378         pt.innerHTML = '';
22379         this.inputEl().dom.type='password';
22380     },
22381     // private
22382     validateValue: function (value)
22383     {
22384         
22385         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
22386             return false;
22387         }
22388         if (value.length == 0) {
22389             if (this.allowBlank) {
22390                 this.clearInvalid();
22391                 return true;
22392             }
22393
22394             this.markInvalid(this.errors.PwdEmpty);
22395             this.errorMsg = this.errors.PwdEmpty;
22396             return false;
22397         }
22398         
22399         if(this.insecure){
22400             return true;
22401         }
22402         
22403         if ('[\x21-\x7e]*'.match(value)) {
22404             this.markInvalid(this.errors.PwdBadChar);
22405             this.errorMsg = this.errors.PwdBadChar;
22406             return false;
22407         }
22408         if (value.length < 6) {
22409             this.markInvalid(this.errors.PwdShort);
22410             this.errorMsg = this.errors.PwdShort;
22411             return false;
22412         }
22413         if (value.length > 16) {
22414             this.markInvalid(this.errors.PwdLong);
22415             this.errorMsg = this.errors.PwdLong;
22416             return false;
22417         }
22418         var strength;
22419         if (this.ClientSideStrongPassword(value)) {
22420             strength = 3;
22421         } else if (this.ClientSideMediumPassword(value)) {
22422             strength = 2;
22423         } else if (this.ClientSideWeakPassword(value)) {
22424             strength = 1;
22425         } else {
22426             strength = 0;
22427         }
22428
22429         
22430         if (strength < 2) {
22431             //this.markInvalid(this.errors.TooWeak);
22432             this.errorMsg = this.errors.TooWeak;
22433             //return false;
22434         }
22435         
22436         
22437         console.log('strength2: ' + strength);
22438         
22439         //var pm = this.trigger.child('div/div/div').dom;
22440         
22441         var pm = this.trigger.child('div/div');
22442         pm.removeClass(this.meterClass);
22443         pm.addClass(this.meterClass[strength]);
22444                 
22445         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22446                 
22447         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
22448         
22449         this.errorMsg = ''; 
22450         return true;
22451     },
22452     // private
22453     CharacterSetChecks: function (type)
22454     {
22455         this.type = type;
22456         this.fResult = false;
22457     },
22458     // private
22459     isctype: function (character, type)
22460     {
22461         switch (type) {  
22462             case this.kCapitalLetter:
22463                 if (character >= 'A' && character <= 'Z') {
22464                     return true;
22465                 }
22466                 break;
22467             
22468             case this.kSmallLetter:
22469                 if (character >= 'a' && character <= 'z') {
22470                     return true;
22471                 }
22472                 break;
22473             
22474             case this.kDigit:
22475                 if (character >= '0' && character <= '9') {
22476                     return true;
22477                 }
22478                 break;
22479             
22480             case this.kPunctuation:
22481                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
22482                     return true;
22483                 }
22484                 break;
22485             
22486             default:
22487                 return false;
22488         }
22489
22490     },
22491     // private
22492     IsLongEnough: function (pwd, size)
22493     {
22494         return !(pwd == null || isNaN(size) || pwd.length < size);
22495     },
22496     // private
22497     SpansEnoughCharacterSets: function (word, nb)
22498     {
22499         if (!this.IsLongEnough(word, nb))
22500         {
22501             return false;
22502         }
22503
22504         var characterSetChecks = new Array(
22505             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
22506             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
22507         );
22508         
22509         for (var index = 0; index < word.length; ++index) {
22510             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22511                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
22512                     characterSetChecks[nCharSet].fResult = true;
22513                     break;
22514                 }
22515             }
22516         }
22517
22518         var nCharSets = 0;
22519         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22520             if (characterSetChecks[nCharSet].fResult) {
22521                 ++nCharSets;
22522             }
22523         }
22524
22525         if (nCharSets < nb) {
22526             return false;
22527         }
22528         return true;
22529     },
22530     // private
22531     ClientSideStrongPassword: function (pwd)
22532     {
22533         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22534     },
22535     // private
22536     ClientSideMediumPassword: function (pwd)
22537     {
22538         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22539     },
22540     // private
22541     ClientSideWeakPassword: function (pwd)
22542     {
22543         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22544     }
22545           
22546 })//<script type="text/javascript">
22547
22548 /*
22549  * Based  Ext JS Library 1.1.1
22550  * Copyright(c) 2006-2007, Ext JS, LLC.
22551  * LGPL
22552  *
22553  */
22554  
22555 /**
22556  * @class Roo.HtmlEditorCore
22557  * @extends Roo.Component
22558  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22559  *
22560  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22561  */
22562
22563 Roo.HtmlEditorCore = function(config){
22564     
22565     
22566     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22567     
22568     
22569     this.addEvents({
22570         /**
22571          * @event initialize
22572          * Fires when the editor is fully initialized (including the iframe)
22573          * @param {Roo.HtmlEditorCore} this
22574          */
22575         initialize: true,
22576         /**
22577          * @event activate
22578          * Fires when the editor is first receives the focus. Any insertion must wait
22579          * until after this event.
22580          * @param {Roo.HtmlEditorCore} this
22581          */
22582         activate: true,
22583          /**
22584          * @event beforesync
22585          * Fires before the textarea is updated with content from the editor iframe. Return false
22586          * to cancel the sync.
22587          * @param {Roo.HtmlEditorCore} this
22588          * @param {String} html
22589          */
22590         beforesync: true,
22591          /**
22592          * @event beforepush
22593          * Fires before the iframe editor is updated with content from the textarea. Return false
22594          * to cancel the push.
22595          * @param {Roo.HtmlEditorCore} this
22596          * @param {String} html
22597          */
22598         beforepush: true,
22599          /**
22600          * @event sync
22601          * Fires when the textarea is updated with content from the editor iframe.
22602          * @param {Roo.HtmlEditorCore} this
22603          * @param {String} html
22604          */
22605         sync: true,
22606          /**
22607          * @event push
22608          * Fires when the iframe editor is updated with content from the textarea.
22609          * @param {Roo.HtmlEditorCore} this
22610          * @param {String} html
22611          */
22612         push: true,
22613         
22614         /**
22615          * @event editorevent
22616          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22617          * @param {Roo.HtmlEditorCore} this
22618          */
22619         editorevent: true
22620         
22621     });
22622     
22623     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22624     
22625     // defaults : white / black...
22626     this.applyBlacklists();
22627     
22628     
22629     
22630 };
22631
22632
22633 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22634
22635
22636      /**
22637      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22638      */
22639     
22640     owner : false,
22641     
22642      /**
22643      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22644      *                        Roo.resizable.
22645      */
22646     resizable : false,
22647      /**
22648      * @cfg {Number} height (in pixels)
22649      */   
22650     height: 300,
22651    /**
22652      * @cfg {Number} width (in pixels)
22653      */   
22654     width: 500,
22655     
22656     /**
22657      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22658      * 
22659      */
22660     stylesheets: false,
22661     
22662     // id of frame..
22663     frameId: false,
22664     
22665     // private properties
22666     validationEvent : false,
22667     deferHeight: true,
22668     initialized : false,
22669     activated : false,
22670     sourceEditMode : false,
22671     onFocus : Roo.emptyFn,
22672     iframePad:3,
22673     hideMode:'offsets',
22674     
22675     clearUp: true,
22676     
22677     // blacklist + whitelisted elements..
22678     black: false,
22679     white: false,
22680      
22681     bodyCls : '',
22682
22683     /**
22684      * Protected method that will not generally be called directly. It
22685      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22686      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22687      */
22688     getDocMarkup : function(){
22689         // body styles..
22690         var st = '';
22691         
22692         // inherit styels from page...?? 
22693         if (this.stylesheets === false) {
22694             
22695             Roo.get(document.head).select('style').each(function(node) {
22696                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22697             });
22698             
22699             Roo.get(document.head).select('link').each(function(node) { 
22700                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22701             });
22702             
22703         } else if (!this.stylesheets.length) {
22704                 // simple..
22705                 st = '<style type="text/css">' +
22706                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22707                    '</style>';
22708         } else { 
22709             st = '<style type="text/css">' +
22710                     this.stylesheets +
22711                 '</style>';
22712         }
22713         
22714         st +=  '<style type="text/css">' +
22715             'IMG { cursor: pointer } ' +
22716         '</style>';
22717
22718         var cls = 'roo-htmleditor-body';
22719         
22720         if(this.bodyCls.length){
22721             cls += ' ' + this.bodyCls;
22722         }
22723         
22724         return '<html><head>' + st  +
22725             //<style type="text/css">' +
22726             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22727             //'</style>' +
22728             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
22729     },
22730
22731     // private
22732     onRender : function(ct, position)
22733     {
22734         var _t = this;
22735         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22736         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22737         
22738         
22739         this.el.dom.style.border = '0 none';
22740         this.el.dom.setAttribute('tabIndex', -1);
22741         this.el.addClass('x-hidden hide');
22742         
22743         
22744         
22745         if(Roo.isIE){ // fix IE 1px bogus margin
22746             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22747         }
22748        
22749         
22750         this.frameId = Roo.id();
22751         
22752          
22753         
22754         var iframe = this.owner.wrap.createChild({
22755             tag: 'iframe',
22756             cls: 'form-control', // bootstrap..
22757             id: this.frameId,
22758             name: this.frameId,
22759             frameBorder : 'no',
22760             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22761         }, this.el
22762         );
22763         
22764         
22765         this.iframe = iframe.dom;
22766
22767          this.assignDocWin();
22768         
22769         this.doc.designMode = 'on';
22770        
22771         this.doc.open();
22772         this.doc.write(this.getDocMarkup());
22773         this.doc.close();
22774
22775         
22776         var task = { // must defer to wait for browser to be ready
22777             run : function(){
22778                 //console.log("run task?" + this.doc.readyState);
22779                 this.assignDocWin();
22780                 if(this.doc.body || this.doc.readyState == 'complete'){
22781                     try {
22782                         this.doc.designMode="on";
22783                     } catch (e) {
22784                         return;
22785                     }
22786                     Roo.TaskMgr.stop(task);
22787                     this.initEditor.defer(10, this);
22788                 }
22789             },
22790             interval : 10,
22791             duration: 10000,
22792             scope: this
22793         };
22794         Roo.TaskMgr.start(task);
22795
22796     },
22797
22798     // private
22799     onResize : function(w, h)
22800     {
22801          Roo.log('resize: ' +w + ',' + h );
22802         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22803         if(!this.iframe){
22804             return;
22805         }
22806         if(typeof w == 'number'){
22807             
22808             this.iframe.style.width = w + 'px';
22809         }
22810         if(typeof h == 'number'){
22811             
22812             this.iframe.style.height = h + 'px';
22813             if(this.doc){
22814                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22815             }
22816         }
22817         
22818     },
22819
22820     /**
22821      * Toggles the editor between standard and source edit mode.
22822      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22823      */
22824     toggleSourceEdit : function(sourceEditMode){
22825         
22826         this.sourceEditMode = sourceEditMode === true;
22827         
22828         if(this.sourceEditMode){
22829  
22830             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22831             
22832         }else{
22833             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22834             //this.iframe.className = '';
22835             this.deferFocus();
22836         }
22837         //this.setSize(this.owner.wrap.getSize());
22838         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22839     },
22840
22841     
22842   
22843
22844     /**
22845      * Protected method that will not generally be called directly. If you need/want
22846      * custom HTML cleanup, this is the method you should override.
22847      * @param {String} html The HTML to be cleaned
22848      * return {String} The cleaned HTML
22849      */
22850     cleanHtml : function(html){
22851         html = String(html);
22852         if(html.length > 5){
22853             if(Roo.isSafari){ // strip safari nonsense
22854                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22855             }
22856         }
22857         if(html == '&nbsp;'){
22858             html = '';
22859         }
22860         return html;
22861     },
22862
22863     /**
22864      * HTML Editor -> Textarea
22865      * Protected method that will not generally be called directly. Syncs the contents
22866      * of the editor iframe with the textarea.
22867      */
22868     syncValue : function(){
22869         if(this.initialized){
22870             var bd = (this.doc.body || this.doc.documentElement);
22871             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22872             var html = bd.innerHTML;
22873             if(Roo.isSafari){
22874                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22875                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22876                 if(m && m[1]){
22877                     html = '<div style="'+m[0]+'">' + html + '</div>';
22878                 }
22879             }
22880             html = this.cleanHtml(html);
22881             // fix up the special chars.. normaly like back quotes in word...
22882             // however we do not want to do this with chinese..
22883             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
22884                 
22885                 var cc = match.charCodeAt();
22886
22887                 // Get the character value, handling surrogate pairs
22888                 if (match.length == 2) {
22889                     // It's a surrogate pair, calculate the Unicode code point
22890                     var high = match.charCodeAt(0) - 0xD800;
22891                     var low  = match.charCodeAt(1) - 0xDC00;
22892                     cc = (high * 0x400) + low + 0x10000;
22893                 }  else if (
22894                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22895                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22896                     (cc >= 0xf900 && cc < 0xfb00 )
22897                 ) {
22898                         return match;
22899                 }  
22900          
22901                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
22902                 return "&#" + cc + ";";
22903                 
22904                 
22905             });
22906             
22907             
22908              
22909             if(this.owner.fireEvent('beforesync', this, html) !== false){
22910                 this.el.dom.value = html;
22911                 this.owner.fireEvent('sync', this, html);
22912             }
22913         }
22914     },
22915
22916     /**
22917      * Protected method that will not generally be called directly. Pushes the value of the textarea
22918      * into the iframe editor.
22919      */
22920     pushValue : function(){
22921         if(this.initialized){
22922             var v = this.el.dom.value.trim();
22923             
22924 //            if(v.length < 1){
22925 //                v = '&#160;';
22926 //            }
22927             
22928             if(this.owner.fireEvent('beforepush', this, v) !== false){
22929                 var d = (this.doc.body || this.doc.documentElement);
22930                 d.innerHTML = v;
22931                 this.cleanUpPaste();
22932                 this.el.dom.value = d.innerHTML;
22933                 this.owner.fireEvent('push', this, v);
22934             }
22935         }
22936     },
22937
22938     // private
22939     deferFocus : function(){
22940         this.focus.defer(10, this);
22941     },
22942
22943     // doc'ed in Field
22944     focus : function(){
22945         if(this.win && !this.sourceEditMode){
22946             this.win.focus();
22947         }else{
22948             this.el.focus();
22949         }
22950     },
22951     
22952     assignDocWin: function()
22953     {
22954         var iframe = this.iframe;
22955         
22956          if(Roo.isIE){
22957             this.doc = iframe.contentWindow.document;
22958             this.win = iframe.contentWindow;
22959         } else {
22960 //            if (!Roo.get(this.frameId)) {
22961 //                return;
22962 //            }
22963 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22964 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22965             
22966             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22967                 return;
22968             }
22969             
22970             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22971             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22972         }
22973     },
22974     
22975     // private
22976     initEditor : function(){
22977         //console.log("INIT EDITOR");
22978         this.assignDocWin();
22979         
22980         
22981         
22982         this.doc.designMode="on";
22983         this.doc.open();
22984         this.doc.write(this.getDocMarkup());
22985         this.doc.close();
22986         
22987         var dbody = (this.doc.body || this.doc.documentElement);
22988         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22989         // this copies styles from the containing element into thsi one..
22990         // not sure why we need all of this..
22991         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22992         
22993         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22994         //ss['background-attachment'] = 'fixed'; // w3c
22995         dbody.bgProperties = 'fixed'; // ie
22996         //Roo.DomHelper.applyStyles(dbody, ss);
22997         Roo.EventManager.on(this.doc, {
22998             //'mousedown': this.onEditorEvent,
22999             'mouseup': this.onEditorEvent,
23000             'dblclick': this.onEditorEvent,
23001             'click': this.onEditorEvent,
23002             'keyup': this.onEditorEvent,
23003             buffer:100,
23004             scope: this
23005         });
23006         if(Roo.isGecko){
23007             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
23008         }
23009         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
23010             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
23011         }
23012         this.initialized = true;
23013
23014         this.owner.fireEvent('initialize', this);
23015         this.pushValue();
23016     },
23017
23018     // private
23019     onDestroy : function(){
23020         
23021         
23022         
23023         if(this.rendered){
23024             
23025             //for (var i =0; i < this.toolbars.length;i++) {
23026             //    // fixme - ask toolbars for heights?
23027             //    this.toolbars[i].onDestroy();
23028            // }
23029             
23030             //this.wrap.dom.innerHTML = '';
23031             //this.wrap.remove();
23032         }
23033     },
23034
23035     // private
23036     onFirstFocus : function(){
23037         
23038         this.assignDocWin();
23039         
23040         
23041         this.activated = true;
23042          
23043     
23044         if(Roo.isGecko){ // prevent silly gecko errors
23045             this.win.focus();
23046             var s = this.win.getSelection();
23047             if(!s.focusNode || s.focusNode.nodeType != 3){
23048                 var r = s.getRangeAt(0);
23049                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
23050                 r.collapse(true);
23051                 this.deferFocus();
23052             }
23053             try{
23054                 this.execCmd('useCSS', true);
23055                 this.execCmd('styleWithCSS', false);
23056             }catch(e){}
23057         }
23058         this.owner.fireEvent('activate', this);
23059     },
23060
23061     // private
23062     adjustFont: function(btn){
23063         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
23064         //if(Roo.isSafari){ // safari
23065         //    adjust *= 2;
23066        // }
23067         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
23068         if(Roo.isSafari){ // safari
23069             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
23070             v =  (v < 10) ? 10 : v;
23071             v =  (v > 48) ? 48 : v;
23072             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
23073             
23074         }
23075         
23076         
23077         v = Math.max(1, v+adjust);
23078         
23079         this.execCmd('FontSize', v  );
23080     },
23081
23082     onEditorEvent : function(e)
23083     {
23084         this.owner.fireEvent('editorevent', this, e);
23085       //  this.updateToolbar();
23086         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
23087     },
23088
23089     insertTag : function(tg)
23090     {
23091         // could be a bit smarter... -> wrap the current selected tRoo..
23092         if (tg.toLowerCase() == 'span' ||
23093             tg.toLowerCase() == 'code' ||
23094             tg.toLowerCase() == 'sup' ||
23095             tg.toLowerCase() == 'sub' 
23096             ) {
23097             
23098             range = this.createRange(this.getSelection());
23099             var wrappingNode = this.doc.createElement(tg.toLowerCase());
23100             wrappingNode.appendChild(range.extractContents());
23101             range.insertNode(wrappingNode);
23102
23103             return;
23104             
23105             
23106             
23107         }
23108         this.execCmd("formatblock",   tg);
23109         
23110     },
23111     
23112     insertText : function(txt)
23113     {
23114         
23115         
23116         var range = this.createRange();
23117         range.deleteContents();
23118                //alert(Sender.getAttribute('label'));
23119                
23120         range.insertNode(this.doc.createTextNode(txt));
23121     } ,
23122     
23123      
23124
23125     /**
23126      * Executes a Midas editor command on the editor document and performs necessary focus and
23127      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
23128      * @param {String} cmd The Midas command
23129      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
23130      */
23131     relayCmd : function(cmd, value){
23132         this.win.focus();
23133         this.execCmd(cmd, value);
23134         this.owner.fireEvent('editorevent', this);
23135         //this.updateToolbar();
23136         this.owner.deferFocus();
23137     },
23138
23139     /**
23140      * Executes a Midas editor command directly on the editor document.
23141      * For visual commands, you should use {@link #relayCmd} instead.
23142      * <b>This should only be called after the editor is initialized.</b>
23143      * @param {String} cmd The Midas command
23144      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
23145      */
23146     execCmd : function(cmd, value){
23147         this.doc.execCommand(cmd, false, value === undefined ? null : value);
23148         this.syncValue();
23149     },
23150  
23151  
23152    
23153     /**
23154      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
23155      * to insert tRoo.
23156      * @param {String} text | dom node.. 
23157      */
23158     insertAtCursor : function(text)
23159     {
23160         
23161         if(!this.activated){
23162             return;
23163         }
23164         /*
23165         if(Roo.isIE){
23166             this.win.focus();
23167             var r = this.doc.selection.createRange();
23168             if(r){
23169                 r.collapse(true);
23170                 r.pasteHTML(text);
23171                 this.syncValue();
23172                 this.deferFocus();
23173             
23174             }
23175             return;
23176         }
23177         */
23178         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
23179             this.win.focus();
23180             
23181             
23182             // from jquery ui (MIT licenced)
23183             var range, node;
23184             var win = this.win;
23185             
23186             if (win.getSelection && win.getSelection().getRangeAt) {
23187                 range = win.getSelection().getRangeAt(0);
23188                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
23189                 range.insertNode(node);
23190             } else if (win.document.selection && win.document.selection.createRange) {
23191                 // no firefox support
23192                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23193                 win.document.selection.createRange().pasteHTML(txt);
23194             } else {
23195                 // no firefox support
23196                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23197                 this.execCmd('InsertHTML', txt);
23198             } 
23199             
23200             this.syncValue();
23201             
23202             this.deferFocus();
23203         }
23204     },
23205  // private
23206     mozKeyPress : function(e){
23207         if(e.ctrlKey){
23208             var c = e.getCharCode(), cmd;
23209           
23210             if(c > 0){
23211                 c = String.fromCharCode(c).toLowerCase();
23212                 switch(c){
23213                     case 'b':
23214                         cmd = 'bold';
23215                         break;
23216                     case 'i':
23217                         cmd = 'italic';
23218                         break;
23219                     
23220                     case 'u':
23221                         cmd = 'underline';
23222                         break;
23223                     
23224                     case 'v':
23225                         this.cleanUpPaste.defer(100, this);
23226                         return;
23227                         
23228                 }
23229                 if(cmd){
23230                     this.win.focus();
23231                     this.execCmd(cmd);
23232                     this.deferFocus();
23233                     e.preventDefault();
23234                 }
23235                 
23236             }
23237         }
23238     },
23239
23240     // private
23241     fixKeys : function(){ // load time branching for fastest keydown performance
23242         if(Roo.isIE){
23243             return function(e){
23244                 var k = e.getKey(), r;
23245                 if(k == e.TAB){
23246                     e.stopEvent();
23247                     r = this.doc.selection.createRange();
23248                     if(r){
23249                         r.collapse(true);
23250                         r.pasteHTML('&#160;&#160;&#160;&#160;');
23251                         this.deferFocus();
23252                     }
23253                     return;
23254                 }
23255                 
23256                 if(k == e.ENTER){
23257                     r = this.doc.selection.createRange();
23258                     if(r){
23259                         var target = r.parentElement();
23260                         if(!target || target.tagName.toLowerCase() != 'li'){
23261                             e.stopEvent();
23262                             r.pasteHTML('<br />');
23263                             r.collapse(false);
23264                             r.select();
23265                         }
23266                     }
23267                 }
23268                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23269                     this.cleanUpPaste.defer(100, this);
23270                     return;
23271                 }
23272                 
23273                 
23274             };
23275         }else if(Roo.isOpera){
23276             return function(e){
23277                 var k = e.getKey();
23278                 if(k == e.TAB){
23279                     e.stopEvent();
23280                     this.win.focus();
23281                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
23282                     this.deferFocus();
23283                 }
23284                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23285                     this.cleanUpPaste.defer(100, this);
23286                     return;
23287                 }
23288                 
23289             };
23290         }else if(Roo.isSafari){
23291             return function(e){
23292                 var k = e.getKey();
23293                 
23294                 if(k == e.TAB){
23295                     e.stopEvent();
23296                     this.execCmd('InsertText','\t');
23297                     this.deferFocus();
23298                     return;
23299                 }
23300                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23301                     this.cleanUpPaste.defer(100, this);
23302                     return;
23303                 }
23304                 
23305              };
23306         }
23307     }(),
23308     
23309     getAllAncestors: function()
23310     {
23311         var p = this.getSelectedNode();
23312         var a = [];
23313         if (!p) {
23314             a.push(p); // push blank onto stack..
23315             p = this.getParentElement();
23316         }
23317         
23318         
23319         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
23320             a.push(p);
23321             p = p.parentNode;
23322         }
23323         a.push(this.doc.body);
23324         return a;
23325     },
23326     lastSel : false,
23327     lastSelNode : false,
23328     
23329     
23330     getSelection : function() 
23331     {
23332         this.assignDocWin();
23333         return Roo.isIE ? this.doc.selection : this.win.getSelection();
23334     },
23335     
23336     getSelectedNode: function() 
23337     {
23338         // this may only work on Gecko!!!
23339         
23340         // should we cache this!!!!
23341         
23342         
23343         
23344          
23345         var range = this.createRange(this.getSelection()).cloneRange();
23346         
23347         if (Roo.isIE) {
23348             var parent = range.parentElement();
23349             while (true) {
23350                 var testRange = range.duplicate();
23351                 testRange.moveToElementText(parent);
23352                 if (testRange.inRange(range)) {
23353                     break;
23354                 }
23355                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
23356                     break;
23357                 }
23358                 parent = parent.parentElement;
23359             }
23360             return parent;
23361         }
23362         
23363         // is ancestor a text element.
23364         var ac =  range.commonAncestorContainer;
23365         if (ac.nodeType == 3) {
23366             ac = ac.parentNode;
23367         }
23368         
23369         var ar = ac.childNodes;
23370          
23371         var nodes = [];
23372         var other_nodes = [];
23373         var has_other_nodes = false;
23374         for (var i=0;i<ar.length;i++) {
23375             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
23376                 continue;
23377             }
23378             // fullly contained node.
23379             
23380             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
23381                 nodes.push(ar[i]);
23382                 continue;
23383             }
23384             
23385             // probably selected..
23386             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
23387                 other_nodes.push(ar[i]);
23388                 continue;
23389             }
23390             // outer..
23391             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
23392                 continue;
23393             }
23394             
23395             
23396             has_other_nodes = true;
23397         }
23398         if (!nodes.length && other_nodes.length) {
23399             nodes= other_nodes;
23400         }
23401         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
23402             return false;
23403         }
23404         
23405         return nodes[0];
23406     },
23407     createRange: function(sel)
23408     {
23409         // this has strange effects when using with 
23410         // top toolbar - not sure if it's a great idea.
23411         //this.editor.contentWindow.focus();
23412         if (typeof sel != "undefined") {
23413             try {
23414                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
23415             } catch(e) {
23416                 return this.doc.createRange();
23417             }
23418         } else {
23419             return this.doc.createRange();
23420         }
23421     },
23422     getParentElement: function()
23423     {
23424         
23425         this.assignDocWin();
23426         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
23427         
23428         var range = this.createRange(sel);
23429          
23430         try {
23431             var p = range.commonAncestorContainer;
23432             while (p.nodeType == 3) { // text node
23433                 p = p.parentNode;
23434             }
23435             return p;
23436         } catch (e) {
23437             return null;
23438         }
23439     
23440     },
23441     /***
23442      *
23443      * Range intersection.. the hard stuff...
23444      *  '-1' = before
23445      *  '0' = hits..
23446      *  '1' = after.
23447      *         [ -- selected range --- ]
23448      *   [fail]                        [fail]
23449      *
23450      *    basically..
23451      *      if end is before start or  hits it. fail.
23452      *      if start is after end or hits it fail.
23453      *
23454      *   if either hits (but other is outside. - then it's not 
23455      *   
23456      *    
23457      **/
23458     
23459     
23460     // @see http://www.thismuchiknow.co.uk/?p=64.
23461     rangeIntersectsNode : function(range, node)
23462     {
23463         var nodeRange = node.ownerDocument.createRange();
23464         try {
23465             nodeRange.selectNode(node);
23466         } catch (e) {
23467             nodeRange.selectNodeContents(node);
23468         }
23469     
23470         var rangeStartRange = range.cloneRange();
23471         rangeStartRange.collapse(true);
23472     
23473         var rangeEndRange = range.cloneRange();
23474         rangeEndRange.collapse(false);
23475     
23476         var nodeStartRange = nodeRange.cloneRange();
23477         nodeStartRange.collapse(true);
23478     
23479         var nodeEndRange = nodeRange.cloneRange();
23480         nodeEndRange.collapse(false);
23481     
23482         return rangeStartRange.compareBoundaryPoints(
23483                  Range.START_TO_START, nodeEndRange) == -1 &&
23484                rangeEndRange.compareBoundaryPoints(
23485                  Range.START_TO_START, nodeStartRange) == 1;
23486         
23487          
23488     },
23489     rangeCompareNode : function(range, node)
23490     {
23491         var nodeRange = node.ownerDocument.createRange();
23492         try {
23493             nodeRange.selectNode(node);
23494         } catch (e) {
23495             nodeRange.selectNodeContents(node);
23496         }
23497         
23498         
23499         range.collapse(true);
23500     
23501         nodeRange.collapse(true);
23502      
23503         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23504         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
23505          
23506         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23507         
23508         var nodeIsBefore   =  ss == 1;
23509         var nodeIsAfter    = ee == -1;
23510         
23511         if (nodeIsBefore && nodeIsAfter) {
23512             return 0; // outer
23513         }
23514         if (!nodeIsBefore && nodeIsAfter) {
23515             return 1; //right trailed.
23516         }
23517         
23518         if (nodeIsBefore && !nodeIsAfter) {
23519             return 2;  // left trailed.
23520         }
23521         // fully contined.
23522         return 3;
23523     },
23524
23525     // private? - in a new class?
23526     cleanUpPaste :  function()
23527     {
23528         // cleans up the whole document..
23529         Roo.log('cleanuppaste');
23530         
23531         this.cleanUpChildren(this.doc.body);
23532         var clean = this.cleanWordChars(this.doc.body.innerHTML);
23533         if (clean != this.doc.body.innerHTML) {
23534             this.doc.body.innerHTML = clean;
23535         }
23536         
23537     },
23538     
23539     cleanWordChars : function(input) {// change the chars to hex code
23540         var he = Roo.HtmlEditorCore;
23541         
23542         var output = input;
23543         Roo.each(he.swapCodes, function(sw) { 
23544             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23545             
23546             output = output.replace(swapper, sw[1]);
23547         });
23548         
23549         return output;
23550     },
23551     
23552     
23553     cleanUpChildren : function (n)
23554     {
23555         if (!n.childNodes.length) {
23556             return;
23557         }
23558         for (var i = n.childNodes.length-1; i > -1 ; i--) {
23559            this.cleanUpChild(n.childNodes[i]);
23560         }
23561     },
23562     
23563     
23564         
23565     
23566     cleanUpChild : function (node)
23567     {
23568         var ed = this;
23569         //console.log(node);
23570         if (node.nodeName == "#text") {
23571             // clean up silly Windows -- stuff?
23572             return; 
23573         }
23574         if (node.nodeName == "#comment") {
23575             node.parentNode.removeChild(node);
23576             // clean up silly Windows -- stuff?
23577             return; 
23578         }
23579         var lcname = node.tagName.toLowerCase();
23580         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23581         // whitelist of tags..
23582         
23583         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23584             // remove node.
23585             node.parentNode.removeChild(node);
23586             return;
23587             
23588         }
23589         
23590         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23591         
23592         // spans with no attributes - just remove them..
23593         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
23594             remove_keep_children = true;
23595         }
23596         
23597         // remove <a name=....> as rendering on yahoo mailer is borked with this.
23598         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23599         
23600         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23601         //    remove_keep_children = true;
23602         //}
23603         
23604         if (remove_keep_children) {
23605             this.cleanUpChildren(node);
23606             // inserts everything just before this node...
23607             while (node.childNodes.length) {
23608                 var cn = node.childNodes[0];
23609                 node.removeChild(cn);
23610                 node.parentNode.insertBefore(cn, node);
23611             }
23612             node.parentNode.removeChild(node);
23613             return;
23614         }
23615         
23616         if (!node.attributes || !node.attributes.length) {
23617             
23618           
23619             
23620             
23621             this.cleanUpChildren(node);
23622             return;
23623         }
23624         
23625         function cleanAttr(n,v)
23626         {
23627             
23628             if (v.match(/^\./) || v.match(/^\//)) {
23629                 return;
23630             }
23631             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23632                 return;
23633             }
23634             if (v.match(/^#/)) {
23635                 return;
23636             }
23637 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23638             node.removeAttribute(n);
23639             
23640         }
23641         
23642         var cwhite = this.cwhite;
23643         var cblack = this.cblack;
23644             
23645         function cleanStyle(n,v)
23646         {
23647             if (v.match(/expression/)) { //XSS?? should we even bother..
23648                 node.removeAttribute(n);
23649                 return;
23650             }
23651             
23652             var parts = v.split(/;/);
23653             var clean = [];
23654             
23655             Roo.each(parts, function(p) {
23656                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23657                 if (!p.length) {
23658                     return true;
23659                 }
23660                 var l = p.split(':').shift().replace(/\s+/g,'');
23661                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23662                 
23663                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23664 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23665                     //node.removeAttribute(n);
23666                     return true;
23667                 }
23668                 //Roo.log()
23669                 // only allow 'c whitelisted system attributes'
23670                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23671 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23672                     //node.removeAttribute(n);
23673                     return true;
23674                 }
23675                 
23676                 
23677                  
23678                 
23679                 clean.push(p);
23680                 return true;
23681             });
23682             if (clean.length) { 
23683                 node.setAttribute(n, clean.join(';'));
23684             } else {
23685                 node.removeAttribute(n);
23686             }
23687             
23688         }
23689         
23690         
23691         for (var i = node.attributes.length-1; i > -1 ; i--) {
23692             var a = node.attributes[i];
23693             //console.log(a);
23694             
23695             if (a.name.toLowerCase().substr(0,2)=='on')  {
23696                 node.removeAttribute(a.name);
23697                 continue;
23698             }
23699             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23700                 node.removeAttribute(a.name);
23701                 continue;
23702             }
23703             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23704                 cleanAttr(a.name,a.value); // fixme..
23705                 continue;
23706             }
23707             if (a.name == 'style') {
23708                 cleanStyle(a.name,a.value);
23709                 continue;
23710             }
23711             /// clean up MS crap..
23712             // tecnically this should be a list of valid class'es..
23713             
23714             
23715             if (a.name == 'class') {
23716                 if (a.value.match(/^Mso/)) {
23717                     node.removeAttribute('class');
23718                 }
23719                 
23720                 if (a.value.match(/^body$/)) {
23721                     node.removeAttribute('class');
23722                 }
23723                 continue;
23724             }
23725             
23726             // style cleanup!?
23727             // class cleanup?
23728             
23729         }
23730         
23731         
23732         this.cleanUpChildren(node);
23733         
23734         
23735     },
23736     
23737     /**
23738      * Clean up MS wordisms...
23739      */
23740     cleanWord : function(node)
23741     {
23742         if (!node) {
23743             this.cleanWord(this.doc.body);
23744             return;
23745         }
23746         
23747         if(
23748                 node.nodeName == 'SPAN' &&
23749                 !node.hasAttributes() &&
23750                 node.childNodes.length == 1 &&
23751                 node.firstChild.nodeName == "#text"  
23752         ) {
23753             var textNode = node.firstChild;
23754             node.removeChild(textNode);
23755             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
23756                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
23757             }
23758             node.parentNode.insertBefore(textNode, node);
23759             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
23760                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
23761             }
23762             node.parentNode.removeChild(node);
23763         }
23764         
23765         if (node.nodeName == "#text") {
23766             // clean up silly Windows -- stuff?
23767             return; 
23768         }
23769         if (node.nodeName == "#comment") {
23770             node.parentNode.removeChild(node);
23771             // clean up silly Windows -- stuff?
23772             return; 
23773         }
23774         
23775         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23776             node.parentNode.removeChild(node);
23777             return;
23778         }
23779         //Roo.log(node.tagName);
23780         // remove - but keep children..
23781         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
23782             //Roo.log('-- removed');
23783             while (node.childNodes.length) {
23784                 var cn = node.childNodes[0];
23785                 node.removeChild(cn);
23786                 node.parentNode.insertBefore(cn, node);
23787                 // move node to parent - and clean it..
23788                 this.cleanWord(cn);
23789             }
23790             node.parentNode.removeChild(node);
23791             /// no need to iterate chidlren = it's got none..
23792             //this.iterateChildren(node, this.cleanWord);
23793             return;
23794         }
23795         // clean styles
23796         if (node.className.length) {
23797             
23798             var cn = node.className.split(/\W+/);
23799             var cna = [];
23800             Roo.each(cn, function(cls) {
23801                 if (cls.match(/Mso[a-zA-Z]+/)) {
23802                     return;
23803                 }
23804                 cna.push(cls);
23805             });
23806             node.className = cna.length ? cna.join(' ') : '';
23807             if (!cna.length) {
23808                 node.removeAttribute("class");
23809             }
23810         }
23811         
23812         if (node.hasAttribute("lang")) {
23813             node.removeAttribute("lang");
23814         }
23815         
23816         if (node.hasAttribute("style")) {
23817             
23818             var styles = node.getAttribute("style").split(";");
23819             var nstyle = [];
23820             Roo.each(styles, function(s) {
23821                 if (!s.match(/:/)) {
23822                     return;
23823                 }
23824                 var kv = s.split(":");
23825                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23826                     return;
23827                 }
23828                 // what ever is left... we allow.
23829                 nstyle.push(s);
23830             });
23831             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23832             if (!nstyle.length) {
23833                 node.removeAttribute('style');
23834             }
23835         }
23836         this.iterateChildren(node, this.cleanWord);
23837         
23838         
23839         
23840     },
23841     /**
23842      * iterateChildren of a Node, calling fn each time, using this as the scole..
23843      * @param {DomNode} node node to iterate children of.
23844      * @param {Function} fn method of this class to call on each item.
23845      */
23846     iterateChildren : function(node, fn)
23847     {
23848         if (!node.childNodes.length) {
23849                 return;
23850         }
23851         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23852            fn.call(this, node.childNodes[i])
23853         }
23854     },
23855     
23856     
23857     /**
23858      * cleanTableWidths.
23859      *
23860      * Quite often pasting from word etc.. results in tables with column and widths.
23861      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23862      *
23863      */
23864     cleanTableWidths : function(node)
23865     {
23866          
23867          
23868         if (!node) {
23869             this.cleanTableWidths(this.doc.body);
23870             return;
23871         }
23872         
23873         // ignore list...
23874         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23875             return; 
23876         }
23877         Roo.log(node.tagName);
23878         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23879             this.iterateChildren(node, this.cleanTableWidths);
23880             return;
23881         }
23882         if (node.hasAttribute('width')) {
23883             node.removeAttribute('width');
23884         }
23885         
23886          
23887         if (node.hasAttribute("style")) {
23888             // pretty basic...
23889             
23890             var styles = node.getAttribute("style").split(";");
23891             var nstyle = [];
23892             Roo.each(styles, function(s) {
23893                 if (!s.match(/:/)) {
23894                     return;
23895                 }
23896                 var kv = s.split(":");
23897                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23898                     return;
23899                 }
23900                 // what ever is left... we allow.
23901                 nstyle.push(s);
23902             });
23903             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23904             if (!nstyle.length) {
23905                 node.removeAttribute('style');
23906             }
23907         }
23908         
23909         this.iterateChildren(node, this.cleanTableWidths);
23910         
23911         
23912     },
23913     
23914     
23915     
23916     
23917     domToHTML : function(currentElement, depth, nopadtext) {
23918         
23919         depth = depth || 0;
23920         nopadtext = nopadtext || false;
23921     
23922         if (!currentElement) {
23923             return this.domToHTML(this.doc.body);
23924         }
23925         
23926         //Roo.log(currentElement);
23927         var j;
23928         var allText = false;
23929         var nodeName = currentElement.nodeName;
23930         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23931         
23932         if  (nodeName == '#text') {
23933             
23934             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23935         }
23936         
23937         
23938         var ret = '';
23939         if (nodeName != 'BODY') {
23940              
23941             var i = 0;
23942             // Prints the node tagName, such as <A>, <IMG>, etc
23943             if (tagName) {
23944                 var attr = [];
23945                 for(i = 0; i < currentElement.attributes.length;i++) {
23946                     // quoting?
23947                     var aname = currentElement.attributes.item(i).name;
23948                     if (!currentElement.attributes.item(i).value.length) {
23949                         continue;
23950                     }
23951                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23952                 }
23953                 
23954                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23955             } 
23956             else {
23957                 
23958                 // eack
23959             }
23960         } else {
23961             tagName = false;
23962         }
23963         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23964             return ret;
23965         }
23966         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23967             nopadtext = true;
23968         }
23969         
23970         
23971         // Traverse the tree
23972         i = 0;
23973         var currentElementChild = currentElement.childNodes.item(i);
23974         var allText = true;
23975         var innerHTML  = '';
23976         lastnode = '';
23977         while (currentElementChild) {
23978             // Formatting code (indent the tree so it looks nice on the screen)
23979             var nopad = nopadtext;
23980             if (lastnode == 'SPAN') {
23981                 nopad  = true;
23982             }
23983             // text
23984             if  (currentElementChild.nodeName == '#text') {
23985                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23986                 toadd = nopadtext ? toadd : toadd.trim();
23987                 if (!nopad && toadd.length > 80) {
23988                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23989                 }
23990                 innerHTML  += toadd;
23991                 
23992                 i++;
23993                 currentElementChild = currentElement.childNodes.item(i);
23994                 lastNode = '';
23995                 continue;
23996             }
23997             allText = false;
23998             
23999             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
24000                 
24001             // Recursively traverse the tree structure of the child node
24002             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
24003             lastnode = currentElementChild.nodeName;
24004             i++;
24005             currentElementChild=currentElement.childNodes.item(i);
24006         }
24007         
24008         ret += innerHTML;
24009         
24010         if (!allText) {
24011                 // The remaining code is mostly for formatting the tree
24012             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
24013         }
24014         
24015         
24016         if (tagName) {
24017             ret+= "</"+tagName+">";
24018         }
24019         return ret;
24020         
24021     },
24022         
24023     applyBlacklists : function()
24024     {
24025         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
24026         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
24027         
24028         this.white = [];
24029         this.black = [];
24030         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
24031             if (b.indexOf(tag) > -1) {
24032                 return;
24033             }
24034             this.white.push(tag);
24035             
24036         }, this);
24037         
24038         Roo.each(w, function(tag) {
24039             if (b.indexOf(tag) > -1) {
24040                 return;
24041             }
24042             if (this.white.indexOf(tag) > -1) {
24043                 return;
24044             }
24045             this.white.push(tag);
24046             
24047         }, this);
24048         
24049         
24050         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
24051             if (w.indexOf(tag) > -1) {
24052                 return;
24053             }
24054             this.black.push(tag);
24055             
24056         }, this);
24057         
24058         Roo.each(b, function(tag) {
24059             if (w.indexOf(tag) > -1) {
24060                 return;
24061             }
24062             if (this.black.indexOf(tag) > -1) {
24063                 return;
24064             }
24065             this.black.push(tag);
24066             
24067         }, this);
24068         
24069         
24070         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
24071         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
24072         
24073         this.cwhite = [];
24074         this.cblack = [];
24075         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
24076             if (b.indexOf(tag) > -1) {
24077                 return;
24078             }
24079             this.cwhite.push(tag);
24080             
24081         }, this);
24082         
24083         Roo.each(w, function(tag) {
24084             if (b.indexOf(tag) > -1) {
24085                 return;
24086             }
24087             if (this.cwhite.indexOf(tag) > -1) {
24088                 return;
24089             }
24090             this.cwhite.push(tag);
24091             
24092         }, this);
24093         
24094         
24095         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
24096             if (w.indexOf(tag) > -1) {
24097                 return;
24098             }
24099             this.cblack.push(tag);
24100             
24101         }, this);
24102         
24103         Roo.each(b, function(tag) {
24104             if (w.indexOf(tag) > -1) {
24105                 return;
24106             }
24107             if (this.cblack.indexOf(tag) > -1) {
24108                 return;
24109             }
24110             this.cblack.push(tag);
24111             
24112         }, this);
24113     },
24114     
24115     setStylesheets : function(stylesheets)
24116     {
24117         if(typeof(stylesheets) == 'string'){
24118             Roo.get(this.iframe.contentDocument.head).createChild({
24119                 tag : 'link',
24120                 rel : 'stylesheet',
24121                 type : 'text/css',
24122                 href : stylesheets
24123             });
24124             
24125             return;
24126         }
24127         var _this = this;
24128      
24129         Roo.each(stylesheets, function(s) {
24130             if(!s.length){
24131                 return;
24132             }
24133             
24134             Roo.get(_this.iframe.contentDocument.head).createChild({
24135                 tag : 'link',
24136                 rel : 'stylesheet',
24137                 type : 'text/css',
24138                 href : s
24139             });
24140         });
24141
24142         
24143     },
24144     
24145     removeStylesheets : function()
24146     {
24147         var _this = this;
24148         
24149         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
24150             s.remove();
24151         });
24152     },
24153     
24154     setStyle : function(style)
24155     {
24156         Roo.get(this.iframe.contentDocument.head).createChild({
24157             tag : 'style',
24158             type : 'text/css',
24159             html : style
24160         });
24161
24162         return;
24163     }
24164     
24165     // hide stuff that is not compatible
24166     /**
24167      * @event blur
24168      * @hide
24169      */
24170     /**
24171      * @event change
24172      * @hide
24173      */
24174     /**
24175      * @event focus
24176      * @hide
24177      */
24178     /**
24179      * @event specialkey
24180      * @hide
24181      */
24182     /**
24183      * @cfg {String} fieldClass @hide
24184      */
24185     /**
24186      * @cfg {String} focusClass @hide
24187      */
24188     /**
24189      * @cfg {String} autoCreate @hide
24190      */
24191     /**
24192      * @cfg {String} inputType @hide
24193      */
24194     /**
24195      * @cfg {String} invalidClass @hide
24196      */
24197     /**
24198      * @cfg {String} invalidText @hide
24199      */
24200     /**
24201      * @cfg {String} msgFx @hide
24202      */
24203     /**
24204      * @cfg {String} validateOnBlur @hide
24205      */
24206 });
24207
24208 Roo.HtmlEditorCore.white = [
24209         'area', 'br', 'img', 'input', 'hr', 'wbr',
24210         
24211        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24212        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24213        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24214        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24215        'table',   'ul',         'xmp', 
24216        
24217        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
24218       'thead',   'tr', 
24219      
24220       'dir', 'menu', 'ol', 'ul', 'dl',
24221        
24222       'embed',  'object'
24223 ];
24224
24225
24226 Roo.HtmlEditorCore.black = [
24227     //    'embed',  'object', // enable - backend responsiblity to clean thiese
24228         'applet', // 
24229         'base',   'basefont', 'bgsound', 'blink',  'body', 
24230         'frame',  'frameset', 'head',    'html',   'ilayer', 
24231         'iframe', 'layer',  'link',     'meta',    'object',   
24232         'script', 'style' ,'title',  'xml' // clean later..
24233 ];
24234 Roo.HtmlEditorCore.clean = [
24235     'script', 'style', 'title', 'xml'
24236 ];
24237 Roo.HtmlEditorCore.remove = [
24238     'font'
24239 ];
24240 // attributes..
24241
24242 Roo.HtmlEditorCore.ablack = [
24243     'on'
24244 ];
24245     
24246 Roo.HtmlEditorCore.aclean = [ 
24247     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
24248 ];
24249
24250 // protocols..
24251 Roo.HtmlEditorCore.pwhite= [
24252         'http',  'https',  'mailto'
24253 ];
24254
24255 // white listed style attributes.
24256 Roo.HtmlEditorCore.cwhite= [
24257       //  'text-align', /// default is to allow most things..
24258       
24259          
24260 //        'font-size'//??
24261 ];
24262
24263 // black listed style attributes.
24264 Roo.HtmlEditorCore.cblack= [
24265       //  'font-size' -- this can be set by the project 
24266 ];
24267
24268
24269 Roo.HtmlEditorCore.swapCodes   =[ 
24270     [    8211, "--" ], 
24271     [    8212, "--" ], 
24272     [    8216,  "'" ],  
24273     [    8217, "'" ],  
24274     [    8220, '"' ],  
24275     [    8221, '"' ],  
24276     [    8226, "*" ],  
24277     [    8230, "..." ]
24278 ]; 
24279
24280     /*
24281  * - LGPL
24282  *
24283  * HtmlEditor
24284  * 
24285  */
24286
24287 /**
24288  * @class Roo.bootstrap.HtmlEditor
24289  * @extends Roo.bootstrap.TextArea
24290  * Bootstrap HtmlEditor class
24291
24292  * @constructor
24293  * Create a new HtmlEditor
24294  * @param {Object} config The config object
24295  */
24296
24297 Roo.bootstrap.HtmlEditor = function(config){
24298     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
24299     if (!this.toolbars) {
24300         this.toolbars = [];
24301     }
24302     
24303     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
24304     this.addEvents({
24305             /**
24306              * @event initialize
24307              * Fires when the editor is fully initialized (including the iframe)
24308              * @param {HtmlEditor} this
24309              */
24310             initialize: true,
24311             /**
24312              * @event activate
24313              * Fires when the editor is first receives the focus. Any insertion must wait
24314              * until after this event.
24315              * @param {HtmlEditor} this
24316              */
24317             activate: true,
24318              /**
24319              * @event beforesync
24320              * Fires before the textarea is updated with content from the editor iframe. Return false
24321              * to cancel the sync.
24322              * @param {HtmlEditor} this
24323              * @param {String} html
24324              */
24325             beforesync: true,
24326              /**
24327              * @event beforepush
24328              * Fires before the iframe editor is updated with content from the textarea. Return false
24329              * to cancel the push.
24330              * @param {HtmlEditor} this
24331              * @param {String} html
24332              */
24333             beforepush: true,
24334              /**
24335              * @event sync
24336              * Fires when the textarea is updated with content from the editor iframe.
24337              * @param {HtmlEditor} this
24338              * @param {String} html
24339              */
24340             sync: true,
24341              /**
24342              * @event push
24343              * Fires when the iframe editor is updated with content from the textarea.
24344              * @param {HtmlEditor} this
24345              * @param {String} html
24346              */
24347             push: true,
24348              /**
24349              * @event editmodechange
24350              * Fires when the editor switches edit modes
24351              * @param {HtmlEditor} this
24352              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24353              */
24354             editmodechange: true,
24355             /**
24356              * @event editorevent
24357              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24358              * @param {HtmlEditor} this
24359              */
24360             editorevent: true,
24361             /**
24362              * @event firstfocus
24363              * Fires when on first focus - needed by toolbars..
24364              * @param {HtmlEditor} this
24365              */
24366             firstfocus: true,
24367             /**
24368              * @event autosave
24369              * Auto save the htmlEditor value as a file into Events
24370              * @param {HtmlEditor} this
24371              */
24372             autosave: true,
24373             /**
24374              * @event savedpreview
24375              * preview the saved version of htmlEditor
24376              * @param {HtmlEditor} this
24377              */
24378             savedpreview: true
24379         });
24380 };
24381
24382
24383 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
24384     
24385     
24386       /**
24387      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24388      */
24389     toolbars : false,
24390     
24391      /**
24392     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
24393     */
24394     btns : [],
24395    
24396      /**
24397      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24398      *                        Roo.resizable.
24399      */
24400     resizable : false,
24401      /**
24402      * @cfg {Number} height (in pixels)
24403      */   
24404     height: 300,
24405    /**
24406      * @cfg {Number} width (in pixels)
24407      */   
24408     width: false,
24409     
24410     /**
24411      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24412      * 
24413      */
24414     stylesheets: false,
24415     
24416     // id of frame..
24417     frameId: false,
24418     
24419     // private properties
24420     validationEvent : false,
24421     deferHeight: true,
24422     initialized : false,
24423     activated : false,
24424     
24425     onFocus : Roo.emptyFn,
24426     iframePad:3,
24427     hideMode:'offsets',
24428     
24429     tbContainer : false,
24430     
24431     bodyCls : '',
24432     
24433     toolbarContainer :function() {
24434         return this.wrap.select('.x-html-editor-tb',true).first();
24435     },
24436
24437     /**
24438      * Protected method that will not generally be called directly. It
24439      * is called when the editor creates its toolbar. Override this method if you need to
24440      * add custom toolbar buttons.
24441      * @param {HtmlEditor} editor
24442      */
24443     createToolbar : function(){
24444         Roo.log('renewing');
24445         Roo.log("create toolbars");
24446         
24447         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
24448         this.toolbars[0].render(this.toolbarContainer());
24449         
24450         return;
24451         
24452 //        if (!editor.toolbars || !editor.toolbars.length) {
24453 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
24454 //        }
24455 //        
24456 //        for (var i =0 ; i < editor.toolbars.length;i++) {
24457 //            editor.toolbars[i] = Roo.factory(
24458 //                    typeof(editor.toolbars[i]) == 'string' ?
24459 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
24460 //                Roo.bootstrap.HtmlEditor);
24461 //            editor.toolbars[i].init(editor);
24462 //        }
24463     },
24464
24465      
24466     // private
24467     onRender : function(ct, position)
24468     {
24469        // Roo.log("Call onRender: " + this.xtype);
24470         var _t = this;
24471         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
24472       
24473         this.wrap = this.inputEl().wrap({
24474             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24475         });
24476         
24477         this.editorcore.onRender(ct, position);
24478          
24479         if (this.resizable) {
24480             this.resizeEl = new Roo.Resizable(this.wrap, {
24481                 pinned : true,
24482                 wrap: true,
24483                 dynamic : true,
24484                 minHeight : this.height,
24485                 height: this.height,
24486                 handles : this.resizable,
24487                 width: this.width,
24488                 listeners : {
24489                     resize : function(r, w, h) {
24490                         _t.onResize(w,h); // -something
24491                     }
24492                 }
24493             });
24494             
24495         }
24496         this.createToolbar(this);
24497        
24498         
24499         if(!this.width && this.resizable){
24500             this.setSize(this.wrap.getSize());
24501         }
24502         if (this.resizeEl) {
24503             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24504             // should trigger onReize..
24505         }
24506         
24507     },
24508
24509     // private
24510     onResize : function(w, h)
24511     {
24512         Roo.log('resize: ' +w + ',' + h );
24513         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
24514         var ew = false;
24515         var eh = false;
24516         
24517         if(this.inputEl() ){
24518             if(typeof w == 'number'){
24519                 var aw = w - this.wrap.getFrameWidth('lr');
24520                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
24521                 ew = aw;
24522             }
24523             if(typeof h == 'number'){
24524                  var tbh = -11;  // fixme it needs to tool bar size!
24525                 for (var i =0; i < this.toolbars.length;i++) {
24526                     // fixme - ask toolbars for heights?
24527                     tbh += this.toolbars[i].el.getHeight();
24528                     //if (this.toolbars[i].footer) {
24529                     //    tbh += this.toolbars[i].footer.el.getHeight();
24530                     //}
24531                 }
24532               
24533                 
24534                 
24535                 
24536                 
24537                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24538                 ah -= 5; // knock a few pixes off for look..
24539                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24540                 var eh = ah;
24541             }
24542         }
24543         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24544         this.editorcore.onResize(ew,eh);
24545         
24546     },
24547
24548     /**
24549      * Toggles the editor between standard and source edit mode.
24550      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24551      */
24552     toggleSourceEdit : function(sourceEditMode)
24553     {
24554         this.editorcore.toggleSourceEdit(sourceEditMode);
24555         
24556         if(this.editorcore.sourceEditMode){
24557             Roo.log('editor - showing textarea');
24558             
24559 //            Roo.log('in');
24560 //            Roo.log(this.syncValue());
24561             this.syncValue();
24562             this.inputEl().removeClass(['hide', 'x-hidden']);
24563             this.inputEl().dom.removeAttribute('tabIndex');
24564             this.inputEl().focus();
24565         }else{
24566             Roo.log('editor - hiding textarea');
24567 //            Roo.log('out')
24568 //            Roo.log(this.pushValue()); 
24569             this.pushValue();
24570             
24571             this.inputEl().addClass(['hide', 'x-hidden']);
24572             this.inputEl().dom.setAttribute('tabIndex', -1);
24573             //this.deferFocus();
24574         }
24575          
24576         if(this.resizable){
24577             this.setSize(this.wrap.getSize());
24578         }
24579         
24580         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24581     },
24582  
24583     // private (for BoxComponent)
24584     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24585
24586     // private (for BoxComponent)
24587     getResizeEl : function(){
24588         return this.wrap;
24589     },
24590
24591     // private (for BoxComponent)
24592     getPositionEl : function(){
24593         return this.wrap;
24594     },
24595
24596     // private
24597     initEvents : function(){
24598         this.originalValue = this.getValue();
24599     },
24600
24601 //    /**
24602 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24603 //     * @method
24604 //     */
24605 //    markInvalid : Roo.emptyFn,
24606 //    /**
24607 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24608 //     * @method
24609 //     */
24610 //    clearInvalid : Roo.emptyFn,
24611
24612     setValue : function(v){
24613         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24614         this.editorcore.pushValue();
24615     },
24616
24617      
24618     // private
24619     deferFocus : function(){
24620         this.focus.defer(10, this);
24621     },
24622
24623     // doc'ed in Field
24624     focus : function(){
24625         this.editorcore.focus();
24626         
24627     },
24628       
24629
24630     // private
24631     onDestroy : function(){
24632         
24633         
24634         
24635         if(this.rendered){
24636             
24637             for (var i =0; i < this.toolbars.length;i++) {
24638                 // fixme - ask toolbars for heights?
24639                 this.toolbars[i].onDestroy();
24640             }
24641             
24642             this.wrap.dom.innerHTML = '';
24643             this.wrap.remove();
24644         }
24645     },
24646
24647     // private
24648     onFirstFocus : function(){
24649         //Roo.log("onFirstFocus");
24650         this.editorcore.onFirstFocus();
24651          for (var i =0; i < this.toolbars.length;i++) {
24652             this.toolbars[i].onFirstFocus();
24653         }
24654         
24655     },
24656     
24657     // private
24658     syncValue : function()
24659     {   
24660         this.editorcore.syncValue();
24661     },
24662     
24663     pushValue : function()
24664     {   
24665         this.editorcore.pushValue();
24666     }
24667      
24668     
24669     // hide stuff that is not compatible
24670     /**
24671      * @event blur
24672      * @hide
24673      */
24674     /**
24675      * @event change
24676      * @hide
24677      */
24678     /**
24679      * @event focus
24680      * @hide
24681      */
24682     /**
24683      * @event specialkey
24684      * @hide
24685      */
24686     /**
24687      * @cfg {String} fieldClass @hide
24688      */
24689     /**
24690      * @cfg {String} focusClass @hide
24691      */
24692     /**
24693      * @cfg {String} autoCreate @hide
24694      */
24695     /**
24696      * @cfg {String} inputType @hide
24697      */
24698      
24699     /**
24700      * @cfg {String} invalidText @hide
24701      */
24702     /**
24703      * @cfg {String} msgFx @hide
24704      */
24705     /**
24706      * @cfg {String} validateOnBlur @hide
24707      */
24708 });
24709  
24710     
24711    
24712    
24713    
24714       
24715 Roo.namespace('Roo.bootstrap.htmleditor');
24716 /**
24717  * @class Roo.bootstrap.HtmlEditorToolbar1
24718  * Basic Toolbar
24719  * 
24720  * @example
24721  * Usage:
24722  *
24723  new Roo.bootstrap.HtmlEditor({
24724     ....
24725     toolbars : [
24726         new Roo.bootstrap.HtmlEditorToolbar1({
24727             disable : { fonts: 1 , format: 1, ..., ... , ...],
24728             btns : [ .... ]
24729         })
24730     }
24731      
24732  * 
24733  * @cfg {Object} disable List of elements to disable..
24734  * @cfg {Array} btns List of additional buttons.
24735  * 
24736  * 
24737  * NEEDS Extra CSS? 
24738  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24739  */
24740  
24741 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24742 {
24743     
24744     Roo.apply(this, config);
24745     
24746     // default disabled, based on 'good practice'..
24747     this.disable = this.disable || {};
24748     Roo.applyIf(this.disable, {
24749         fontSize : true,
24750         colors : true,
24751         specialElements : true
24752     });
24753     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24754     
24755     this.editor = config.editor;
24756     this.editorcore = config.editor.editorcore;
24757     
24758     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24759     
24760     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24761     // dont call parent... till later.
24762 }
24763 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24764      
24765     bar : true,
24766     
24767     editor : false,
24768     editorcore : false,
24769     
24770     
24771     formats : [
24772         "p" ,  
24773         "h1","h2","h3","h4","h5","h6", 
24774         "pre", "code", 
24775         "abbr", "acronym", "address", "cite", "samp", "var",
24776         'div','span'
24777     ],
24778     
24779     onRender : function(ct, position)
24780     {
24781        // Roo.log("Call onRender: " + this.xtype);
24782         
24783        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24784        Roo.log(this.el);
24785        this.el.dom.style.marginBottom = '0';
24786        var _this = this;
24787        var editorcore = this.editorcore;
24788        var editor= this.editor;
24789        
24790        var children = [];
24791        var btn = function(id,cmd , toggle, handler, html){
24792        
24793             var  event = toggle ? 'toggle' : 'click';
24794        
24795             var a = {
24796                 size : 'sm',
24797                 xtype: 'Button',
24798                 xns: Roo.bootstrap,
24799                 //glyphicon : id,
24800                 fa: id,
24801                 cmd : id || cmd,
24802                 enableToggle:toggle !== false,
24803                 html : html || '',
24804                 pressed : toggle ? false : null,
24805                 listeners : {}
24806             };
24807             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24808                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24809             };
24810             children.push(a);
24811             return a;
24812        }
24813        
24814     //    var cb_box = function...
24815         
24816         var style = {
24817                 xtype: 'Button',
24818                 size : 'sm',
24819                 xns: Roo.bootstrap,
24820                 fa : 'font',
24821                 //html : 'submit'
24822                 menu : {
24823                     xtype: 'Menu',
24824                     xns: Roo.bootstrap,
24825                     items:  []
24826                 }
24827         };
24828         Roo.each(this.formats, function(f) {
24829             style.menu.items.push({
24830                 xtype :'MenuItem',
24831                 xns: Roo.bootstrap,
24832                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24833                 tagname : f,
24834                 listeners : {
24835                     click : function()
24836                     {
24837                         editorcore.insertTag(this.tagname);
24838                         editor.focus();
24839                     }
24840                 }
24841                 
24842             });
24843         });
24844         children.push(style);   
24845         
24846         btn('bold',false,true);
24847         btn('italic',false,true);
24848         btn('align-left', 'justifyleft',true);
24849         btn('align-center', 'justifycenter',true);
24850         btn('align-right' , 'justifyright',true);
24851         btn('link', false, false, function(btn) {
24852             //Roo.log("create link?");
24853             var url = prompt(this.createLinkText, this.defaultLinkValue);
24854             if(url && url != 'http:/'+'/'){
24855                 this.editorcore.relayCmd('createlink', url);
24856             }
24857         }),
24858         btn('list','insertunorderedlist',true);
24859         btn('pencil', false,true, function(btn){
24860                 Roo.log(this);
24861                 this.toggleSourceEdit(btn.pressed);
24862         });
24863         
24864         if (this.editor.btns.length > 0) {
24865             for (var i = 0; i<this.editor.btns.length; i++) {
24866                 children.push(this.editor.btns[i]);
24867             }
24868         }
24869         
24870         /*
24871         var cog = {
24872                 xtype: 'Button',
24873                 size : 'sm',
24874                 xns: Roo.bootstrap,
24875                 glyphicon : 'cog',
24876                 //html : 'submit'
24877                 menu : {
24878                     xtype: 'Menu',
24879                     xns: Roo.bootstrap,
24880                     items:  []
24881                 }
24882         };
24883         
24884         cog.menu.items.push({
24885             xtype :'MenuItem',
24886             xns: Roo.bootstrap,
24887             html : Clean styles,
24888             tagname : f,
24889             listeners : {
24890                 click : function()
24891                 {
24892                     editorcore.insertTag(this.tagname);
24893                     editor.focus();
24894                 }
24895             }
24896             
24897         });
24898        */
24899         
24900          
24901        this.xtype = 'NavSimplebar';
24902         
24903         for(var i=0;i< children.length;i++) {
24904             
24905             this.buttons.add(this.addxtypeChild(children[i]));
24906             
24907         }
24908         
24909         editor.on('editorevent', this.updateToolbar, this);
24910     },
24911     onBtnClick : function(id)
24912     {
24913        this.editorcore.relayCmd(id);
24914        this.editorcore.focus();
24915     },
24916     
24917     /**
24918      * Protected method that will not generally be called directly. It triggers
24919      * a toolbar update by reading the markup state of the current selection in the editor.
24920      */
24921     updateToolbar: function(){
24922
24923         if(!this.editorcore.activated){
24924             this.editor.onFirstFocus(); // is this neeed?
24925             return;
24926         }
24927
24928         var btns = this.buttons; 
24929         var doc = this.editorcore.doc;
24930         btns.get('bold').setActive(doc.queryCommandState('bold'));
24931         btns.get('italic').setActive(doc.queryCommandState('italic'));
24932         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24933         
24934         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24935         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24936         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24937         
24938         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24939         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24940          /*
24941         
24942         var ans = this.editorcore.getAllAncestors();
24943         if (this.formatCombo) {
24944             
24945             
24946             var store = this.formatCombo.store;
24947             this.formatCombo.setValue("");
24948             for (var i =0; i < ans.length;i++) {
24949                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24950                     // select it..
24951                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24952                     break;
24953                 }
24954             }
24955         }
24956         
24957         
24958         
24959         // hides menus... - so this cant be on a menu...
24960         Roo.bootstrap.MenuMgr.hideAll();
24961         */
24962         Roo.bootstrap.MenuMgr.hideAll();
24963         //this.editorsyncValue();
24964     },
24965     onFirstFocus: function() {
24966         this.buttons.each(function(item){
24967            item.enable();
24968         });
24969     },
24970     toggleSourceEdit : function(sourceEditMode){
24971         
24972           
24973         if(sourceEditMode){
24974             Roo.log("disabling buttons");
24975            this.buttons.each( function(item){
24976                 if(item.cmd != 'pencil'){
24977                     item.disable();
24978                 }
24979             });
24980           
24981         }else{
24982             Roo.log("enabling buttons");
24983             if(this.editorcore.initialized){
24984                 this.buttons.each( function(item){
24985                     item.enable();
24986                 });
24987             }
24988             
24989         }
24990         Roo.log("calling toggole on editor");
24991         // tell the editor that it's been pressed..
24992         this.editor.toggleSourceEdit(sourceEditMode);
24993        
24994     }
24995 });
24996
24997
24998
24999
25000
25001 /**
25002  * @class Roo.bootstrap.Table.AbstractSelectionModel
25003  * @extends Roo.util.Observable
25004  * Abstract base class for grid SelectionModels.  It provides the interface that should be
25005  * implemented by descendant classes.  This class should not be directly instantiated.
25006  * @constructor
25007  */
25008 Roo.bootstrap.Table.AbstractSelectionModel = function(){
25009     this.locked = false;
25010     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
25011 };
25012
25013
25014 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
25015     /** @ignore Called by the grid automatically. Do not call directly. */
25016     init : function(grid){
25017         this.grid = grid;
25018         this.initEvents();
25019     },
25020
25021     /**
25022      * Locks the selections.
25023      */
25024     lock : function(){
25025         this.locked = true;
25026     },
25027
25028     /**
25029      * Unlocks the selections.
25030      */
25031     unlock : function(){
25032         this.locked = false;
25033     },
25034
25035     /**
25036      * Returns true if the selections are locked.
25037      * @return {Boolean}
25038      */
25039     isLocked : function(){
25040         return this.locked;
25041     },
25042     
25043     
25044     initEvents : function ()
25045     {
25046         
25047     }
25048 });
25049 /**
25050  * @extends Roo.bootstrap.Table.AbstractSelectionModel
25051  * @class Roo.bootstrap.Table.RowSelectionModel
25052  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
25053  * It supports multiple selections and keyboard selection/navigation. 
25054  * @constructor
25055  * @param {Object} config
25056  */
25057
25058 Roo.bootstrap.Table.RowSelectionModel = function(config){
25059     Roo.apply(this, config);
25060     this.selections = new Roo.util.MixedCollection(false, function(o){
25061         return o.id;
25062     });
25063
25064     this.last = false;
25065     this.lastActive = false;
25066
25067     this.addEvents({
25068         /**
25069              * @event selectionchange
25070              * Fires when the selection changes
25071              * @param {SelectionModel} this
25072              */
25073             "selectionchange" : true,
25074         /**
25075              * @event afterselectionchange
25076              * Fires after the selection changes (eg. by key press or clicking)
25077              * @param {SelectionModel} this
25078              */
25079             "afterselectionchange" : true,
25080         /**
25081              * @event beforerowselect
25082              * Fires when a row is selected being selected, return false to cancel.
25083              * @param {SelectionModel} this
25084              * @param {Number} rowIndex The selected index
25085              * @param {Boolean} keepExisting False if other selections will be cleared
25086              */
25087             "beforerowselect" : true,
25088         /**
25089              * @event rowselect
25090              * Fires when a row is selected.
25091              * @param {SelectionModel} this
25092              * @param {Number} rowIndex The selected index
25093              * @param {Roo.data.Record} r The record
25094              */
25095             "rowselect" : true,
25096         /**
25097              * @event rowdeselect
25098              * Fires when a row is deselected.
25099              * @param {SelectionModel} this
25100              * @param {Number} rowIndex The selected index
25101              */
25102         "rowdeselect" : true
25103     });
25104     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
25105     this.locked = false;
25106  };
25107
25108 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
25109     /**
25110      * @cfg {Boolean} singleSelect
25111      * True to allow selection of only one row at a time (defaults to false)
25112      */
25113     singleSelect : false,
25114
25115     // private
25116     initEvents : function()
25117     {
25118
25119         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
25120         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
25121         //}else{ // allow click to work like normal
25122          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
25123         //}
25124         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
25125         this.grid.on("rowclick", this.handleMouseDown, this);
25126         
25127         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
25128             "up" : function(e){
25129                 if(!e.shiftKey){
25130                     this.selectPrevious(e.shiftKey);
25131                 }else if(this.last !== false && this.lastActive !== false){
25132                     var last = this.last;
25133                     this.selectRange(this.last,  this.lastActive-1);
25134                     this.grid.getView().focusRow(this.lastActive);
25135                     if(last !== false){
25136                         this.last = last;
25137                     }
25138                 }else{
25139                     this.selectFirstRow();
25140                 }
25141                 this.fireEvent("afterselectionchange", this);
25142             },
25143             "down" : function(e){
25144                 if(!e.shiftKey){
25145                     this.selectNext(e.shiftKey);
25146                 }else if(this.last !== false && this.lastActive !== false){
25147                     var last = this.last;
25148                     this.selectRange(this.last,  this.lastActive+1);
25149                     this.grid.getView().focusRow(this.lastActive);
25150                     if(last !== false){
25151                         this.last = last;
25152                     }
25153                 }else{
25154                     this.selectFirstRow();
25155                 }
25156                 this.fireEvent("afterselectionchange", this);
25157             },
25158             scope: this
25159         });
25160         this.grid.store.on('load', function(){
25161             this.selections.clear();
25162         },this);
25163         /*
25164         var view = this.grid.view;
25165         view.on("refresh", this.onRefresh, this);
25166         view.on("rowupdated", this.onRowUpdated, this);
25167         view.on("rowremoved", this.onRemove, this);
25168         */
25169     },
25170
25171     // private
25172     onRefresh : function()
25173     {
25174         var ds = this.grid.store, i, v = this.grid.view;
25175         var s = this.selections;
25176         s.each(function(r){
25177             if((i = ds.indexOfId(r.id)) != -1){
25178                 v.onRowSelect(i);
25179             }else{
25180                 s.remove(r);
25181             }
25182         });
25183     },
25184
25185     // private
25186     onRemove : function(v, index, r){
25187         this.selections.remove(r);
25188     },
25189
25190     // private
25191     onRowUpdated : function(v, index, r){
25192         if(this.isSelected(r)){
25193             v.onRowSelect(index);
25194         }
25195     },
25196
25197     /**
25198      * Select records.
25199      * @param {Array} records The records to select
25200      * @param {Boolean} keepExisting (optional) True to keep existing selections
25201      */
25202     selectRecords : function(records, keepExisting)
25203     {
25204         if(!keepExisting){
25205             this.clearSelections();
25206         }
25207             var ds = this.grid.store;
25208         for(var i = 0, len = records.length; i < len; i++){
25209             this.selectRow(ds.indexOf(records[i]), true);
25210         }
25211     },
25212
25213     /**
25214      * Gets the number of selected rows.
25215      * @return {Number}
25216      */
25217     getCount : function(){
25218         return this.selections.length;
25219     },
25220
25221     /**
25222      * Selects the first row in the grid.
25223      */
25224     selectFirstRow : function(){
25225         this.selectRow(0);
25226     },
25227
25228     /**
25229      * Select the last row.
25230      * @param {Boolean} keepExisting (optional) True to keep existing selections
25231      */
25232     selectLastRow : function(keepExisting){
25233         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
25234         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
25235     },
25236
25237     /**
25238      * Selects the row immediately following the last selected row.
25239      * @param {Boolean} keepExisting (optional) True to keep existing selections
25240      */
25241     selectNext : function(keepExisting)
25242     {
25243             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
25244             this.selectRow(this.last+1, keepExisting);
25245             this.grid.getView().focusRow(this.last);
25246         }
25247     },
25248
25249     /**
25250      * Selects the row that precedes the last selected row.
25251      * @param {Boolean} keepExisting (optional) True to keep existing selections
25252      */
25253     selectPrevious : function(keepExisting){
25254         if(this.last){
25255             this.selectRow(this.last-1, keepExisting);
25256             this.grid.getView().focusRow(this.last);
25257         }
25258     },
25259
25260     /**
25261      * Returns the selected records
25262      * @return {Array} Array of selected records
25263      */
25264     getSelections : function(){
25265         return [].concat(this.selections.items);
25266     },
25267
25268     /**
25269      * Returns the first selected record.
25270      * @return {Record}
25271      */
25272     getSelected : function(){
25273         return this.selections.itemAt(0);
25274     },
25275
25276
25277     /**
25278      * Clears all selections.
25279      */
25280     clearSelections : function(fast)
25281     {
25282         if(this.locked) {
25283             return;
25284         }
25285         if(fast !== true){
25286                 var ds = this.grid.store;
25287             var s = this.selections;
25288             s.each(function(r){
25289                 this.deselectRow(ds.indexOfId(r.id));
25290             }, this);
25291             s.clear();
25292         }else{
25293             this.selections.clear();
25294         }
25295         this.last = false;
25296     },
25297
25298
25299     /**
25300      * Selects all rows.
25301      */
25302     selectAll : function(){
25303         if(this.locked) {
25304             return;
25305         }
25306         this.selections.clear();
25307         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
25308             this.selectRow(i, true);
25309         }
25310     },
25311
25312     /**
25313      * Returns True if there is a selection.
25314      * @return {Boolean}
25315      */
25316     hasSelection : function(){
25317         return this.selections.length > 0;
25318     },
25319
25320     /**
25321      * Returns True if the specified row is selected.
25322      * @param {Number/Record} record The record or index of the record to check
25323      * @return {Boolean}
25324      */
25325     isSelected : function(index){
25326             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
25327         return (r && this.selections.key(r.id) ? true : false);
25328     },
25329
25330     /**
25331      * Returns True if the specified record id is selected.
25332      * @param {String} id The id of record to check
25333      * @return {Boolean}
25334      */
25335     isIdSelected : function(id){
25336         return (this.selections.key(id) ? true : false);
25337     },
25338
25339
25340     // private
25341     handleMouseDBClick : function(e, t){
25342         
25343     },
25344     // private
25345     handleMouseDown : function(e, t)
25346     {
25347             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
25348         if(this.isLocked() || rowIndex < 0 ){
25349             return;
25350         };
25351         if(e.shiftKey && this.last !== false){
25352             var last = this.last;
25353             this.selectRange(last, rowIndex, e.ctrlKey);
25354             this.last = last; // reset the last
25355             t.focus();
25356     
25357         }else{
25358             var isSelected = this.isSelected(rowIndex);
25359             //Roo.log("select row:" + rowIndex);
25360             if(isSelected){
25361                 this.deselectRow(rowIndex);
25362             } else {
25363                         this.selectRow(rowIndex, true);
25364             }
25365     
25366             /*
25367                 if(e.button !== 0 && isSelected){
25368                 alert('rowIndex 2: ' + rowIndex);
25369                     view.focusRow(rowIndex);
25370                 }else if(e.ctrlKey && isSelected){
25371                     this.deselectRow(rowIndex);
25372                 }else if(!isSelected){
25373                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
25374                     view.focusRow(rowIndex);
25375                 }
25376             */
25377         }
25378         this.fireEvent("afterselectionchange", this);
25379     },
25380     // private
25381     handleDragableRowClick :  function(grid, rowIndex, e) 
25382     {
25383         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
25384             this.selectRow(rowIndex, false);
25385             grid.view.focusRow(rowIndex);
25386              this.fireEvent("afterselectionchange", this);
25387         }
25388     },
25389     
25390     /**
25391      * Selects multiple rows.
25392      * @param {Array} rows Array of the indexes of the row to select
25393      * @param {Boolean} keepExisting (optional) True to keep existing selections
25394      */
25395     selectRows : function(rows, keepExisting){
25396         if(!keepExisting){
25397             this.clearSelections();
25398         }
25399         for(var i = 0, len = rows.length; i < len; i++){
25400             this.selectRow(rows[i], true);
25401         }
25402     },
25403
25404     /**
25405      * Selects a range of rows. All rows in between startRow and endRow are also selected.
25406      * @param {Number} startRow The index of the first row in the range
25407      * @param {Number} endRow The index of the last row in the range
25408      * @param {Boolean} keepExisting (optional) True to retain existing selections
25409      */
25410     selectRange : function(startRow, endRow, keepExisting){
25411         if(this.locked) {
25412             return;
25413         }
25414         if(!keepExisting){
25415             this.clearSelections();
25416         }
25417         if(startRow <= endRow){
25418             for(var i = startRow; i <= endRow; i++){
25419                 this.selectRow(i, true);
25420             }
25421         }else{
25422             for(var i = startRow; i >= endRow; i--){
25423                 this.selectRow(i, true);
25424             }
25425         }
25426     },
25427
25428     /**
25429      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
25430      * @param {Number} startRow The index of the first row in the range
25431      * @param {Number} endRow The index of the last row in the range
25432      */
25433     deselectRange : function(startRow, endRow, preventViewNotify){
25434         if(this.locked) {
25435             return;
25436         }
25437         for(var i = startRow; i <= endRow; i++){
25438             this.deselectRow(i, preventViewNotify);
25439         }
25440     },
25441
25442     /**
25443      * Selects a row.
25444      * @param {Number} row The index of the row to select
25445      * @param {Boolean} keepExisting (optional) True to keep existing selections
25446      */
25447     selectRow : function(index, keepExisting, preventViewNotify)
25448     {
25449             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
25450             return;
25451         }
25452         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
25453             if(!keepExisting || this.singleSelect){
25454                 this.clearSelections();
25455             }
25456             
25457             var r = this.grid.store.getAt(index);
25458             //console.log('selectRow - record id :' + r.id);
25459             
25460             this.selections.add(r);
25461             this.last = this.lastActive = index;
25462             if(!preventViewNotify){
25463                 var proxy = new Roo.Element(
25464                                 this.grid.getRowDom(index)
25465                 );
25466                 proxy.addClass('bg-info info');
25467             }
25468             this.fireEvent("rowselect", this, index, r);
25469             this.fireEvent("selectionchange", this);
25470         }
25471     },
25472
25473     /**
25474      * Deselects a row.
25475      * @param {Number} row The index of the row to deselect
25476      */
25477     deselectRow : function(index, preventViewNotify)
25478     {
25479         if(this.locked) {
25480             return;
25481         }
25482         if(this.last == index){
25483             this.last = false;
25484         }
25485         if(this.lastActive == index){
25486             this.lastActive = false;
25487         }
25488         
25489         var r = this.grid.store.getAt(index);
25490         if (!r) {
25491             return;
25492         }
25493         
25494         this.selections.remove(r);
25495         //.console.log('deselectRow - record id :' + r.id);
25496         if(!preventViewNotify){
25497         
25498             var proxy = new Roo.Element(
25499                 this.grid.getRowDom(index)
25500             );
25501             proxy.removeClass('bg-info info');
25502         }
25503         this.fireEvent("rowdeselect", this, index);
25504         this.fireEvent("selectionchange", this);
25505     },
25506
25507     // private
25508     restoreLast : function(){
25509         if(this._last){
25510             this.last = this._last;
25511         }
25512     },
25513
25514     // private
25515     acceptsNav : function(row, col, cm){
25516         return !cm.isHidden(col) && cm.isCellEditable(col, row);
25517     },
25518
25519     // private
25520     onEditorKey : function(field, e){
25521         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
25522         if(k == e.TAB){
25523             e.stopEvent();
25524             ed.completeEdit();
25525             if(e.shiftKey){
25526                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
25527             }else{
25528                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
25529             }
25530         }else if(k == e.ENTER && !e.ctrlKey){
25531             e.stopEvent();
25532             ed.completeEdit();
25533             if(e.shiftKey){
25534                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25535             }else{
25536                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25537             }
25538         }else if(k == e.ESC){
25539             ed.cancelEdit();
25540         }
25541         if(newCell){
25542             g.startEditing(newCell[0], newCell[1]);
25543         }
25544     }
25545 });
25546 /*
25547  * Based on:
25548  * Ext JS Library 1.1.1
25549  * Copyright(c) 2006-2007, Ext JS, LLC.
25550  *
25551  * Originally Released Under LGPL - original licence link has changed is not relivant.
25552  *
25553  * Fork - LGPL
25554  * <script type="text/javascript">
25555  */
25556  
25557 /**
25558  * @class Roo.bootstrap.PagingToolbar
25559  * @extends Roo.bootstrap.NavSimplebar
25560  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
25561  * @constructor
25562  * Create a new PagingToolbar
25563  * @param {Object} config The config object
25564  * @param {Roo.data.Store} store
25565  */
25566 Roo.bootstrap.PagingToolbar = function(config)
25567 {
25568     // old args format still supported... - xtype is prefered..
25569         // created from xtype...
25570     
25571     this.ds = config.dataSource;
25572     
25573     if (config.store && !this.ds) {
25574         this.store= Roo.factory(config.store, Roo.data);
25575         this.ds = this.store;
25576         this.ds.xmodule = this.xmodule || false;
25577     }
25578     
25579     this.toolbarItems = [];
25580     if (config.items) {
25581         this.toolbarItems = config.items;
25582     }
25583     
25584     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25585     
25586     this.cursor = 0;
25587     
25588     if (this.ds) { 
25589         this.bind(this.ds);
25590     }
25591     
25592     if (Roo.bootstrap.version == 4) {
25593         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
25594     } else {
25595         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
25596     }
25597     
25598 };
25599
25600 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
25601     /**
25602      * @cfg {Roo.data.Store} dataSource
25603      * The underlying data store providing the paged data
25604      */
25605     /**
25606      * @cfg {String/HTMLElement/Element} container
25607      * container The id or element that will contain the toolbar
25608      */
25609     /**
25610      * @cfg {Boolean} displayInfo
25611      * True to display the displayMsg (defaults to false)
25612      */
25613     /**
25614      * @cfg {Number} pageSize
25615      * The number of records to display per page (defaults to 20)
25616      */
25617     pageSize: 20,
25618     /**
25619      * @cfg {String} displayMsg
25620      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25621      */
25622     displayMsg : 'Displaying {0} - {1} of {2}',
25623     /**
25624      * @cfg {String} emptyMsg
25625      * The message to display when no records are found (defaults to "No data to display")
25626      */
25627     emptyMsg : 'No data to display',
25628     /**
25629      * Customizable piece of the default paging text (defaults to "Page")
25630      * @type String
25631      */
25632     beforePageText : "Page",
25633     /**
25634      * Customizable piece of the default paging text (defaults to "of %0")
25635      * @type String
25636      */
25637     afterPageText : "of {0}",
25638     /**
25639      * Customizable piece of the default paging text (defaults to "First Page")
25640      * @type String
25641      */
25642     firstText : "First Page",
25643     /**
25644      * Customizable piece of the default paging text (defaults to "Previous Page")
25645      * @type String
25646      */
25647     prevText : "Previous Page",
25648     /**
25649      * Customizable piece of the default paging text (defaults to "Next Page")
25650      * @type String
25651      */
25652     nextText : "Next Page",
25653     /**
25654      * Customizable piece of the default paging text (defaults to "Last Page")
25655      * @type String
25656      */
25657     lastText : "Last Page",
25658     /**
25659      * Customizable piece of the default paging text (defaults to "Refresh")
25660      * @type String
25661      */
25662     refreshText : "Refresh",
25663
25664     buttons : false,
25665     // private
25666     onRender : function(ct, position) 
25667     {
25668         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25669         this.navgroup.parentId = this.id;
25670         this.navgroup.onRender(this.el, null);
25671         // add the buttons to the navgroup
25672         
25673         if(this.displayInfo){
25674             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25675             this.displayEl = this.el.select('.x-paging-info', true).first();
25676 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25677 //            this.displayEl = navel.el.select('span',true).first();
25678         }
25679         
25680         var _this = this;
25681         
25682         if(this.buttons){
25683             Roo.each(_this.buttons, function(e){ // this might need to use render????
25684                Roo.factory(e).render(_this.el);
25685             });
25686         }
25687             
25688         Roo.each(_this.toolbarItems, function(e) {
25689             _this.navgroup.addItem(e);
25690         });
25691         
25692         
25693         this.first = this.navgroup.addItem({
25694             tooltip: this.firstText,
25695             cls: "prev btn-outline-secondary",
25696             html : ' <i class="fa fa-step-backward"></i>',
25697             disabled: true,
25698             preventDefault: true,
25699             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25700         });
25701         
25702         this.prev =  this.navgroup.addItem({
25703             tooltip: this.prevText,
25704             cls: "prev btn-outline-secondary",
25705             html : ' <i class="fa fa-backward"></i>',
25706             disabled: true,
25707             preventDefault: true,
25708             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25709         });
25710     //this.addSeparator();
25711         
25712         
25713         var field = this.navgroup.addItem( {
25714             tagtype : 'span',
25715             cls : 'x-paging-position  btn-outline-secondary',
25716              disabled: true,
25717             html : this.beforePageText  +
25718                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25719                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25720          } ); //?? escaped?
25721         
25722         this.field = field.el.select('input', true).first();
25723         this.field.on("keydown", this.onPagingKeydown, this);
25724         this.field.on("focus", function(){this.dom.select();});
25725     
25726     
25727         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25728         //this.field.setHeight(18);
25729         //this.addSeparator();
25730         this.next = this.navgroup.addItem({
25731             tooltip: this.nextText,
25732             cls: "next btn-outline-secondary",
25733             html : ' <i class="fa fa-forward"></i>',
25734             disabled: true,
25735             preventDefault: true,
25736             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25737         });
25738         this.last = this.navgroup.addItem({
25739             tooltip: this.lastText,
25740             html : ' <i class="fa fa-step-forward"></i>',
25741             cls: "next btn-outline-secondary",
25742             disabled: true,
25743             preventDefault: true,
25744             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25745         });
25746     //this.addSeparator();
25747         this.loading = this.navgroup.addItem({
25748             tooltip: this.refreshText,
25749             cls: "btn-outline-secondary",
25750             html : ' <i class="fa fa-refresh"></i>',
25751             preventDefault: true,
25752             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25753         });
25754         
25755     },
25756
25757     // private
25758     updateInfo : function(){
25759         if(this.displayEl){
25760             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25761             var msg = count == 0 ?
25762                 this.emptyMsg :
25763                 String.format(
25764                     this.displayMsg,
25765                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25766                 );
25767             this.displayEl.update(msg);
25768         }
25769     },
25770
25771     // private
25772     onLoad : function(ds, r, o)
25773     {
25774         this.cursor = o.params.start ? o.params.start : 0;
25775         
25776         var d = this.getPageData(),
25777             ap = d.activePage,
25778             ps = d.pages;
25779         
25780         
25781         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25782         this.field.dom.value = ap;
25783         this.first.setDisabled(ap == 1);
25784         this.prev.setDisabled(ap == 1);
25785         this.next.setDisabled(ap == ps);
25786         this.last.setDisabled(ap == ps);
25787         this.loading.enable();
25788         this.updateInfo();
25789     },
25790
25791     // private
25792     getPageData : function(){
25793         var total = this.ds.getTotalCount();
25794         return {
25795             total : total,
25796             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25797             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25798         };
25799     },
25800
25801     // private
25802     onLoadError : function(){
25803         this.loading.enable();
25804     },
25805
25806     // private
25807     onPagingKeydown : function(e){
25808         var k = e.getKey();
25809         var d = this.getPageData();
25810         if(k == e.RETURN){
25811             var v = this.field.dom.value, pageNum;
25812             if(!v || isNaN(pageNum = parseInt(v, 10))){
25813                 this.field.dom.value = d.activePage;
25814                 return;
25815             }
25816             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25817             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25818             e.stopEvent();
25819         }
25820         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))
25821         {
25822           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25823           this.field.dom.value = pageNum;
25824           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25825           e.stopEvent();
25826         }
25827         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25828         {
25829           var v = this.field.dom.value, pageNum; 
25830           var increment = (e.shiftKey) ? 10 : 1;
25831           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25832                 increment *= -1;
25833           }
25834           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25835             this.field.dom.value = d.activePage;
25836             return;
25837           }
25838           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25839           {
25840             this.field.dom.value = parseInt(v, 10) + increment;
25841             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25842             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25843           }
25844           e.stopEvent();
25845         }
25846     },
25847
25848     // private
25849     beforeLoad : function(){
25850         if(this.loading){
25851             this.loading.disable();
25852         }
25853     },
25854
25855     // private
25856     onClick : function(which){
25857         
25858         var ds = this.ds;
25859         if (!ds) {
25860             return;
25861         }
25862         
25863         switch(which){
25864             case "first":
25865                 ds.load({params:{start: 0, limit: this.pageSize}});
25866             break;
25867             case "prev":
25868                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25869             break;
25870             case "next":
25871                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25872             break;
25873             case "last":
25874                 var total = ds.getTotalCount();
25875                 var extra = total % this.pageSize;
25876                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25877                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25878             break;
25879             case "refresh":
25880                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25881             break;
25882         }
25883     },
25884
25885     /**
25886      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25887      * @param {Roo.data.Store} store The data store to unbind
25888      */
25889     unbind : function(ds){
25890         ds.un("beforeload", this.beforeLoad, this);
25891         ds.un("load", this.onLoad, this);
25892         ds.un("loadexception", this.onLoadError, this);
25893         ds.un("remove", this.updateInfo, this);
25894         ds.un("add", this.updateInfo, this);
25895         this.ds = undefined;
25896     },
25897
25898     /**
25899      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25900      * @param {Roo.data.Store} store The data store to bind
25901      */
25902     bind : function(ds){
25903         ds.on("beforeload", this.beforeLoad, this);
25904         ds.on("load", this.onLoad, this);
25905         ds.on("loadexception", this.onLoadError, this);
25906         ds.on("remove", this.updateInfo, this);
25907         ds.on("add", this.updateInfo, this);
25908         this.ds = ds;
25909     }
25910 });/*
25911  * - LGPL
25912  *
25913  * element
25914  * 
25915  */
25916
25917 /**
25918  * @class Roo.bootstrap.MessageBar
25919  * @extends Roo.bootstrap.Component
25920  * Bootstrap MessageBar class
25921  * @cfg {String} html contents of the MessageBar
25922  * @cfg {String} weight (info | success | warning | danger) default info
25923  * @cfg {String} beforeClass insert the bar before the given class
25924  * @cfg {Boolean} closable (true | false) default false
25925  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25926  * 
25927  * @constructor
25928  * Create a new Element
25929  * @param {Object} config The config object
25930  */
25931
25932 Roo.bootstrap.MessageBar = function(config){
25933     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25934 };
25935
25936 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25937     
25938     html: '',
25939     weight: 'info',
25940     closable: false,
25941     fixed: false,
25942     beforeClass: 'bootstrap-sticky-wrap',
25943     
25944     getAutoCreate : function(){
25945         
25946         var cfg = {
25947             tag: 'div',
25948             cls: 'alert alert-dismissable alert-' + this.weight,
25949             cn: [
25950                 {
25951                     tag: 'span',
25952                     cls: 'message',
25953                     html: this.html || ''
25954                 }
25955             ]
25956         };
25957         
25958         if(this.fixed){
25959             cfg.cls += ' alert-messages-fixed';
25960         }
25961         
25962         if(this.closable){
25963             cfg.cn.push({
25964                 tag: 'button',
25965                 cls: 'close',
25966                 html: 'x'
25967             });
25968         }
25969         
25970         return cfg;
25971     },
25972     
25973     onRender : function(ct, position)
25974     {
25975         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25976         
25977         if(!this.el){
25978             var cfg = Roo.apply({},  this.getAutoCreate());
25979             cfg.id = Roo.id();
25980             
25981             if (this.cls) {
25982                 cfg.cls += ' ' + this.cls;
25983             }
25984             if (this.style) {
25985                 cfg.style = this.style;
25986             }
25987             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25988             
25989             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25990         }
25991         
25992         this.el.select('>button.close').on('click', this.hide, this);
25993         
25994     },
25995     
25996     show : function()
25997     {
25998         if (!this.rendered) {
25999             this.render();
26000         }
26001         
26002         this.el.show();
26003         
26004         this.fireEvent('show', this);
26005         
26006     },
26007     
26008     hide : function()
26009     {
26010         if (!this.rendered) {
26011             this.render();
26012         }
26013         
26014         this.el.hide();
26015         
26016         this.fireEvent('hide', this);
26017     },
26018     
26019     update : function()
26020     {
26021 //        var e = this.el.dom.firstChild;
26022 //        
26023 //        if(this.closable){
26024 //            e = e.nextSibling;
26025 //        }
26026 //        
26027 //        e.data = this.html || '';
26028
26029         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
26030     }
26031    
26032 });
26033
26034  
26035
26036      /*
26037  * - LGPL
26038  *
26039  * Graph
26040  * 
26041  */
26042
26043
26044 /**
26045  * @class Roo.bootstrap.Graph
26046  * @extends Roo.bootstrap.Component
26047  * Bootstrap Graph class
26048 > Prameters
26049  -sm {number} sm 4
26050  -md {number} md 5
26051  @cfg {String} graphtype  bar | vbar | pie
26052  @cfg {number} g_x coodinator | centre x (pie)
26053  @cfg {number} g_y coodinator | centre y (pie)
26054  @cfg {number} g_r radius (pie)
26055  @cfg {number} g_height height of the chart (respected by all elements in the set)
26056  @cfg {number} g_width width of the chart (respected by all elements in the set)
26057  @cfg {Object} title The title of the chart
26058     
26059  -{Array}  values
26060  -opts (object) options for the chart 
26061      o {
26062      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
26063      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
26064      o vgutter (number)
26065      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.
26066      o stacked (boolean) whether or not to tread values as in a stacked bar chart
26067      o to
26068      o stretch (boolean)
26069      o }
26070  -opts (object) options for the pie
26071      o{
26072      o cut
26073      o startAngle (number)
26074      o endAngle (number)
26075      } 
26076  *
26077  * @constructor
26078  * Create a new Input
26079  * @param {Object} config The config object
26080  */
26081
26082 Roo.bootstrap.Graph = function(config){
26083     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
26084     
26085     this.addEvents({
26086         // img events
26087         /**
26088          * @event click
26089          * The img click event for the img.
26090          * @param {Roo.EventObject} e
26091          */
26092         "click" : true
26093     });
26094 };
26095
26096 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
26097     
26098     sm: 4,
26099     md: 5,
26100     graphtype: 'bar',
26101     g_height: 250,
26102     g_width: 400,
26103     g_x: 50,
26104     g_y: 50,
26105     g_r: 30,
26106     opts:{
26107         //g_colors: this.colors,
26108         g_type: 'soft',
26109         g_gutter: '20%'
26110
26111     },
26112     title : false,
26113
26114     getAutoCreate : function(){
26115         
26116         var cfg = {
26117             tag: 'div',
26118             html : null
26119         };
26120         
26121         
26122         return  cfg;
26123     },
26124
26125     onRender : function(ct,position){
26126         
26127         
26128         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
26129         
26130         if (typeof(Raphael) == 'undefined') {
26131             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
26132             return;
26133         }
26134         
26135         this.raphael = Raphael(this.el.dom);
26136         
26137                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26138                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26139                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
26140                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
26141                 /*
26142                 r.text(160, 10, "Single Series Chart").attr(txtattr);
26143                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
26144                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
26145                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
26146                 
26147                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
26148                 r.barchart(330, 10, 300, 220, data1);
26149                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
26150                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
26151                 */
26152                 
26153                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
26154                 // r.barchart(30, 30, 560, 250,  xdata, {
26155                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
26156                 //     axis : "0 0 1 1",
26157                 //     axisxlabels :  xdata
26158                 //     //yvalues : cols,
26159                    
26160                 // });
26161 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
26162 //        
26163 //        this.load(null,xdata,{
26164 //                axis : "0 0 1 1",
26165 //                axisxlabels :  xdata
26166 //                });
26167
26168     },
26169
26170     load : function(graphtype,xdata,opts)
26171     {
26172         this.raphael.clear();
26173         if(!graphtype) {
26174             graphtype = this.graphtype;
26175         }
26176         if(!opts){
26177             opts = this.opts;
26178         }
26179         var r = this.raphael,
26180             fin = function () {
26181                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
26182             },
26183             fout = function () {
26184                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
26185             },
26186             pfin = function() {
26187                 this.sector.stop();
26188                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
26189
26190                 if (this.label) {
26191                     this.label[0].stop();
26192                     this.label[0].attr({ r: 7.5 });
26193                     this.label[1].attr({ "font-weight": 800 });
26194                 }
26195             },
26196             pfout = function() {
26197                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
26198
26199                 if (this.label) {
26200                     this.label[0].animate({ r: 5 }, 500, "bounce");
26201                     this.label[1].attr({ "font-weight": 400 });
26202                 }
26203             };
26204
26205         switch(graphtype){
26206             case 'bar':
26207                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26208                 break;
26209             case 'hbar':
26210                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26211                 break;
26212             case 'pie':
26213 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
26214 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
26215 //            
26216                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
26217                 
26218                 break;
26219
26220         }
26221         
26222         if(this.title){
26223             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
26224         }
26225         
26226     },
26227     
26228     setTitle: function(o)
26229     {
26230         this.title = o;
26231     },
26232     
26233     initEvents: function() {
26234         
26235         if(!this.href){
26236             this.el.on('click', this.onClick, this);
26237         }
26238     },
26239     
26240     onClick : function(e)
26241     {
26242         Roo.log('img onclick');
26243         this.fireEvent('click', this, e);
26244     }
26245    
26246 });
26247
26248  
26249 /*
26250  * - LGPL
26251  *
26252  * numberBox
26253  * 
26254  */
26255 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26256
26257 /**
26258  * @class Roo.bootstrap.dash.NumberBox
26259  * @extends Roo.bootstrap.Component
26260  * Bootstrap NumberBox class
26261  * @cfg {String} headline Box headline
26262  * @cfg {String} content Box content
26263  * @cfg {String} icon Box icon
26264  * @cfg {String} footer Footer text
26265  * @cfg {String} fhref Footer href
26266  * 
26267  * @constructor
26268  * Create a new NumberBox
26269  * @param {Object} config The config object
26270  */
26271
26272
26273 Roo.bootstrap.dash.NumberBox = function(config){
26274     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
26275     
26276 };
26277
26278 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
26279     
26280     headline : '',
26281     content : '',
26282     icon : '',
26283     footer : '',
26284     fhref : '',
26285     ficon : '',
26286     
26287     getAutoCreate : function(){
26288         
26289         var cfg = {
26290             tag : 'div',
26291             cls : 'small-box ',
26292             cn : [
26293                 {
26294                     tag : 'div',
26295                     cls : 'inner',
26296                     cn :[
26297                         {
26298                             tag : 'h3',
26299                             cls : 'roo-headline',
26300                             html : this.headline
26301                         },
26302                         {
26303                             tag : 'p',
26304                             cls : 'roo-content',
26305                             html : this.content
26306                         }
26307                     ]
26308                 }
26309             ]
26310         };
26311         
26312         if(this.icon){
26313             cfg.cn.push({
26314                 tag : 'div',
26315                 cls : 'icon',
26316                 cn :[
26317                     {
26318                         tag : 'i',
26319                         cls : 'ion ' + this.icon
26320                     }
26321                 ]
26322             });
26323         }
26324         
26325         if(this.footer){
26326             var footer = {
26327                 tag : 'a',
26328                 cls : 'small-box-footer',
26329                 href : this.fhref || '#',
26330                 html : this.footer
26331             };
26332             
26333             cfg.cn.push(footer);
26334             
26335         }
26336         
26337         return  cfg;
26338     },
26339
26340     onRender : function(ct,position){
26341         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
26342
26343
26344        
26345                 
26346     },
26347
26348     setHeadline: function (value)
26349     {
26350         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
26351     },
26352     
26353     setFooter: function (value, href)
26354     {
26355         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
26356         
26357         if(href){
26358             this.el.select('a.small-box-footer',true).first().attr('href', href);
26359         }
26360         
26361     },
26362
26363     setContent: function (value)
26364     {
26365         this.el.select('.roo-content',true).first().dom.innerHTML = value;
26366     },
26367
26368     initEvents: function() 
26369     {   
26370         
26371     }
26372     
26373 });
26374
26375  
26376 /*
26377  * - LGPL
26378  *
26379  * TabBox
26380  * 
26381  */
26382 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26383
26384 /**
26385  * @class Roo.bootstrap.dash.TabBox
26386  * @extends Roo.bootstrap.Component
26387  * Bootstrap TabBox class
26388  * @cfg {String} title Title of the TabBox
26389  * @cfg {String} icon Icon of the TabBox
26390  * @cfg {Boolean} showtabs (true|false) show the tabs default true
26391  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
26392  * 
26393  * @constructor
26394  * Create a new TabBox
26395  * @param {Object} config The config object
26396  */
26397
26398
26399 Roo.bootstrap.dash.TabBox = function(config){
26400     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
26401     this.addEvents({
26402         // raw events
26403         /**
26404          * @event addpane
26405          * When a pane is added
26406          * @param {Roo.bootstrap.dash.TabPane} pane
26407          */
26408         "addpane" : true,
26409         /**
26410          * @event activatepane
26411          * When a pane is activated
26412          * @param {Roo.bootstrap.dash.TabPane} pane
26413          */
26414         "activatepane" : true
26415         
26416          
26417     });
26418     
26419     this.panes = [];
26420 };
26421
26422 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
26423
26424     title : '',
26425     icon : false,
26426     showtabs : true,
26427     tabScrollable : false,
26428     
26429     getChildContainer : function()
26430     {
26431         return this.el.select('.tab-content', true).first();
26432     },
26433     
26434     getAutoCreate : function(){
26435         
26436         var header = {
26437             tag: 'li',
26438             cls: 'pull-left header',
26439             html: this.title,
26440             cn : []
26441         };
26442         
26443         if(this.icon){
26444             header.cn.push({
26445                 tag: 'i',
26446                 cls: 'fa ' + this.icon
26447             });
26448         }
26449         
26450         var h = {
26451             tag: 'ul',
26452             cls: 'nav nav-tabs pull-right',
26453             cn: [
26454                 header
26455             ]
26456         };
26457         
26458         if(this.tabScrollable){
26459             h = {
26460                 tag: 'div',
26461                 cls: 'tab-header',
26462                 cn: [
26463                     {
26464                         tag: 'ul',
26465                         cls: 'nav nav-tabs pull-right',
26466                         cn: [
26467                             header
26468                         ]
26469                     }
26470                 ]
26471             };
26472         }
26473         
26474         var cfg = {
26475             tag: 'div',
26476             cls: 'nav-tabs-custom',
26477             cn: [
26478                 h,
26479                 {
26480                     tag: 'div',
26481                     cls: 'tab-content no-padding',
26482                     cn: []
26483                 }
26484             ]
26485         };
26486
26487         return  cfg;
26488     },
26489     initEvents : function()
26490     {
26491         //Roo.log('add add pane handler');
26492         this.on('addpane', this.onAddPane, this);
26493     },
26494      /**
26495      * Updates the box title
26496      * @param {String} html to set the title to.
26497      */
26498     setTitle : function(value)
26499     {
26500         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
26501     },
26502     onAddPane : function(pane)
26503     {
26504         this.panes.push(pane);
26505         //Roo.log('addpane');
26506         //Roo.log(pane);
26507         // tabs are rendere left to right..
26508         if(!this.showtabs){
26509             return;
26510         }
26511         
26512         var ctr = this.el.select('.nav-tabs', true).first();
26513          
26514          
26515         var existing = ctr.select('.nav-tab',true);
26516         var qty = existing.getCount();;
26517         
26518         
26519         var tab = ctr.createChild({
26520             tag : 'li',
26521             cls : 'nav-tab' + (qty ? '' : ' active'),
26522             cn : [
26523                 {
26524                     tag : 'a',
26525                     href:'#',
26526                     html : pane.title
26527                 }
26528             ]
26529         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
26530         pane.tab = tab;
26531         
26532         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
26533         if (!qty) {
26534             pane.el.addClass('active');
26535         }
26536         
26537                 
26538     },
26539     onTabClick : function(ev,un,ob,pane)
26540     {
26541         //Roo.log('tab - prev default');
26542         ev.preventDefault();
26543         
26544         
26545         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26546         pane.tab.addClass('active');
26547         //Roo.log(pane.title);
26548         this.getChildContainer().select('.tab-pane',true).removeClass('active');
26549         // technically we should have a deactivate event.. but maybe add later.
26550         // and it should not de-activate the selected tab...
26551         this.fireEvent('activatepane', pane);
26552         pane.el.addClass('active');
26553         pane.fireEvent('activate');
26554         
26555         
26556     },
26557     
26558     getActivePane : function()
26559     {
26560         var r = false;
26561         Roo.each(this.panes, function(p) {
26562             if(p.el.hasClass('active')){
26563                 r = p;
26564                 return false;
26565             }
26566             
26567             return;
26568         });
26569         
26570         return r;
26571     }
26572     
26573     
26574 });
26575
26576  
26577 /*
26578  * - LGPL
26579  *
26580  * Tab pane
26581  * 
26582  */
26583 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26584 /**
26585  * @class Roo.bootstrap.TabPane
26586  * @extends Roo.bootstrap.Component
26587  * Bootstrap TabPane class
26588  * @cfg {Boolean} active (false | true) Default false
26589  * @cfg {String} title title of panel
26590
26591  * 
26592  * @constructor
26593  * Create a new TabPane
26594  * @param {Object} config The config object
26595  */
26596
26597 Roo.bootstrap.dash.TabPane = function(config){
26598     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
26599     
26600     this.addEvents({
26601         // raw events
26602         /**
26603          * @event activate
26604          * When a pane is activated
26605          * @param {Roo.bootstrap.dash.TabPane} pane
26606          */
26607         "activate" : true
26608          
26609     });
26610 };
26611
26612 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
26613     
26614     active : false,
26615     title : '',
26616     
26617     // the tabBox that this is attached to.
26618     tab : false,
26619      
26620     getAutoCreate : function() 
26621     {
26622         var cfg = {
26623             tag: 'div',
26624             cls: 'tab-pane'
26625         };
26626         
26627         if(this.active){
26628             cfg.cls += ' active';
26629         }
26630         
26631         return cfg;
26632     },
26633     initEvents  : function()
26634     {
26635         //Roo.log('trigger add pane handler');
26636         this.parent().fireEvent('addpane', this)
26637     },
26638     
26639      /**
26640      * Updates the tab title 
26641      * @param {String} html to set the title to.
26642      */
26643     setTitle: function(str)
26644     {
26645         if (!this.tab) {
26646             return;
26647         }
26648         this.title = str;
26649         this.tab.select('a', true).first().dom.innerHTML = str;
26650         
26651     }
26652     
26653     
26654     
26655 });
26656
26657  
26658
26659
26660  /*
26661  * - LGPL
26662  *
26663  * menu
26664  * 
26665  */
26666 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26667
26668 /**
26669  * @class Roo.bootstrap.menu.Menu
26670  * @extends Roo.bootstrap.Component
26671  * Bootstrap Menu class - container for Menu
26672  * @cfg {String} html Text of the menu
26673  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26674  * @cfg {String} icon Font awesome icon
26675  * @cfg {String} pos Menu align to (top | bottom) default bottom
26676  * 
26677  * 
26678  * @constructor
26679  * Create a new Menu
26680  * @param {Object} config The config object
26681  */
26682
26683
26684 Roo.bootstrap.menu.Menu = function(config){
26685     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26686     
26687     this.addEvents({
26688         /**
26689          * @event beforeshow
26690          * Fires before this menu is displayed
26691          * @param {Roo.bootstrap.menu.Menu} this
26692          */
26693         beforeshow : true,
26694         /**
26695          * @event beforehide
26696          * Fires before this menu is hidden
26697          * @param {Roo.bootstrap.menu.Menu} this
26698          */
26699         beforehide : true,
26700         /**
26701          * @event show
26702          * Fires after this menu is displayed
26703          * @param {Roo.bootstrap.menu.Menu} this
26704          */
26705         show : true,
26706         /**
26707          * @event hide
26708          * Fires after this menu is hidden
26709          * @param {Roo.bootstrap.menu.Menu} this
26710          */
26711         hide : true,
26712         /**
26713          * @event click
26714          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26715          * @param {Roo.bootstrap.menu.Menu} this
26716          * @param {Roo.EventObject} e
26717          */
26718         click : true
26719     });
26720     
26721 };
26722
26723 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26724     
26725     submenu : false,
26726     html : '',
26727     weight : 'default',
26728     icon : false,
26729     pos : 'bottom',
26730     
26731     
26732     getChildContainer : function() {
26733         if(this.isSubMenu){
26734             return this.el;
26735         }
26736         
26737         return this.el.select('ul.dropdown-menu', true).first();  
26738     },
26739     
26740     getAutoCreate : function()
26741     {
26742         var text = [
26743             {
26744                 tag : 'span',
26745                 cls : 'roo-menu-text',
26746                 html : this.html
26747             }
26748         ];
26749         
26750         if(this.icon){
26751             text.unshift({
26752                 tag : 'i',
26753                 cls : 'fa ' + this.icon
26754             })
26755         }
26756         
26757         
26758         var cfg = {
26759             tag : 'div',
26760             cls : 'btn-group',
26761             cn : [
26762                 {
26763                     tag : 'button',
26764                     cls : 'dropdown-button btn btn-' + this.weight,
26765                     cn : text
26766                 },
26767                 {
26768                     tag : 'button',
26769                     cls : 'dropdown-toggle btn btn-' + this.weight,
26770                     cn : [
26771                         {
26772                             tag : 'span',
26773                             cls : 'caret'
26774                         }
26775                     ]
26776                 },
26777                 {
26778                     tag : 'ul',
26779                     cls : 'dropdown-menu'
26780                 }
26781             ]
26782             
26783         };
26784         
26785         if(this.pos == 'top'){
26786             cfg.cls += ' dropup';
26787         }
26788         
26789         if(this.isSubMenu){
26790             cfg = {
26791                 tag : 'ul',
26792                 cls : 'dropdown-menu'
26793             }
26794         }
26795         
26796         return cfg;
26797     },
26798     
26799     onRender : function(ct, position)
26800     {
26801         this.isSubMenu = ct.hasClass('dropdown-submenu');
26802         
26803         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26804     },
26805     
26806     initEvents : function() 
26807     {
26808         if(this.isSubMenu){
26809             return;
26810         }
26811         
26812         this.hidden = true;
26813         
26814         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26815         this.triggerEl.on('click', this.onTriggerPress, this);
26816         
26817         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26818         this.buttonEl.on('click', this.onClick, this);
26819         
26820     },
26821     
26822     list : function()
26823     {
26824         if(this.isSubMenu){
26825             return this.el;
26826         }
26827         
26828         return this.el.select('ul.dropdown-menu', true).first();
26829     },
26830     
26831     onClick : function(e)
26832     {
26833         this.fireEvent("click", this, e);
26834     },
26835     
26836     onTriggerPress  : function(e)
26837     {   
26838         if (this.isVisible()) {
26839             this.hide();
26840         } else {
26841             this.show();
26842         }
26843     },
26844     
26845     isVisible : function(){
26846         return !this.hidden;
26847     },
26848     
26849     show : function()
26850     {
26851         this.fireEvent("beforeshow", this);
26852         
26853         this.hidden = false;
26854         this.el.addClass('open');
26855         
26856         Roo.get(document).on("mouseup", this.onMouseUp, this);
26857         
26858         this.fireEvent("show", this);
26859         
26860         
26861     },
26862     
26863     hide : function()
26864     {
26865         this.fireEvent("beforehide", this);
26866         
26867         this.hidden = true;
26868         this.el.removeClass('open');
26869         
26870         Roo.get(document).un("mouseup", this.onMouseUp);
26871         
26872         this.fireEvent("hide", this);
26873     },
26874     
26875     onMouseUp : function()
26876     {
26877         this.hide();
26878     }
26879     
26880 });
26881
26882  
26883  /*
26884  * - LGPL
26885  *
26886  * menu item
26887  * 
26888  */
26889 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26890
26891 /**
26892  * @class Roo.bootstrap.menu.Item
26893  * @extends Roo.bootstrap.Component
26894  * Bootstrap MenuItem class
26895  * @cfg {Boolean} submenu (true | false) default false
26896  * @cfg {String} html text of the item
26897  * @cfg {String} href the link
26898  * @cfg {Boolean} disable (true | false) default false
26899  * @cfg {Boolean} preventDefault (true | false) default true
26900  * @cfg {String} icon Font awesome icon
26901  * @cfg {String} pos Submenu align to (left | right) default right 
26902  * 
26903  * 
26904  * @constructor
26905  * Create a new Item
26906  * @param {Object} config The config object
26907  */
26908
26909
26910 Roo.bootstrap.menu.Item = function(config){
26911     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26912     this.addEvents({
26913         /**
26914          * @event mouseover
26915          * Fires when the mouse is hovering over this menu
26916          * @param {Roo.bootstrap.menu.Item} this
26917          * @param {Roo.EventObject} e
26918          */
26919         mouseover : true,
26920         /**
26921          * @event mouseout
26922          * Fires when the mouse exits this menu
26923          * @param {Roo.bootstrap.menu.Item} this
26924          * @param {Roo.EventObject} e
26925          */
26926         mouseout : true,
26927         // raw events
26928         /**
26929          * @event click
26930          * The raw click event for the entire grid.
26931          * @param {Roo.EventObject} e
26932          */
26933         click : true
26934     });
26935 };
26936
26937 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26938     
26939     submenu : false,
26940     href : '',
26941     html : '',
26942     preventDefault: true,
26943     disable : false,
26944     icon : false,
26945     pos : 'right',
26946     
26947     getAutoCreate : function()
26948     {
26949         var text = [
26950             {
26951                 tag : 'span',
26952                 cls : 'roo-menu-item-text',
26953                 html : this.html
26954             }
26955         ];
26956         
26957         if(this.icon){
26958             text.unshift({
26959                 tag : 'i',
26960                 cls : 'fa ' + this.icon
26961             })
26962         }
26963         
26964         var cfg = {
26965             tag : 'li',
26966             cn : [
26967                 {
26968                     tag : 'a',
26969                     href : this.href || '#',
26970                     cn : text
26971                 }
26972             ]
26973         };
26974         
26975         if(this.disable){
26976             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26977         }
26978         
26979         if(this.submenu){
26980             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26981             
26982             if(this.pos == 'left'){
26983                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26984             }
26985         }
26986         
26987         return cfg;
26988     },
26989     
26990     initEvents : function() 
26991     {
26992         this.el.on('mouseover', this.onMouseOver, this);
26993         this.el.on('mouseout', this.onMouseOut, this);
26994         
26995         this.el.select('a', true).first().on('click', this.onClick, this);
26996         
26997     },
26998     
26999     onClick : function(e)
27000     {
27001         if(this.preventDefault){
27002             e.preventDefault();
27003         }
27004         
27005         this.fireEvent("click", this, e);
27006     },
27007     
27008     onMouseOver : function(e)
27009     {
27010         if(this.submenu && this.pos == 'left'){
27011             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
27012         }
27013         
27014         this.fireEvent("mouseover", this, e);
27015     },
27016     
27017     onMouseOut : function(e)
27018     {
27019         this.fireEvent("mouseout", this, e);
27020     }
27021 });
27022
27023  
27024
27025  /*
27026  * - LGPL
27027  *
27028  * menu separator
27029  * 
27030  */
27031 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27032
27033 /**
27034  * @class Roo.bootstrap.menu.Separator
27035  * @extends Roo.bootstrap.Component
27036  * Bootstrap Separator class
27037  * 
27038  * @constructor
27039  * Create a new Separator
27040  * @param {Object} config The config object
27041  */
27042
27043
27044 Roo.bootstrap.menu.Separator = function(config){
27045     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
27046 };
27047
27048 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
27049     
27050     getAutoCreate : function(){
27051         var cfg = {
27052             tag : 'li',
27053             cls: 'divider'
27054         };
27055         
27056         return cfg;
27057     }
27058    
27059 });
27060
27061  
27062
27063  /*
27064  * - LGPL
27065  *
27066  * Tooltip
27067  * 
27068  */
27069
27070 /**
27071  * @class Roo.bootstrap.Tooltip
27072  * Bootstrap Tooltip class
27073  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
27074  * to determine which dom element triggers the tooltip.
27075  * 
27076  * It needs to add support for additional attributes like tooltip-position
27077  * 
27078  * @constructor
27079  * Create a new Toolti
27080  * @param {Object} config The config object
27081  */
27082
27083 Roo.bootstrap.Tooltip = function(config){
27084     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
27085     
27086     this.alignment = Roo.bootstrap.Tooltip.alignment;
27087     
27088     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
27089         this.alignment = config.alignment;
27090     }
27091     
27092 };
27093
27094 Roo.apply(Roo.bootstrap.Tooltip, {
27095     /**
27096      * @function init initialize tooltip monitoring.
27097      * @static
27098      */
27099     currentEl : false,
27100     currentTip : false,
27101     currentRegion : false,
27102     
27103     //  init : delay?
27104     
27105     init : function()
27106     {
27107         Roo.get(document).on('mouseover', this.enter ,this);
27108         Roo.get(document).on('mouseout', this.leave, this);
27109          
27110         
27111         this.currentTip = new Roo.bootstrap.Tooltip();
27112     },
27113     
27114     enter : function(ev)
27115     {
27116         var dom = ev.getTarget();
27117         
27118         //Roo.log(['enter',dom]);
27119         var el = Roo.fly(dom);
27120         if (this.currentEl) {
27121             //Roo.log(dom);
27122             //Roo.log(this.currentEl);
27123             //Roo.log(this.currentEl.contains(dom));
27124             if (this.currentEl == el) {
27125                 return;
27126             }
27127             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
27128                 return;
27129             }
27130
27131         }
27132         
27133         if (this.currentTip.el) {
27134             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
27135         }    
27136         //Roo.log(ev);
27137         
27138         if(!el || el.dom == document){
27139             return;
27140         }
27141         
27142         var bindEl = el;
27143         
27144         // you can not look for children, as if el is the body.. then everythign is the child..
27145         if (!el.attr('tooltip')) { //
27146             if (!el.select("[tooltip]").elements.length) {
27147                 return;
27148             }
27149             // is the mouse over this child...?
27150             bindEl = el.select("[tooltip]").first();
27151             var xy = ev.getXY();
27152             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
27153                 //Roo.log("not in region.");
27154                 return;
27155             }
27156             //Roo.log("child element over..");
27157             
27158         }
27159         this.currentEl = bindEl;
27160         this.currentTip.bind(bindEl);
27161         this.currentRegion = Roo.lib.Region.getRegion(dom);
27162         this.currentTip.enter();
27163         
27164     },
27165     leave : function(ev)
27166     {
27167         var dom = ev.getTarget();
27168         //Roo.log(['leave',dom]);
27169         if (!this.currentEl) {
27170             return;
27171         }
27172         
27173         
27174         if (dom != this.currentEl.dom) {
27175             return;
27176         }
27177         var xy = ev.getXY();
27178         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
27179             return;
27180         }
27181         // only activate leave if mouse cursor is outside... bounding box..
27182         
27183         
27184         
27185         
27186         if (this.currentTip) {
27187             this.currentTip.leave();
27188         }
27189         //Roo.log('clear currentEl');
27190         this.currentEl = false;
27191         
27192         
27193     },
27194     alignment : {
27195         'left' : ['r-l', [-2,0], 'right'],
27196         'right' : ['l-r', [2,0], 'left'],
27197         'bottom' : ['t-b', [0,2], 'top'],
27198         'top' : [ 'b-t', [0,-2], 'bottom']
27199     }
27200     
27201 });
27202
27203
27204 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
27205     
27206     
27207     bindEl : false,
27208     
27209     delay : null, // can be { show : 300 , hide: 500}
27210     
27211     timeout : null,
27212     
27213     hoverState : null, //???
27214     
27215     placement : 'bottom', 
27216     
27217     alignment : false,
27218     
27219     getAutoCreate : function(){
27220     
27221         var cfg = {
27222            cls : 'tooltip',
27223            role : 'tooltip',
27224            cn : [
27225                 {
27226                     cls : 'tooltip-arrow'
27227                 },
27228                 {
27229                     cls : 'tooltip-inner'
27230                 }
27231            ]
27232         };
27233         
27234         return cfg;
27235     },
27236     bind : function(el)
27237     {
27238         this.bindEl = el;
27239     },
27240       
27241     
27242     enter : function () {
27243        
27244         if (this.timeout != null) {
27245             clearTimeout(this.timeout);
27246         }
27247         
27248         this.hoverState = 'in';
27249          //Roo.log("enter - show");
27250         if (!this.delay || !this.delay.show) {
27251             this.show();
27252             return;
27253         }
27254         var _t = this;
27255         this.timeout = setTimeout(function () {
27256             if (_t.hoverState == 'in') {
27257                 _t.show();
27258             }
27259         }, this.delay.show);
27260     },
27261     leave : function()
27262     {
27263         clearTimeout(this.timeout);
27264     
27265         this.hoverState = 'out';
27266          if (!this.delay || !this.delay.hide) {
27267             this.hide();
27268             return;
27269         }
27270        
27271         var _t = this;
27272         this.timeout = setTimeout(function () {
27273             //Roo.log("leave - timeout");
27274             
27275             if (_t.hoverState == 'out') {
27276                 _t.hide();
27277                 Roo.bootstrap.Tooltip.currentEl = false;
27278             }
27279         }, delay);
27280     },
27281     
27282     show : function (msg)
27283     {
27284         if (!this.el) {
27285             this.render(document.body);
27286         }
27287         // set content.
27288         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
27289         
27290         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
27291         
27292         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
27293         
27294         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
27295         
27296         var placement = typeof this.placement == 'function' ?
27297             this.placement.call(this, this.el, on_el) :
27298             this.placement;
27299             
27300         var autoToken = /\s?auto?\s?/i;
27301         var autoPlace = autoToken.test(placement);
27302         if (autoPlace) {
27303             placement = placement.replace(autoToken, '') || 'top';
27304         }
27305         
27306         //this.el.detach()
27307         //this.el.setXY([0,0]);
27308         this.el.show();
27309         //this.el.dom.style.display='block';
27310         
27311         //this.el.appendTo(on_el);
27312         
27313         var p = this.getPosition();
27314         var box = this.el.getBox();
27315         
27316         if (autoPlace) {
27317             // fixme..
27318         }
27319         
27320         var align = this.alignment[placement];
27321         
27322         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
27323         
27324         if(placement == 'top' || placement == 'bottom'){
27325             if(xy[0] < 0){
27326                 placement = 'right';
27327             }
27328             
27329             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
27330                 placement = 'left';
27331             }
27332             
27333             var scroll = Roo.select('body', true).first().getScroll();
27334             
27335             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
27336                 placement = 'top';
27337             }
27338             
27339             align = this.alignment[placement];
27340         }
27341         
27342         this.el.alignTo(this.bindEl, align[0],align[1]);
27343         //var arrow = this.el.select('.arrow',true).first();
27344         //arrow.set(align[2], 
27345         
27346         this.el.addClass(placement);
27347         
27348         this.el.addClass('in fade');
27349         
27350         this.hoverState = null;
27351         
27352         if (this.el.hasClass('fade')) {
27353             // fade it?
27354         }
27355         
27356     },
27357     hide : function()
27358     {
27359          
27360         if (!this.el) {
27361             return;
27362         }
27363         //this.el.setXY([0,0]);
27364         this.el.removeClass('in');
27365         //this.el.hide();
27366         
27367     }
27368     
27369 });
27370  
27371
27372  /*
27373  * - LGPL
27374  *
27375  * Location Picker
27376  * 
27377  */
27378
27379 /**
27380  * @class Roo.bootstrap.LocationPicker
27381  * @extends Roo.bootstrap.Component
27382  * Bootstrap LocationPicker class
27383  * @cfg {Number} latitude Position when init default 0
27384  * @cfg {Number} longitude Position when init default 0
27385  * @cfg {Number} zoom default 15
27386  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
27387  * @cfg {Boolean} mapTypeControl default false
27388  * @cfg {Boolean} disableDoubleClickZoom default false
27389  * @cfg {Boolean} scrollwheel default true
27390  * @cfg {Boolean} streetViewControl default false
27391  * @cfg {Number} radius default 0
27392  * @cfg {String} locationName
27393  * @cfg {Boolean} draggable default true
27394  * @cfg {Boolean} enableAutocomplete default false
27395  * @cfg {Boolean} enableReverseGeocode default true
27396  * @cfg {String} markerTitle
27397  * 
27398  * @constructor
27399  * Create a new LocationPicker
27400  * @param {Object} config The config object
27401  */
27402
27403
27404 Roo.bootstrap.LocationPicker = function(config){
27405     
27406     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
27407     
27408     this.addEvents({
27409         /**
27410          * @event initial
27411          * Fires when the picker initialized.
27412          * @param {Roo.bootstrap.LocationPicker} this
27413          * @param {Google Location} location
27414          */
27415         initial : true,
27416         /**
27417          * @event positionchanged
27418          * Fires when the picker position changed.
27419          * @param {Roo.bootstrap.LocationPicker} this
27420          * @param {Google Location} location
27421          */
27422         positionchanged : true,
27423         /**
27424          * @event resize
27425          * Fires when the map resize.
27426          * @param {Roo.bootstrap.LocationPicker} this
27427          */
27428         resize : true,
27429         /**
27430          * @event show
27431          * Fires when the map show.
27432          * @param {Roo.bootstrap.LocationPicker} this
27433          */
27434         show : true,
27435         /**
27436          * @event hide
27437          * Fires when the map hide.
27438          * @param {Roo.bootstrap.LocationPicker} this
27439          */
27440         hide : true,
27441         /**
27442          * @event mapClick
27443          * Fires when click the map.
27444          * @param {Roo.bootstrap.LocationPicker} this
27445          * @param {Map event} e
27446          */
27447         mapClick : true,
27448         /**
27449          * @event mapRightClick
27450          * Fires when right click the map.
27451          * @param {Roo.bootstrap.LocationPicker} this
27452          * @param {Map event} e
27453          */
27454         mapRightClick : true,
27455         /**
27456          * @event markerClick
27457          * Fires when click the marker.
27458          * @param {Roo.bootstrap.LocationPicker} this
27459          * @param {Map event} e
27460          */
27461         markerClick : true,
27462         /**
27463          * @event markerRightClick
27464          * Fires when right click the marker.
27465          * @param {Roo.bootstrap.LocationPicker} this
27466          * @param {Map event} e
27467          */
27468         markerRightClick : true,
27469         /**
27470          * @event OverlayViewDraw
27471          * Fires when OverlayView Draw
27472          * @param {Roo.bootstrap.LocationPicker} this
27473          */
27474         OverlayViewDraw : true,
27475         /**
27476          * @event OverlayViewOnAdd
27477          * Fires when OverlayView Draw
27478          * @param {Roo.bootstrap.LocationPicker} this
27479          */
27480         OverlayViewOnAdd : true,
27481         /**
27482          * @event OverlayViewOnRemove
27483          * Fires when OverlayView Draw
27484          * @param {Roo.bootstrap.LocationPicker} this
27485          */
27486         OverlayViewOnRemove : true,
27487         /**
27488          * @event OverlayViewShow
27489          * Fires when OverlayView Draw
27490          * @param {Roo.bootstrap.LocationPicker} this
27491          * @param {Pixel} cpx
27492          */
27493         OverlayViewShow : true,
27494         /**
27495          * @event OverlayViewHide
27496          * Fires when OverlayView Draw
27497          * @param {Roo.bootstrap.LocationPicker} this
27498          */
27499         OverlayViewHide : true,
27500         /**
27501          * @event loadexception
27502          * Fires when load google lib failed.
27503          * @param {Roo.bootstrap.LocationPicker} this
27504          */
27505         loadexception : true
27506     });
27507         
27508 };
27509
27510 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
27511     
27512     gMapContext: false,
27513     
27514     latitude: 0,
27515     longitude: 0,
27516     zoom: 15,
27517     mapTypeId: false,
27518     mapTypeControl: false,
27519     disableDoubleClickZoom: false,
27520     scrollwheel: true,
27521     streetViewControl: false,
27522     radius: 0,
27523     locationName: '',
27524     draggable: true,
27525     enableAutocomplete: false,
27526     enableReverseGeocode: true,
27527     markerTitle: '',
27528     
27529     getAutoCreate: function()
27530     {
27531
27532         var cfg = {
27533             tag: 'div',
27534             cls: 'roo-location-picker'
27535         };
27536         
27537         return cfg
27538     },
27539     
27540     initEvents: function(ct, position)
27541     {       
27542         if(!this.el.getWidth() || this.isApplied()){
27543             return;
27544         }
27545         
27546         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27547         
27548         this.initial();
27549     },
27550     
27551     initial: function()
27552     {
27553         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27554             this.fireEvent('loadexception', this);
27555             return;
27556         }
27557         
27558         if(!this.mapTypeId){
27559             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
27560         }
27561         
27562         this.gMapContext = this.GMapContext();
27563         
27564         this.initOverlayView();
27565         
27566         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
27567         
27568         var _this = this;
27569                 
27570         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
27571             _this.setPosition(_this.gMapContext.marker.position);
27572         });
27573         
27574         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
27575             _this.fireEvent('mapClick', this, event);
27576             
27577         });
27578
27579         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
27580             _this.fireEvent('mapRightClick', this, event);
27581             
27582         });
27583         
27584         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27585             _this.fireEvent('markerClick', this, event);
27586             
27587         });
27588
27589         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27590             _this.fireEvent('markerRightClick', this, event);
27591             
27592         });
27593         
27594         this.setPosition(this.gMapContext.location);
27595         
27596         this.fireEvent('initial', this, this.gMapContext.location);
27597     },
27598     
27599     initOverlayView: function()
27600     {
27601         var _this = this;
27602         
27603         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27604             
27605             draw: function()
27606             {
27607                 _this.fireEvent('OverlayViewDraw', _this);
27608             },
27609             
27610             onAdd: function()
27611             {
27612                 _this.fireEvent('OverlayViewOnAdd', _this);
27613             },
27614             
27615             onRemove: function()
27616             {
27617                 _this.fireEvent('OverlayViewOnRemove', _this);
27618             },
27619             
27620             show: function(cpx)
27621             {
27622                 _this.fireEvent('OverlayViewShow', _this, cpx);
27623             },
27624             
27625             hide: function()
27626             {
27627                 _this.fireEvent('OverlayViewHide', _this);
27628             }
27629             
27630         });
27631     },
27632     
27633     fromLatLngToContainerPixel: function(event)
27634     {
27635         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27636     },
27637     
27638     isApplied: function() 
27639     {
27640         return this.getGmapContext() == false ? false : true;
27641     },
27642     
27643     getGmapContext: function() 
27644     {
27645         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27646     },
27647     
27648     GMapContext: function() 
27649     {
27650         var position = new google.maps.LatLng(this.latitude, this.longitude);
27651         
27652         var _map = new google.maps.Map(this.el.dom, {
27653             center: position,
27654             zoom: this.zoom,
27655             mapTypeId: this.mapTypeId,
27656             mapTypeControl: this.mapTypeControl,
27657             disableDoubleClickZoom: this.disableDoubleClickZoom,
27658             scrollwheel: this.scrollwheel,
27659             streetViewControl: this.streetViewControl,
27660             locationName: this.locationName,
27661             draggable: this.draggable,
27662             enableAutocomplete: this.enableAutocomplete,
27663             enableReverseGeocode: this.enableReverseGeocode
27664         });
27665         
27666         var _marker = new google.maps.Marker({
27667             position: position,
27668             map: _map,
27669             title: this.markerTitle,
27670             draggable: this.draggable
27671         });
27672         
27673         return {
27674             map: _map,
27675             marker: _marker,
27676             circle: null,
27677             location: position,
27678             radius: this.radius,
27679             locationName: this.locationName,
27680             addressComponents: {
27681                 formatted_address: null,
27682                 addressLine1: null,
27683                 addressLine2: null,
27684                 streetName: null,
27685                 streetNumber: null,
27686                 city: null,
27687                 district: null,
27688                 state: null,
27689                 stateOrProvince: null
27690             },
27691             settings: this,
27692             domContainer: this.el.dom,
27693             geodecoder: new google.maps.Geocoder()
27694         };
27695     },
27696     
27697     drawCircle: function(center, radius, options) 
27698     {
27699         if (this.gMapContext.circle != null) {
27700             this.gMapContext.circle.setMap(null);
27701         }
27702         if (radius > 0) {
27703             radius *= 1;
27704             options = Roo.apply({}, options, {
27705                 strokeColor: "#0000FF",
27706                 strokeOpacity: .35,
27707                 strokeWeight: 2,
27708                 fillColor: "#0000FF",
27709                 fillOpacity: .2
27710             });
27711             
27712             options.map = this.gMapContext.map;
27713             options.radius = radius;
27714             options.center = center;
27715             this.gMapContext.circle = new google.maps.Circle(options);
27716             return this.gMapContext.circle;
27717         }
27718         
27719         return null;
27720     },
27721     
27722     setPosition: function(location) 
27723     {
27724         this.gMapContext.location = location;
27725         this.gMapContext.marker.setPosition(location);
27726         this.gMapContext.map.panTo(location);
27727         this.drawCircle(location, this.gMapContext.radius, {});
27728         
27729         var _this = this;
27730         
27731         if (this.gMapContext.settings.enableReverseGeocode) {
27732             this.gMapContext.geodecoder.geocode({
27733                 latLng: this.gMapContext.location
27734             }, function(results, status) {
27735                 
27736                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27737                     _this.gMapContext.locationName = results[0].formatted_address;
27738                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27739                     
27740                     _this.fireEvent('positionchanged', this, location);
27741                 }
27742             });
27743             
27744             return;
27745         }
27746         
27747         this.fireEvent('positionchanged', this, location);
27748     },
27749     
27750     resize: function()
27751     {
27752         google.maps.event.trigger(this.gMapContext.map, "resize");
27753         
27754         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27755         
27756         this.fireEvent('resize', this);
27757     },
27758     
27759     setPositionByLatLng: function(latitude, longitude)
27760     {
27761         this.setPosition(new google.maps.LatLng(latitude, longitude));
27762     },
27763     
27764     getCurrentPosition: function() 
27765     {
27766         return {
27767             latitude: this.gMapContext.location.lat(),
27768             longitude: this.gMapContext.location.lng()
27769         };
27770     },
27771     
27772     getAddressName: function() 
27773     {
27774         return this.gMapContext.locationName;
27775     },
27776     
27777     getAddressComponents: function() 
27778     {
27779         return this.gMapContext.addressComponents;
27780     },
27781     
27782     address_component_from_google_geocode: function(address_components) 
27783     {
27784         var result = {};
27785         
27786         for (var i = 0; i < address_components.length; i++) {
27787             var component = address_components[i];
27788             if (component.types.indexOf("postal_code") >= 0) {
27789                 result.postalCode = component.short_name;
27790             } else if (component.types.indexOf("street_number") >= 0) {
27791                 result.streetNumber = component.short_name;
27792             } else if (component.types.indexOf("route") >= 0) {
27793                 result.streetName = component.short_name;
27794             } else if (component.types.indexOf("neighborhood") >= 0) {
27795                 result.city = component.short_name;
27796             } else if (component.types.indexOf("locality") >= 0) {
27797                 result.city = component.short_name;
27798             } else if (component.types.indexOf("sublocality") >= 0) {
27799                 result.district = component.short_name;
27800             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27801                 result.stateOrProvince = component.short_name;
27802             } else if (component.types.indexOf("country") >= 0) {
27803                 result.country = component.short_name;
27804             }
27805         }
27806         
27807         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27808         result.addressLine2 = "";
27809         return result;
27810     },
27811     
27812     setZoomLevel: function(zoom)
27813     {
27814         this.gMapContext.map.setZoom(zoom);
27815     },
27816     
27817     show: function()
27818     {
27819         if(!this.el){
27820             return;
27821         }
27822         
27823         this.el.show();
27824         
27825         this.resize();
27826         
27827         this.fireEvent('show', this);
27828     },
27829     
27830     hide: function()
27831     {
27832         if(!this.el){
27833             return;
27834         }
27835         
27836         this.el.hide();
27837         
27838         this.fireEvent('hide', this);
27839     }
27840     
27841 });
27842
27843 Roo.apply(Roo.bootstrap.LocationPicker, {
27844     
27845     OverlayView : function(map, options)
27846     {
27847         options = options || {};
27848         
27849         this.setMap(map);
27850     }
27851     
27852     
27853 });/**
27854  * @class Roo.bootstrap.Alert
27855  * @extends Roo.bootstrap.Component
27856  * Bootstrap Alert class - shows an alert area box
27857  * eg
27858  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27859   Enter a valid email address
27860 </div>
27861  * @licence LGPL
27862  * @cfg {String} title The title of alert
27863  * @cfg {String} html The content of alert
27864  * @cfg {String} weight (  success | info | warning | danger )
27865  * @cfg {String} faicon font-awesomeicon
27866  * 
27867  * @constructor
27868  * Create a new alert
27869  * @param {Object} config The config object
27870  */
27871
27872
27873 Roo.bootstrap.Alert = function(config){
27874     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27875     
27876 };
27877
27878 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27879     
27880     title: '',
27881     html: '',
27882     weight: false,
27883     faicon: false,
27884     
27885     getAutoCreate : function()
27886     {
27887         
27888         var cfg = {
27889             tag : 'div',
27890             cls : 'alert',
27891             cn : [
27892                 {
27893                     tag : 'i',
27894                     cls : 'roo-alert-icon'
27895                     
27896                 },
27897                 {
27898                     tag : 'b',
27899                     cls : 'roo-alert-title',
27900                     html : this.title
27901                 },
27902                 {
27903                     tag : 'span',
27904                     cls : 'roo-alert-text',
27905                     html : this.html
27906                 }
27907             ]
27908         };
27909         
27910         if(this.faicon){
27911             cfg.cn[0].cls += ' fa ' + this.faicon;
27912         }
27913         
27914         if(this.weight){
27915             cfg.cls += ' alert-' + this.weight;
27916         }
27917         
27918         return cfg;
27919     },
27920     
27921     initEvents: function() 
27922     {
27923         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27924     },
27925     
27926     setTitle : function(str)
27927     {
27928         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27929     },
27930     
27931     setText : function(str)
27932     {
27933         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27934     },
27935     
27936     setWeight : function(weight)
27937     {
27938         if(this.weight){
27939             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27940         }
27941         
27942         this.weight = weight;
27943         
27944         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27945     },
27946     
27947     setIcon : function(icon)
27948     {
27949         if(this.faicon){
27950             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27951         }
27952         
27953         this.faicon = icon;
27954         
27955         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27956     },
27957     
27958     hide: function() 
27959     {
27960         this.el.hide();   
27961     },
27962     
27963     show: function() 
27964     {  
27965         this.el.show();   
27966     }
27967     
27968 });
27969
27970  
27971 /*
27972 * Licence: LGPL
27973 */
27974
27975 /**
27976  * @class Roo.bootstrap.UploadCropbox
27977  * @extends Roo.bootstrap.Component
27978  * Bootstrap UploadCropbox class
27979  * @cfg {String} emptyText show when image has been loaded
27980  * @cfg {String} rotateNotify show when image too small to rotate
27981  * @cfg {Number} errorTimeout default 3000
27982  * @cfg {Number} minWidth default 300
27983  * @cfg {Number} minHeight default 300
27984  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27985  * @cfg {Boolean} isDocument (true|false) default false
27986  * @cfg {String} url action url
27987  * @cfg {String} paramName default 'imageUpload'
27988  * @cfg {String} method default POST
27989  * @cfg {Boolean} loadMask (true|false) default true
27990  * @cfg {Boolean} loadingText default 'Loading...'
27991  * 
27992  * @constructor
27993  * Create a new UploadCropbox
27994  * @param {Object} config The config object
27995  */
27996
27997 Roo.bootstrap.UploadCropbox = function(config){
27998     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27999     
28000     this.addEvents({
28001         /**
28002          * @event beforeselectfile
28003          * Fire before select file
28004          * @param {Roo.bootstrap.UploadCropbox} this
28005          */
28006         "beforeselectfile" : true,
28007         /**
28008          * @event initial
28009          * Fire after initEvent
28010          * @param {Roo.bootstrap.UploadCropbox} this
28011          */
28012         "initial" : true,
28013         /**
28014          * @event crop
28015          * Fire after initEvent
28016          * @param {Roo.bootstrap.UploadCropbox} this
28017          * @param {String} data
28018          */
28019         "crop" : true,
28020         /**
28021          * @event prepare
28022          * Fire when preparing the file data
28023          * @param {Roo.bootstrap.UploadCropbox} this
28024          * @param {Object} file
28025          */
28026         "prepare" : true,
28027         /**
28028          * @event exception
28029          * Fire when get exception
28030          * @param {Roo.bootstrap.UploadCropbox} this
28031          * @param {XMLHttpRequest} xhr
28032          */
28033         "exception" : true,
28034         /**
28035          * @event beforeloadcanvas
28036          * Fire before load the canvas
28037          * @param {Roo.bootstrap.UploadCropbox} this
28038          * @param {String} src
28039          */
28040         "beforeloadcanvas" : true,
28041         /**
28042          * @event trash
28043          * Fire when trash image
28044          * @param {Roo.bootstrap.UploadCropbox} this
28045          */
28046         "trash" : true,
28047         /**
28048          * @event download
28049          * Fire when download the image
28050          * @param {Roo.bootstrap.UploadCropbox} this
28051          */
28052         "download" : true,
28053         /**
28054          * @event footerbuttonclick
28055          * Fire when footerbuttonclick
28056          * @param {Roo.bootstrap.UploadCropbox} this
28057          * @param {String} type
28058          */
28059         "footerbuttonclick" : true,
28060         /**
28061          * @event resize
28062          * Fire when resize
28063          * @param {Roo.bootstrap.UploadCropbox} this
28064          */
28065         "resize" : true,
28066         /**
28067          * @event rotate
28068          * Fire when rotate the image
28069          * @param {Roo.bootstrap.UploadCropbox} this
28070          * @param {String} pos
28071          */
28072         "rotate" : true,
28073         /**
28074          * @event inspect
28075          * Fire when inspect the file
28076          * @param {Roo.bootstrap.UploadCropbox} this
28077          * @param {Object} file
28078          */
28079         "inspect" : true,
28080         /**
28081          * @event upload
28082          * Fire when xhr upload the file
28083          * @param {Roo.bootstrap.UploadCropbox} this
28084          * @param {Object} data
28085          */
28086         "upload" : true,
28087         /**
28088          * @event arrange
28089          * Fire when arrange the file data
28090          * @param {Roo.bootstrap.UploadCropbox} this
28091          * @param {Object} formData
28092          */
28093         "arrange" : true
28094     });
28095     
28096     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
28097 };
28098
28099 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
28100     
28101     emptyText : 'Click to upload image',
28102     rotateNotify : 'Image is too small to rotate',
28103     errorTimeout : 3000,
28104     scale : 0,
28105     baseScale : 1,
28106     rotate : 0,
28107     dragable : false,
28108     pinching : false,
28109     mouseX : 0,
28110     mouseY : 0,
28111     cropData : false,
28112     minWidth : 300,
28113     minHeight : 300,
28114     file : false,
28115     exif : {},
28116     baseRotate : 1,
28117     cropType : 'image/jpeg',
28118     buttons : false,
28119     canvasLoaded : false,
28120     isDocument : false,
28121     method : 'POST',
28122     paramName : 'imageUpload',
28123     loadMask : true,
28124     loadingText : 'Loading...',
28125     maskEl : false,
28126     
28127     getAutoCreate : function()
28128     {
28129         var cfg = {
28130             tag : 'div',
28131             cls : 'roo-upload-cropbox',
28132             cn : [
28133                 {
28134                     tag : 'input',
28135                     cls : 'roo-upload-cropbox-selector',
28136                     type : 'file'
28137                 },
28138                 {
28139                     tag : 'div',
28140                     cls : 'roo-upload-cropbox-body',
28141                     style : 'cursor:pointer',
28142                     cn : [
28143                         {
28144                             tag : 'div',
28145                             cls : 'roo-upload-cropbox-preview'
28146                         },
28147                         {
28148                             tag : 'div',
28149                             cls : 'roo-upload-cropbox-thumb'
28150                         },
28151                         {
28152                             tag : 'div',
28153                             cls : 'roo-upload-cropbox-empty-notify',
28154                             html : this.emptyText
28155                         },
28156                         {
28157                             tag : 'div',
28158                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
28159                             html : this.rotateNotify
28160                         }
28161                     ]
28162                 },
28163                 {
28164                     tag : 'div',
28165                     cls : 'roo-upload-cropbox-footer',
28166                     cn : {
28167                         tag : 'div',
28168                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
28169                         cn : []
28170                     }
28171                 }
28172             ]
28173         };
28174         
28175         return cfg;
28176     },
28177     
28178     onRender : function(ct, position)
28179     {
28180         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
28181         
28182         if (this.buttons.length) {
28183             
28184             Roo.each(this.buttons, function(bb) {
28185                 
28186                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
28187                 
28188                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
28189                 
28190             }, this);
28191         }
28192         
28193         if(this.loadMask){
28194             this.maskEl = this.el;
28195         }
28196     },
28197     
28198     initEvents : function()
28199     {
28200         this.urlAPI = (window.createObjectURL && window) || 
28201                                 (window.URL && URL.revokeObjectURL && URL) || 
28202                                 (window.webkitURL && webkitURL);
28203                         
28204         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
28205         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28206         
28207         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
28208         this.selectorEl.hide();
28209         
28210         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
28211         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28212         
28213         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
28214         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28215         this.thumbEl.hide();
28216         
28217         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
28218         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28219         
28220         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
28221         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28222         this.errorEl.hide();
28223         
28224         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
28225         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28226         this.footerEl.hide();
28227         
28228         this.setThumbBoxSize();
28229         
28230         this.bind();
28231         
28232         this.resize();
28233         
28234         this.fireEvent('initial', this);
28235     },
28236
28237     bind : function()
28238     {
28239         var _this = this;
28240         
28241         window.addEventListener("resize", function() { _this.resize(); } );
28242         
28243         this.bodyEl.on('click', this.beforeSelectFile, this);
28244         
28245         if(Roo.isTouch){
28246             this.bodyEl.on('touchstart', this.onTouchStart, this);
28247             this.bodyEl.on('touchmove', this.onTouchMove, this);
28248             this.bodyEl.on('touchend', this.onTouchEnd, this);
28249         }
28250         
28251         if(!Roo.isTouch){
28252             this.bodyEl.on('mousedown', this.onMouseDown, this);
28253             this.bodyEl.on('mousemove', this.onMouseMove, this);
28254             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
28255             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
28256             Roo.get(document).on('mouseup', this.onMouseUp, this);
28257         }
28258         
28259         this.selectorEl.on('change', this.onFileSelected, this);
28260     },
28261     
28262     reset : function()
28263     {    
28264         this.scale = 0;
28265         this.baseScale = 1;
28266         this.rotate = 0;
28267         this.baseRotate = 1;
28268         this.dragable = false;
28269         this.pinching = false;
28270         this.mouseX = 0;
28271         this.mouseY = 0;
28272         this.cropData = false;
28273         this.notifyEl.dom.innerHTML = this.emptyText;
28274         
28275         this.selectorEl.dom.value = '';
28276         
28277     },
28278     
28279     resize : function()
28280     {
28281         if(this.fireEvent('resize', this) != false){
28282             this.setThumbBoxPosition();
28283             this.setCanvasPosition();
28284         }
28285     },
28286     
28287     onFooterButtonClick : function(e, el, o, type)
28288     {
28289         switch (type) {
28290             case 'rotate-left' :
28291                 this.onRotateLeft(e);
28292                 break;
28293             case 'rotate-right' :
28294                 this.onRotateRight(e);
28295                 break;
28296             case 'picture' :
28297                 this.beforeSelectFile(e);
28298                 break;
28299             case 'trash' :
28300                 this.trash(e);
28301                 break;
28302             case 'crop' :
28303                 this.crop(e);
28304                 break;
28305             case 'download' :
28306                 this.download(e);
28307                 break;
28308             default :
28309                 break;
28310         }
28311         
28312         this.fireEvent('footerbuttonclick', this, type);
28313     },
28314     
28315     beforeSelectFile : function(e)
28316     {
28317         e.preventDefault();
28318         
28319         if(this.fireEvent('beforeselectfile', this) != false){
28320             this.selectorEl.dom.click();
28321         }
28322     },
28323     
28324     onFileSelected : function(e)
28325     {
28326         e.preventDefault();
28327         
28328         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28329             return;
28330         }
28331         
28332         var file = this.selectorEl.dom.files[0];
28333         
28334         if(this.fireEvent('inspect', this, file) != false){
28335             this.prepare(file);
28336         }
28337         
28338     },
28339     
28340     trash : function(e)
28341     {
28342         this.fireEvent('trash', this);
28343     },
28344     
28345     download : function(e)
28346     {
28347         this.fireEvent('download', this);
28348     },
28349     
28350     loadCanvas : function(src)
28351     {   
28352         if(this.fireEvent('beforeloadcanvas', this, src) != false){
28353             
28354             this.reset();
28355             
28356             this.imageEl = document.createElement('img');
28357             
28358             var _this = this;
28359             
28360             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
28361             
28362             this.imageEl.src = src;
28363         }
28364     },
28365     
28366     onLoadCanvas : function()
28367     {   
28368         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
28369         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
28370         
28371         this.bodyEl.un('click', this.beforeSelectFile, this);
28372         
28373         this.notifyEl.hide();
28374         this.thumbEl.show();
28375         this.footerEl.show();
28376         
28377         this.baseRotateLevel();
28378         
28379         if(this.isDocument){
28380             this.setThumbBoxSize();
28381         }
28382         
28383         this.setThumbBoxPosition();
28384         
28385         this.baseScaleLevel();
28386         
28387         this.draw();
28388         
28389         this.resize();
28390         
28391         this.canvasLoaded = true;
28392         
28393         if(this.loadMask){
28394             this.maskEl.unmask();
28395         }
28396         
28397     },
28398     
28399     setCanvasPosition : function()
28400     {   
28401         if(!this.canvasEl){
28402             return;
28403         }
28404         
28405         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
28406         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
28407         
28408         this.previewEl.setLeft(pw);
28409         this.previewEl.setTop(ph);
28410         
28411     },
28412     
28413     onMouseDown : function(e)
28414     {   
28415         e.stopEvent();
28416         
28417         this.dragable = true;
28418         this.pinching = false;
28419         
28420         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
28421             this.dragable = false;
28422             return;
28423         }
28424         
28425         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28426         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28427         
28428     },
28429     
28430     onMouseMove : function(e)
28431     {   
28432         e.stopEvent();
28433         
28434         if(!this.canvasLoaded){
28435             return;
28436         }
28437         
28438         if (!this.dragable){
28439             return;
28440         }
28441         
28442         var minX = Math.ceil(this.thumbEl.getLeft(true));
28443         var minY = Math.ceil(this.thumbEl.getTop(true));
28444         
28445         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
28446         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
28447         
28448         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28449         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28450         
28451         x = x - this.mouseX;
28452         y = y - this.mouseY;
28453         
28454         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
28455         var bgY = Math.ceil(y + this.previewEl.getTop(true));
28456         
28457         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
28458         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
28459         
28460         this.previewEl.setLeft(bgX);
28461         this.previewEl.setTop(bgY);
28462         
28463         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28464         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28465     },
28466     
28467     onMouseUp : function(e)
28468     {   
28469         e.stopEvent();
28470         
28471         this.dragable = false;
28472     },
28473     
28474     onMouseWheel : function(e)
28475     {   
28476         e.stopEvent();
28477         
28478         this.startScale = this.scale;
28479         
28480         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
28481         
28482         if(!this.zoomable()){
28483             this.scale = this.startScale;
28484             return;
28485         }
28486         
28487         this.draw();
28488         
28489         return;
28490     },
28491     
28492     zoomable : function()
28493     {
28494         var minScale = this.thumbEl.getWidth() / this.minWidth;
28495         
28496         if(this.minWidth < this.minHeight){
28497             minScale = this.thumbEl.getHeight() / this.minHeight;
28498         }
28499         
28500         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
28501         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
28502         
28503         if(
28504                 this.isDocument &&
28505                 (this.rotate == 0 || this.rotate == 180) && 
28506                 (
28507                     width > this.imageEl.OriginWidth || 
28508                     height > this.imageEl.OriginHeight ||
28509                     (width < this.minWidth && height < this.minHeight)
28510                 )
28511         ){
28512             return false;
28513         }
28514         
28515         if(
28516                 this.isDocument &&
28517                 (this.rotate == 90 || this.rotate == 270) && 
28518                 (
28519                     width > this.imageEl.OriginWidth || 
28520                     height > this.imageEl.OriginHeight ||
28521                     (width < this.minHeight && height < this.minWidth)
28522                 )
28523         ){
28524             return false;
28525         }
28526         
28527         if(
28528                 !this.isDocument &&
28529                 (this.rotate == 0 || this.rotate == 180) && 
28530                 (
28531                     width < this.minWidth || 
28532                     width > this.imageEl.OriginWidth || 
28533                     height < this.minHeight || 
28534                     height > this.imageEl.OriginHeight
28535                 )
28536         ){
28537             return false;
28538         }
28539         
28540         if(
28541                 !this.isDocument &&
28542                 (this.rotate == 90 || this.rotate == 270) && 
28543                 (
28544                     width < this.minHeight || 
28545                     width > this.imageEl.OriginWidth || 
28546                     height < this.minWidth || 
28547                     height > this.imageEl.OriginHeight
28548                 )
28549         ){
28550             return false;
28551         }
28552         
28553         return true;
28554         
28555     },
28556     
28557     onRotateLeft : function(e)
28558     {   
28559         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28560             
28561             var minScale = this.thumbEl.getWidth() / this.minWidth;
28562             
28563             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28564             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28565             
28566             this.startScale = this.scale;
28567             
28568             while (this.getScaleLevel() < minScale){
28569             
28570                 this.scale = this.scale + 1;
28571                 
28572                 if(!this.zoomable()){
28573                     break;
28574                 }
28575                 
28576                 if(
28577                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28578                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28579                 ){
28580                     continue;
28581                 }
28582                 
28583                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28584
28585                 this.draw();
28586                 
28587                 return;
28588             }
28589             
28590             this.scale = this.startScale;
28591             
28592             this.onRotateFail();
28593             
28594             return false;
28595         }
28596         
28597         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28598
28599         if(this.isDocument){
28600             this.setThumbBoxSize();
28601             this.setThumbBoxPosition();
28602             this.setCanvasPosition();
28603         }
28604         
28605         this.draw();
28606         
28607         this.fireEvent('rotate', this, 'left');
28608         
28609     },
28610     
28611     onRotateRight : function(e)
28612     {
28613         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28614             
28615             var minScale = this.thumbEl.getWidth() / this.minWidth;
28616         
28617             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28618             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28619             
28620             this.startScale = this.scale;
28621             
28622             while (this.getScaleLevel() < minScale){
28623             
28624                 this.scale = this.scale + 1;
28625                 
28626                 if(!this.zoomable()){
28627                     break;
28628                 }
28629                 
28630                 if(
28631                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28632                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28633                 ){
28634                     continue;
28635                 }
28636                 
28637                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28638
28639                 this.draw();
28640                 
28641                 return;
28642             }
28643             
28644             this.scale = this.startScale;
28645             
28646             this.onRotateFail();
28647             
28648             return false;
28649         }
28650         
28651         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28652
28653         if(this.isDocument){
28654             this.setThumbBoxSize();
28655             this.setThumbBoxPosition();
28656             this.setCanvasPosition();
28657         }
28658         
28659         this.draw();
28660         
28661         this.fireEvent('rotate', this, 'right');
28662     },
28663     
28664     onRotateFail : function()
28665     {
28666         this.errorEl.show(true);
28667         
28668         var _this = this;
28669         
28670         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28671     },
28672     
28673     draw : function()
28674     {
28675         this.previewEl.dom.innerHTML = '';
28676         
28677         var canvasEl = document.createElement("canvas");
28678         
28679         var contextEl = canvasEl.getContext("2d");
28680         
28681         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28682         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28683         var center = this.imageEl.OriginWidth / 2;
28684         
28685         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28686             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28687             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28688             center = this.imageEl.OriginHeight / 2;
28689         }
28690         
28691         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28692         
28693         contextEl.translate(center, center);
28694         contextEl.rotate(this.rotate * Math.PI / 180);
28695
28696         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28697         
28698         this.canvasEl = document.createElement("canvas");
28699         
28700         this.contextEl = this.canvasEl.getContext("2d");
28701         
28702         switch (this.rotate) {
28703             case 0 :
28704                 
28705                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28706                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28707                 
28708                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28709                 
28710                 break;
28711             case 90 : 
28712                 
28713                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28714                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28715                 
28716                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28717                     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);
28718                     break;
28719                 }
28720                 
28721                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28722                 
28723                 break;
28724             case 180 :
28725                 
28726                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28727                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28728                 
28729                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28730                     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);
28731                     break;
28732                 }
28733                 
28734                 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);
28735                 
28736                 break;
28737             case 270 :
28738                 
28739                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28740                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28741         
28742                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28743                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28744                     break;
28745                 }
28746                 
28747                 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);
28748                 
28749                 break;
28750             default : 
28751                 break;
28752         }
28753         
28754         this.previewEl.appendChild(this.canvasEl);
28755         
28756         this.setCanvasPosition();
28757     },
28758     
28759     crop : function()
28760     {
28761         if(!this.canvasLoaded){
28762             return;
28763         }
28764         
28765         var imageCanvas = document.createElement("canvas");
28766         
28767         var imageContext = imageCanvas.getContext("2d");
28768         
28769         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28770         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28771         
28772         var center = imageCanvas.width / 2;
28773         
28774         imageContext.translate(center, center);
28775         
28776         imageContext.rotate(this.rotate * Math.PI / 180);
28777         
28778         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28779         
28780         var canvas = document.createElement("canvas");
28781         
28782         var context = canvas.getContext("2d");
28783                 
28784         canvas.width = this.minWidth;
28785         canvas.height = this.minHeight;
28786
28787         switch (this.rotate) {
28788             case 0 :
28789                 
28790                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28791                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28792                 
28793                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28794                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28795                 
28796                 var targetWidth = this.minWidth - 2 * x;
28797                 var targetHeight = this.minHeight - 2 * y;
28798                 
28799                 var scale = 1;
28800                 
28801                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28802                     scale = targetWidth / width;
28803                 }
28804                 
28805                 if(x > 0 && y == 0){
28806                     scale = targetHeight / height;
28807                 }
28808                 
28809                 if(x > 0 && y > 0){
28810                     scale = targetWidth / width;
28811                     
28812                     if(width < height){
28813                         scale = targetHeight / height;
28814                     }
28815                 }
28816                 
28817                 context.scale(scale, scale);
28818                 
28819                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28820                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28821
28822                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28823                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28824
28825                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28826                 
28827                 break;
28828             case 90 : 
28829                 
28830                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28831                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28832                 
28833                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28834                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28835                 
28836                 var targetWidth = this.minWidth - 2 * x;
28837                 var targetHeight = this.minHeight - 2 * y;
28838                 
28839                 var scale = 1;
28840                 
28841                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28842                     scale = targetWidth / width;
28843                 }
28844                 
28845                 if(x > 0 && y == 0){
28846                     scale = targetHeight / height;
28847                 }
28848                 
28849                 if(x > 0 && y > 0){
28850                     scale = targetWidth / width;
28851                     
28852                     if(width < height){
28853                         scale = targetHeight / height;
28854                     }
28855                 }
28856                 
28857                 context.scale(scale, scale);
28858                 
28859                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28860                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28861
28862                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28863                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28864                 
28865                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28866                 
28867                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28868                 
28869                 break;
28870             case 180 :
28871                 
28872                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28873                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28874                 
28875                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28876                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28877                 
28878                 var targetWidth = this.minWidth - 2 * x;
28879                 var targetHeight = this.minHeight - 2 * y;
28880                 
28881                 var scale = 1;
28882                 
28883                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28884                     scale = targetWidth / width;
28885                 }
28886                 
28887                 if(x > 0 && y == 0){
28888                     scale = targetHeight / height;
28889                 }
28890                 
28891                 if(x > 0 && y > 0){
28892                     scale = targetWidth / width;
28893                     
28894                     if(width < height){
28895                         scale = targetHeight / height;
28896                     }
28897                 }
28898                 
28899                 context.scale(scale, scale);
28900                 
28901                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28902                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28903
28904                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28905                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28906
28907                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28908                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28909                 
28910                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28911                 
28912                 break;
28913             case 270 :
28914                 
28915                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28916                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28917                 
28918                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28919                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28920                 
28921                 var targetWidth = this.minWidth - 2 * x;
28922                 var targetHeight = this.minHeight - 2 * y;
28923                 
28924                 var scale = 1;
28925                 
28926                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28927                     scale = targetWidth / width;
28928                 }
28929                 
28930                 if(x > 0 && y == 0){
28931                     scale = targetHeight / height;
28932                 }
28933                 
28934                 if(x > 0 && y > 0){
28935                     scale = targetWidth / width;
28936                     
28937                     if(width < height){
28938                         scale = targetHeight / height;
28939                     }
28940                 }
28941                 
28942                 context.scale(scale, scale);
28943                 
28944                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28945                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28946
28947                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28948                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28949                 
28950                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28951                 
28952                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28953                 
28954                 break;
28955             default : 
28956                 break;
28957         }
28958         
28959         this.cropData = canvas.toDataURL(this.cropType);
28960         
28961         if(this.fireEvent('crop', this, this.cropData) !== false){
28962             this.process(this.file, this.cropData);
28963         }
28964         
28965         return;
28966         
28967     },
28968     
28969     setThumbBoxSize : function()
28970     {
28971         var width, height;
28972         
28973         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28974             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28975             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28976             
28977             this.minWidth = width;
28978             this.minHeight = height;
28979             
28980             if(this.rotate == 90 || this.rotate == 270){
28981                 this.minWidth = height;
28982                 this.minHeight = width;
28983             }
28984         }
28985         
28986         height = 300;
28987         width = Math.ceil(this.minWidth * height / this.minHeight);
28988         
28989         if(this.minWidth > this.minHeight){
28990             width = 300;
28991             height = Math.ceil(this.minHeight * width / this.minWidth);
28992         }
28993         
28994         this.thumbEl.setStyle({
28995             width : width + 'px',
28996             height : height + 'px'
28997         });
28998
28999         return;
29000             
29001     },
29002     
29003     setThumbBoxPosition : function()
29004     {
29005         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
29006         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
29007         
29008         this.thumbEl.setLeft(x);
29009         this.thumbEl.setTop(y);
29010         
29011     },
29012     
29013     baseRotateLevel : function()
29014     {
29015         this.baseRotate = 1;
29016         
29017         if(
29018                 typeof(this.exif) != 'undefined' &&
29019                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
29020                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
29021         ){
29022             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
29023         }
29024         
29025         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
29026         
29027     },
29028     
29029     baseScaleLevel : function()
29030     {
29031         var width, height;
29032         
29033         if(this.isDocument){
29034             
29035             if(this.baseRotate == 6 || this.baseRotate == 8){
29036             
29037                 height = this.thumbEl.getHeight();
29038                 this.baseScale = height / this.imageEl.OriginWidth;
29039
29040                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
29041                     width = this.thumbEl.getWidth();
29042                     this.baseScale = width / this.imageEl.OriginHeight;
29043                 }
29044
29045                 return;
29046             }
29047
29048             height = this.thumbEl.getHeight();
29049             this.baseScale = height / this.imageEl.OriginHeight;
29050
29051             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
29052                 width = this.thumbEl.getWidth();
29053                 this.baseScale = width / this.imageEl.OriginWidth;
29054             }
29055
29056             return;
29057         }
29058         
29059         if(this.baseRotate == 6 || this.baseRotate == 8){
29060             
29061             width = this.thumbEl.getHeight();
29062             this.baseScale = width / this.imageEl.OriginHeight;
29063             
29064             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
29065                 height = this.thumbEl.getWidth();
29066                 this.baseScale = height / this.imageEl.OriginHeight;
29067             }
29068             
29069             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29070                 height = this.thumbEl.getWidth();
29071                 this.baseScale = height / this.imageEl.OriginHeight;
29072                 
29073                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
29074                     width = this.thumbEl.getHeight();
29075                     this.baseScale = width / this.imageEl.OriginWidth;
29076                 }
29077             }
29078             
29079             return;
29080         }
29081         
29082         width = this.thumbEl.getWidth();
29083         this.baseScale = width / this.imageEl.OriginWidth;
29084         
29085         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
29086             height = this.thumbEl.getHeight();
29087             this.baseScale = height / this.imageEl.OriginHeight;
29088         }
29089         
29090         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29091             
29092             height = this.thumbEl.getHeight();
29093             this.baseScale = height / this.imageEl.OriginHeight;
29094             
29095             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
29096                 width = this.thumbEl.getWidth();
29097                 this.baseScale = width / this.imageEl.OriginWidth;
29098             }
29099             
29100         }
29101         
29102         return;
29103     },
29104     
29105     getScaleLevel : function()
29106     {
29107         return this.baseScale * Math.pow(1.1, this.scale);
29108     },
29109     
29110     onTouchStart : function(e)
29111     {
29112         if(!this.canvasLoaded){
29113             this.beforeSelectFile(e);
29114             return;
29115         }
29116         
29117         var touches = e.browserEvent.touches;
29118         
29119         if(!touches){
29120             return;
29121         }
29122         
29123         if(touches.length == 1){
29124             this.onMouseDown(e);
29125             return;
29126         }
29127         
29128         if(touches.length != 2){
29129             return;
29130         }
29131         
29132         var coords = [];
29133         
29134         for(var i = 0, finger; finger = touches[i]; i++){
29135             coords.push(finger.pageX, finger.pageY);
29136         }
29137         
29138         var x = Math.pow(coords[0] - coords[2], 2);
29139         var y = Math.pow(coords[1] - coords[3], 2);
29140         
29141         this.startDistance = Math.sqrt(x + y);
29142         
29143         this.startScale = this.scale;
29144         
29145         this.pinching = true;
29146         this.dragable = false;
29147         
29148     },
29149     
29150     onTouchMove : function(e)
29151     {
29152         if(!this.pinching && !this.dragable){
29153             return;
29154         }
29155         
29156         var touches = e.browserEvent.touches;
29157         
29158         if(!touches){
29159             return;
29160         }
29161         
29162         if(this.dragable){
29163             this.onMouseMove(e);
29164             return;
29165         }
29166         
29167         var coords = [];
29168         
29169         for(var i = 0, finger; finger = touches[i]; i++){
29170             coords.push(finger.pageX, finger.pageY);
29171         }
29172         
29173         var x = Math.pow(coords[0] - coords[2], 2);
29174         var y = Math.pow(coords[1] - coords[3], 2);
29175         
29176         this.endDistance = Math.sqrt(x + y);
29177         
29178         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
29179         
29180         if(!this.zoomable()){
29181             this.scale = this.startScale;
29182             return;
29183         }
29184         
29185         this.draw();
29186         
29187     },
29188     
29189     onTouchEnd : function(e)
29190     {
29191         this.pinching = false;
29192         this.dragable = false;
29193         
29194     },
29195     
29196     process : function(file, crop)
29197     {
29198         if(this.loadMask){
29199             this.maskEl.mask(this.loadingText);
29200         }
29201         
29202         this.xhr = new XMLHttpRequest();
29203         
29204         file.xhr = this.xhr;
29205
29206         this.xhr.open(this.method, this.url, true);
29207         
29208         var headers = {
29209             "Accept": "application/json",
29210             "Cache-Control": "no-cache",
29211             "X-Requested-With": "XMLHttpRequest"
29212         };
29213         
29214         for (var headerName in headers) {
29215             var headerValue = headers[headerName];
29216             if (headerValue) {
29217                 this.xhr.setRequestHeader(headerName, headerValue);
29218             }
29219         }
29220         
29221         var _this = this;
29222         
29223         this.xhr.onload = function()
29224         {
29225             _this.xhrOnLoad(_this.xhr);
29226         }
29227         
29228         this.xhr.onerror = function()
29229         {
29230             _this.xhrOnError(_this.xhr);
29231         }
29232         
29233         var formData = new FormData();
29234
29235         formData.append('returnHTML', 'NO');
29236         
29237         if(crop){
29238             formData.append('crop', crop);
29239         }
29240         
29241         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
29242             formData.append(this.paramName, file, file.name);
29243         }
29244         
29245         if(typeof(file.filename) != 'undefined'){
29246             formData.append('filename', file.filename);
29247         }
29248         
29249         if(typeof(file.mimetype) != 'undefined'){
29250             formData.append('mimetype', file.mimetype);
29251         }
29252         
29253         if(this.fireEvent('arrange', this, formData) != false){
29254             this.xhr.send(formData);
29255         };
29256     },
29257     
29258     xhrOnLoad : function(xhr)
29259     {
29260         if(this.loadMask){
29261             this.maskEl.unmask();
29262         }
29263         
29264         if (xhr.readyState !== 4) {
29265             this.fireEvent('exception', this, xhr);
29266             return;
29267         }
29268
29269         var response = Roo.decode(xhr.responseText);
29270         
29271         if(!response.success){
29272             this.fireEvent('exception', this, xhr);
29273             return;
29274         }
29275         
29276         var response = Roo.decode(xhr.responseText);
29277         
29278         this.fireEvent('upload', this, response);
29279         
29280     },
29281     
29282     xhrOnError : function()
29283     {
29284         if(this.loadMask){
29285             this.maskEl.unmask();
29286         }
29287         
29288         Roo.log('xhr on error');
29289         
29290         var response = Roo.decode(xhr.responseText);
29291           
29292         Roo.log(response);
29293         
29294     },
29295     
29296     prepare : function(file)
29297     {   
29298         if(this.loadMask){
29299             this.maskEl.mask(this.loadingText);
29300         }
29301         
29302         this.file = false;
29303         this.exif = {};
29304         
29305         if(typeof(file) === 'string'){
29306             this.loadCanvas(file);
29307             return;
29308         }
29309         
29310         if(!file || !this.urlAPI){
29311             return;
29312         }
29313         
29314         this.file = file;
29315         this.cropType = file.type;
29316         
29317         var _this = this;
29318         
29319         if(this.fireEvent('prepare', this, this.file) != false){
29320             
29321             var reader = new FileReader();
29322             
29323             reader.onload = function (e) {
29324                 if (e.target.error) {
29325                     Roo.log(e.target.error);
29326                     return;
29327                 }
29328                 
29329                 var buffer = e.target.result,
29330                     dataView = new DataView(buffer),
29331                     offset = 2,
29332                     maxOffset = dataView.byteLength - 4,
29333                     markerBytes,
29334                     markerLength;
29335                 
29336                 if (dataView.getUint16(0) === 0xffd8) {
29337                     while (offset < maxOffset) {
29338                         markerBytes = dataView.getUint16(offset);
29339                         
29340                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
29341                             markerLength = dataView.getUint16(offset + 2) + 2;
29342                             if (offset + markerLength > dataView.byteLength) {
29343                                 Roo.log('Invalid meta data: Invalid segment size.');
29344                                 break;
29345                             }
29346                             
29347                             if(markerBytes == 0xffe1){
29348                                 _this.parseExifData(
29349                                     dataView,
29350                                     offset,
29351                                     markerLength
29352                                 );
29353                             }
29354                             
29355                             offset += markerLength;
29356                             
29357                             continue;
29358                         }
29359                         
29360                         break;
29361                     }
29362                     
29363                 }
29364                 
29365                 var url = _this.urlAPI.createObjectURL(_this.file);
29366                 
29367                 _this.loadCanvas(url);
29368                 
29369                 return;
29370             }
29371             
29372             reader.readAsArrayBuffer(this.file);
29373             
29374         }
29375         
29376     },
29377     
29378     parseExifData : function(dataView, offset, length)
29379     {
29380         var tiffOffset = offset + 10,
29381             littleEndian,
29382             dirOffset;
29383     
29384         if (dataView.getUint32(offset + 4) !== 0x45786966) {
29385             // No Exif data, might be XMP data instead
29386             return;
29387         }
29388         
29389         // Check for the ASCII code for "Exif" (0x45786966):
29390         if (dataView.getUint32(offset + 4) !== 0x45786966) {
29391             // No Exif data, might be XMP data instead
29392             return;
29393         }
29394         if (tiffOffset + 8 > dataView.byteLength) {
29395             Roo.log('Invalid Exif data: Invalid segment size.');
29396             return;
29397         }
29398         // Check for the two null bytes:
29399         if (dataView.getUint16(offset + 8) !== 0x0000) {
29400             Roo.log('Invalid Exif data: Missing byte alignment offset.');
29401             return;
29402         }
29403         // Check the byte alignment:
29404         switch (dataView.getUint16(tiffOffset)) {
29405         case 0x4949:
29406             littleEndian = true;
29407             break;
29408         case 0x4D4D:
29409             littleEndian = false;
29410             break;
29411         default:
29412             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
29413             return;
29414         }
29415         // Check for the TIFF tag marker (0x002A):
29416         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
29417             Roo.log('Invalid Exif data: Missing TIFF marker.');
29418             return;
29419         }
29420         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
29421         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
29422         
29423         this.parseExifTags(
29424             dataView,
29425             tiffOffset,
29426             tiffOffset + dirOffset,
29427             littleEndian
29428         );
29429     },
29430     
29431     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
29432     {
29433         var tagsNumber,
29434             dirEndOffset,
29435             i;
29436         if (dirOffset + 6 > dataView.byteLength) {
29437             Roo.log('Invalid Exif data: Invalid directory offset.');
29438             return;
29439         }
29440         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
29441         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
29442         if (dirEndOffset + 4 > dataView.byteLength) {
29443             Roo.log('Invalid Exif data: Invalid directory size.');
29444             return;
29445         }
29446         for (i = 0; i < tagsNumber; i += 1) {
29447             this.parseExifTag(
29448                 dataView,
29449                 tiffOffset,
29450                 dirOffset + 2 + 12 * i, // tag offset
29451                 littleEndian
29452             );
29453         }
29454         // Return the offset to the next directory:
29455         return dataView.getUint32(dirEndOffset, littleEndian);
29456     },
29457     
29458     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
29459     {
29460         var tag = dataView.getUint16(offset, littleEndian);
29461         
29462         this.exif[tag] = this.getExifValue(
29463             dataView,
29464             tiffOffset,
29465             offset,
29466             dataView.getUint16(offset + 2, littleEndian), // tag type
29467             dataView.getUint32(offset + 4, littleEndian), // tag length
29468             littleEndian
29469         );
29470     },
29471     
29472     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
29473     {
29474         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
29475             tagSize,
29476             dataOffset,
29477             values,
29478             i,
29479             str,
29480             c;
29481     
29482         if (!tagType) {
29483             Roo.log('Invalid Exif data: Invalid tag type.');
29484             return;
29485         }
29486         
29487         tagSize = tagType.size * length;
29488         // Determine if the value is contained in the dataOffset bytes,
29489         // or if the value at the dataOffset is a pointer to the actual data:
29490         dataOffset = tagSize > 4 ?
29491                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
29492         if (dataOffset + tagSize > dataView.byteLength) {
29493             Roo.log('Invalid Exif data: Invalid data offset.');
29494             return;
29495         }
29496         if (length === 1) {
29497             return tagType.getValue(dataView, dataOffset, littleEndian);
29498         }
29499         values = [];
29500         for (i = 0; i < length; i += 1) {
29501             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
29502         }
29503         
29504         if (tagType.ascii) {
29505             str = '';
29506             // Concatenate the chars:
29507             for (i = 0; i < values.length; i += 1) {
29508                 c = values[i];
29509                 // Ignore the terminating NULL byte(s):
29510                 if (c === '\u0000') {
29511                     break;
29512                 }
29513                 str += c;
29514             }
29515             return str;
29516         }
29517         return values;
29518     }
29519     
29520 });
29521
29522 Roo.apply(Roo.bootstrap.UploadCropbox, {
29523     tags : {
29524         'Orientation': 0x0112
29525     },
29526     
29527     Orientation: {
29528             1: 0, //'top-left',
29529 //            2: 'top-right',
29530             3: 180, //'bottom-right',
29531 //            4: 'bottom-left',
29532 //            5: 'left-top',
29533             6: 90, //'right-top',
29534 //            7: 'right-bottom',
29535             8: 270 //'left-bottom'
29536     },
29537     
29538     exifTagTypes : {
29539         // byte, 8-bit unsigned int:
29540         1: {
29541             getValue: function (dataView, dataOffset) {
29542                 return dataView.getUint8(dataOffset);
29543             },
29544             size: 1
29545         },
29546         // ascii, 8-bit byte:
29547         2: {
29548             getValue: function (dataView, dataOffset) {
29549                 return String.fromCharCode(dataView.getUint8(dataOffset));
29550             },
29551             size: 1,
29552             ascii: true
29553         },
29554         // short, 16 bit int:
29555         3: {
29556             getValue: function (dataView, dataOffset, littleEndian) {
29557                 return dataView.getUint16(dataOffset, littleEndian);
29558             },
29559             size: 2
29560         },
29561         // long, 32 bit int:
29562         4: {
29563             getValue: function (dataView, dataOffset, littleEndian) {
29564                 return dataView.getUint32(dataOffset, littleEndian);
29565             },
29566             size: 4
29567         },
29568         // rational = two long values, first is numerator, second is denominator:
29569         5: {
29570             getValue: function (dataView, dataOffset, littleEndian) {
29571                 return dataView.getUint32(dataOffset, littleEndian) /
29572                     dataView.getUint32(dataOffset + 4, littleEndian);
29573             },
29574             size: 8
29575         },
29576         // slong, 32 bit signed int:
29577         9: {
29578             getValue: function (dataView, dataOffset, littleEndian) {
29579                 return dataView.getInt32(dataOffset, littleEndian);
29580             },
29581             size: 4
29582         },
29583         // srational, two slongs, first is numerator, second is denominator:
29584         10: {
29585             getValue: function (dataView, dataOffset, littleEndian) {
29586                 return dataView.getInt32(dataOffset, littleEndian) /
29587                     dataView.getInt32(dataOffset + 4, littleEndian);
29588             },
29589             size: 8
29590         }
29591     },
29592     
29593     footer : {
29594         STANDARD : [
29595             {
29596                 tag : 'div',
29597                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29598                 action : 'rotate-left',
29599                 cn : [
29600                     {
29601                         tag : 'button',
29602                         cls : 'btn btn-default',
29603                         html : '<i class="fa fa-undo"></i>'
29604                     }
29605                 ]
29606             },
29607             {
29608                 tag : 'div',
29609                 cls : 'btn-group roo-upload-cropbox-picture',
29610                 action : 'picture',
29611                 cn : [
29612                     {
29613                         tag : 'button',
29614                         cls : 'btn btn-default',
29615                         html : '<i class="fa fa-picture-o"></i>'
29616                     }
29617                 ]
29618             },
29619             {
29620                 tag : 'div',
29621                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29622                 action : 'rotate-right',
29623                 cn : [
29624                     {
29625                         tag : 'button',
29626                         cls : 'btn btn-default',
29627                         html : '<i class="fa fa-repeat"></i>'
29628                     }
29629                 ]
29630             }
29631         ],
29632         DOCUMENT : [
29633             {
29634                 tag : 'div',
29635                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29636                 action : 'rotate-left',
29637                 cn : [
29638                     {
29639                         tag : 'button',
29640                         cls : 'btn btn-default',
29641                         html : '<i class="fa fa-undo"></i>'
29642                     }
29643                 ]
29644             },
29645             {
29646                 tag : 'div',
29647                 cls : 'btn-group roo-upload-cropbox-download',
29648                 action : 'download',
29649                 cn : [
29650                     {
29651                         tag : 'button',
29652                         cls : 'btn btn-default',
29653                         html : '<i class="fa fa-download"></i>'
29654                     }
29655                 ]
29656             },
29657             {
29658                 tag : 'div',
29659                 cls : 'btn-group roo-upload-cropbox-crop',
29660                 action : 'crop',
29661                 cn : [
29662                     {
29663                         tag : 'button',
29664                         cls : 'btn btn-default',
29665                         html : '<i class="fa fa-crop"></i>'
29666                     }
29667                 ]
29668             },
29669             {
29670                 tag : 'div',
29671                 cls : 'btn-group roo-upload-cropbox-trash',
29672                 action : 'trash',
29673                 cn : [
29674                     {
29675                         tag : 'button',
29676                         cls : 'btn btn-default',
29677                         html : '<i class="fa fa-trash"></i>'
29678                     }
29679                 ]
29680             },
29681             {
29682                 tag : 'div',
29683                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29684                 action : 'rotate-right',
29685                 cn : [
29686                     {
29687                         tag : 'button',
29688                         cls : 'btn btn-default',
29689                         html : '<i class="fa fa-repeat"></i>'
29690                     }
29691                 ]
29692             }
29693         ],
29694         ROTATOR : [
29695             {
29696                 tag : 'div',
29697                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29698                 action : 'rotate-left',
29699                 cn : [
29700                     {
29701                         tag : 'button',
29702                         cls : 'btn btn-default',
29703                         html : '<i class="fa fa-undo"></i>'
29704                     }
29705                 ]
29706             },
29707             {
29708                 tag : 'div',
29709                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29710                 action : 'rotate-right',
29711                 cn : [
29712                     {
29713                         tag : 'button',
29714                         cls : 'btn btn-default',
29715                         html : '<i class="fa fa-repeat"></i>'
29716                     }
29717                 ]
29718             }
29719         ]
29720     }
29721 });
29722
29723 /*
29724 * Licence: LGPL
29725 */
29726
29727 /**
29728  * @class Roo.bootstrap.DocumentManager
29729  * @extends Roo.bootstrap.Component
29730  * Bootstrap DocumentManager class
29731  * @cfg {String} paramName default 'imageUpload'
29732  * @cfg {String} toolTipName default 'filename'
29733  * @cfg {String} method default POST
29734  * @cfg {String} url action url
29735  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29736  * @cfg {Boolean} multiple multiple upload default true
29737  * @cfg {Number} thumbSize default 300
29738  * @cfg {String} fieldLabel
29739  * @cfg {Number} labelWidth default 4
29740  * @cfg {String} labelAlign (left|top) default left
29741  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29742 * @cfg {Number} labellg set the width of label (1-12)
29743  * @cfg {Number} labelmd set the width of label (1-12)
29744  * @cfg {Number} labelsm set the width of label (1-12)
29745  * @cfg {Number} labelxs set the width of label (1-12)
29746  * 
29747  * @constructor
29748  * Create a new DocumentManager
29749  * @param {Object} config The config object
29750  */
29751
29752 Roo.bootstrap.DocumentManager = function(config){
29753     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29754     
29755     this.files = [];
29756     this.delegates = [];
29757     
29758     this.addEvents({
29759         /**
29760          * @event initial
29761          * Fire when initial the DocumentManager
29762          * @param {Roo.bootstrap.DocumentManager} this
29763          */
29764         "initial" : true,
29765         /**
29766          * @event inspect
29767          * inspect selected file
29768          * @param {Roo.bootstrap.DocumentManager} this
29769          * @param {File} file
29770          */
29771         "inspect" : true,
29772         /**
29773          * @event exception
29774          * Fire when xhr load exception
29775          * @param {Roo.bootstrap.DocumentManager} this
29776          * @param {XMLHttpRequest} xhr
29777          */
29778         "exception" : true,
29779         /**
29780          * @event afterupload
29781          * Fire when xhr load exception
29782          * @param {Roo.bootstrap.DocumentManager} this
29783          * @param {XMLHttpRequest} xhr
29784          */
29785         "afterupload" : true,
29786         /**
29787          * @event prepare
29788          * prepare the form data
29789          * @param {Roo.bootstrap.DocumentManager} this
29790          * @param {Object} formData
29791          */
29792         "prepare" : true,
29793         /**
29794          * @event remove
29795          * Fire when remove the file
29796          * @param {Roo.bootstrap.DocumentManager} this
29797          * @param {Object} file
29798          */
29799         "remove" : true,
29800         /**
29801          * @event refresh
29802          * Fire after refresh the file
29803          * @param {Roo.bootstrap.DocumentManager} this
29804          */
29805         "refresh" : true,
29806         /**
29807          * @event click
29808          * Fire after click the image
29809          * @param {Roo.bootstrap.DocumentManager} this
29810          * @param {Object} file
29811          */
29812         "click" : true,
29813         /**
29814          * @event edit
29815          * Fire when upload a image and editable set to true
29816          * @param {Roo.bootstrap.DocumentManager} this
29817          * @param {Object} file
29818          */
29819         "edit" : true,
29820         /**
29821          * @event beforeselectfile
29822          * Fire before select file
29823          * @param {Roo.bootstrap.DocumentManager} this
29824          */
29825         "beforeselectfile" : true,
29826         /**
29827          * @event process
29828          * Fire before process file
29829          * @param {Roo.bootstrap.DocumentManager} this
29830          * @param {Object} file
29831          */
29832         "process" : true,
29833         /**
29834          * @event previewrendered
29835          * Fire when preview rendered
29836          * @param {Roo.bootstrap.DocumentManager} this
29837          * @param {Object} file
29838          */
29839         "previewrendered" : true,
29840         /**
29841          */
29842         "previewResize" : true
29843         
29844     });
29845 };
29846
29847 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29848     
29849     boxes : 0,
29850     inputName : '',
29851     thumbSize : 300,
29852     multiple : true,
29853     files : false,
29854     method : 'POST',
29855     url : '',
29856     paramName : 'imageUpload',
29857     toolTipName : 'filename',
29858     fieldLabel : '',
29859     labelWidth : 4,
29860     labelAlign : 'left',
29861     editable : true,
29862     delegates : false,
29863     xhr : false, 
29864     
29865     labellg : 0,
29866     labelmd : 0,
29867     labelsm : 0,
29868     labelxs : 0,
29869     
29870     getAutoCreate : function()
29871     {   
29872         var managerWidget = {
29873             tag : 'div',
29874             cls : 'roo-document-manager',
29875             cn : [
29876                 {
29877                     tag : 'input',
29878                     cls : 'roo-document-manager-selector',
29879                     type : 'file'
29880                 },
29881                 {
29882                     tag : 'div',
29883                     cls : 'roo-document-manager-uploader',
29884                     cn : [
29885                         {
29886                             tag : 'div',
29887                             cls : 'roo-document-manager-upload-btn',
29888                             html : '<i class="fa fa-plus"></i>'
29889                         }
29890                     ]
29891                     
29892                 }
29893             ]
29894         };
29895         
29896         var content = [
29897             {
29898                 tag : 'div',
29899                 cls : 'column col-md-12',
29900                 cn : managerWidget
29901             }
29902         ];
29903         
29904         if(this.fieldLabel.length){
29905             
29906             content = [
29907                 {
29908                     tag : 'div',
29909                     cls : 'column col-md-12',
29910                     html : this.fieldLabel
29911                 },
29912                 {
29913                     tag : 'div',
29914                     cls : 'column col-md-12',
29915                     cn : managerWidget
29916                 }
29917             ];
29918
29919             if(this.labelAlign == 'left'){
29920                 content = [
29921                     {
29922                         tag : 'div',
29923                         cls : 'column',
29924                         html : this.fieldLabel
29925                     },
29926                     {
29927                         tag : 'div',
29928                         cls : 'column',
29929                         cn : managerWidget
29930                     }
29931                 ];
29932                 
29933                 if(this.labelWidth > 12){
29934                     content[0].style = "width: " + this.labelWidth + 'px';
29935                 }
29936
29937                 if(this.labelWidth < 13 && this.labelmd == 0){
29938                     this.labelmd = this.labelWidth;
29939                 }
29940
29941                 if(this.labellg > 0){
29942                     content[0].cls += ' col-lg-' + this.labellg;
29943                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29944                 }
29945
29946                 if(this.labelmd > 0){
29947                     content[0].cls += ' col-md-' + this.labelmd;
29948                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29949                 }
29950
29951                 if(this.labelsm > 0){
29952                     content[0].cls += ' col-sm-' + this.labelsm;
29953                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29954                 }
29955
29956                 if(this.labelxs > 0){
29957                     content[0].cls += ' col-xs-' + this.labelxs;
29958                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29959                 }
29960                 
29961             }
29962         }
29963         
29964         var cfg = {
29965             tag : 'div',
29966             cls : 'row clearfix',
29967             cn : content
29968         };
29969         
29970         return cfg;
29971         
29972     },
29973     
29974     initEvents : function()
29975     {
29976         this.managerEl = this.el.select('.roo-document-manager', true).first();
29977         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29978         
29979         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29980         this.selectorEl.hide();
29981         
29982         if(this.multiple){
29983             this.selectorEl.attr('multiple', 'multiple');
29984         }
29985         
29986         this.selectorEl.on('change', this.onFileSelected, this);
29987         
29988         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29989         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29990         
29991         this.uploader.on('click', this.onUploaderClick, this);
29992         
29993         this.renderProgressDialog();
29994         
29995         var _this = this;
29996         
29997         window.addEventListener("resize", function() { _this.refresh(); } );
29998         
29999         this.fireEvent('initial', this);
30000     },
30001     
30002     renderProgressDialog : function()
30003     {
30004         var _this = this;
30005         
30006         this.progressDialog = new Roo.bootstrap.Modal({
30007             cls : 'roo-document-manager-progress-dialog',
30008             allow_close : false,
30009             animate : false,
30010             title : '',
30011             buttons : [
30012                 {
30013                     name  :'cancel',
30014                     weight : 'danger',
30015                     html : 'Cancel'
30016                 }
30017             ], 
30018             listeners : { 
30019                 btnclick : function() {
30020                     _this.uploadCancel();
30021                     this.hide();
30022                 }
30023             }
30024         });
30025          
30026         this.progressDialog.render(Roo.get(document.body));
30027          
30028         this.progress = new Roo.bootstrap.Progress({
30029             cls : 'roo-document-manager-progress',
30030             active : true,
30031             striped : true
30032         });
30033         
30034         this.progress.render(this.progressDialog.getChildContainer());
30035         
30036         this.progressBar = new Roo.bootstrap.ProgressBar({
30037             cls : 'roo-document-manager-progress-bar',
30038             aria_valuenow : 0,
30039             aria_valuemin : 0,
30040             aria_valuemax : 12,
30041             panel : 'success'
30042         });
30043         
30044         this.progressBar.render(this.progress.getChildContainer());
30045     },
30046     
30047     onUploaderClick : function(e)
30048     {
30049         e.preventDefault();
30050      
30051         if(this.fireEvent('beforeselectfile', this) != false){
30052             this.selectorEl.dom.click();
30053         }
30054         
30055     },
30056     
30057     onFileSelected : function(e)
30058     {
30059         e.preventDefault();
30060         
30061         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30062             return;
30063         }
30064         
30065         Roo.each(this.selectorEl.dom.files, function(file){
30066             if(this.fireEvent('inspect', this, file) != false){
30067                 this.files.push(file);
30068             }
30069         }, this);
30070         
30071         this.queue();
30072         
30073     },
30074     
30075     queue : function()
30076     {
30077         this.selectorEl.dom.value = '';
30078         
30079         if(!this.files || !this.files.length){
30080             return;
30081         }
30082         
30083         if(this.boxes > 0 && this.files.length > this.boxes){
30084             this.files = this.files.slice(0, this.boxes);
30085         }
30086         
30087         this.uploader.show();
30088         
30089         if(this.boxes > 0 && this.files.length > this.boxes - 1){
30090             this.uploader.hide();
30091         }
30092         
30093         var _this = this;
30094         
30095         var files = [];
30096         
30097         var docs = [];
30098         
30099         Roo.each(this.files, function(file){
30100             
30101             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30102                 var f = this.renderPreview(file);
30103                 files.push(f);
30104                 return;
30105             }
30106             
30107             if(file.type.indexOf('image') != -1){
30108                 this.delegates.push(
30109                     (function(){
30110                         _this.process(file);
30111                     }).createDelegate(this)
30112                 );
30113         
30114                 return;
30115             }
30116             
30117             docs.push(
30118                 (function(){
30119                     _this.process(file);
30120                 }).createDelegate(this)
30121             );
30122             
30123         }, this);
30124         
30125         this.files = files;
30126         
30127         this.delegates = this.delegates.concat(docs);
30128         
30129         if(!this.delegates.length){
30130             this.refresh();
30131             return;
30132         }
30133         
30134         this.progressBar.aria_valuemax = this.delegates.length;
30135         
30136         this.arrange();
30137         
30138         return;
30139     },
30140     
30141     arrange : function()
30142     {
30143         if(!this.delegates.length){
30144             this.progressDialog.hide();
30145             this.refresh();
30146             return;
30147         }
30148         
30149         var delegate = this.delegates.shift();
30150         
30151         this.progressDialog.show();
30152         
30153         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
30154         
30155         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
30156         
30157         delegate();
30158     },
30159     
30160     refresh : function()
30161     {
30162         this.uploader.show();
30163         
30164         if(this.boxes > 0 && this.files.length > this.boxes - 1){
30165             this.uploader.hide();
30166         }
30167         
30168         Roo.isTouch ? this.closable(false) : this.closable(true);
30169         
30170         this.fireEvent('refresh', this);
30171     },
30172     
30173     onRemove : function(e, el, o)
30174     {
30175         e.preventDefault();
30176         
30177         this.fireEvent('remove', this, o);
30178         
30179     },
30180     
30181     remove : function(o)
30182     {
30183         var files = [];
30184         
30185         Roo.each(this.files, function(file){
30186             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
30187                 files.push(file);
30188                 return;
30189             }
30190
30191             o.target.remove();
30192
30193         }, this);
30194         
30195         this.files = files;
30196         
30197         this.refresh();
30198     },
30199     
30200     clear : function()
30201     {
30202         Roo.each(this.files, function(file){
30203             if(!file.target){
30204                 return;
30205             }
30206             
30207             file.target.remove();
30208
30209         }, this);
30210         
30211         this.files = [];
30212         
30213         this.refresh();
30214     },
30215     
30216     onClick : function(e, el, o)
30217     {
30218         e.preventDefault();
30219         
30220         this.fireEvent('click', this, o);
30221         
30222     },
30223     
30224     closable : function(closable)
30225     {
30226         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
30227             
30228             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30229             
30230             if(closable){
30231                 el.show();
30232                 return;
30233             }
30234             
30235             el.hide();
30236             
30237         }, this);
30238     },
30239     
30240     xhrOnLoad : function(xhr)
30241     {
30242         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30243             el.remove();
30244         }, this);
30245         
30246         if (xhr.readyState !== 4) {
30247             this.arrange();
30248             this.fireEvent('exception', this, xhr);
30249             return;
30250         }
30251
30252         var response = Roo.decode(xhr.responseText);
30253         
30254         if(!response.success){
30255             this.arrange();
30256             this.fireEvent('exception', this, xhr);
30257             return;
30258         }
30259         
30260         var file = this.renderPreview(response.data);
30261         
30262         this.files.push(file);
30263         
30264         this.arrange();
30265         
30266         this.fireEvent('afterupload', this, xhr);
30267         
30268     },
30269     
30270     xhrOnError : function(xhr)
30271     {
30272         Roo.log('xhr on error');
30273         
30274         var response = Roo.decode(xhr.responseText);
30275           
30276         Roo.log(response);
30277         
30278         this.arrange();
30279     },
30280     
30281     process : function(file)
30282     {
30283         if(this.fireEvent('process', this, file) !== false){
30284             if(this.editable && file.type.indexOf('image') != -1){
30285                 this.fireEvent('edit', this, file);
30286                 return;
30287             }
30288
30289             this.uploadStart(file, false);
30290
30291             return;
30292         }
30293         
30294     },
30295     
30296     uploadStart : function(file, crop)
30297     {
30298         this.xhr = new XMLHttpRequest();
30299         
30300         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30301             this.arrange();
30302             return;
30303         }
30304         
30305         file.xhr = this.xhr;
30306             
30307         this.managerEl.createChild({
30308             tag : 'div',
30309             cls : 'roo-document-manager-loading',
30310             cn : [
30311                 {
30312                     tag : 'div',
30313                     tooltip : file.name,
30314                     cls : 'roo-document-manager-thumb',
30315                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30316                 }
30317             ]
30318
30319         });
30320
30321         this.xhr.open(this.method, this.url, true);
30322         
30323         var headers = {
30324             "Accept": "application/json",
30325             "Cache-Control": "no-cache",
30326             "X-Requested-With": "XMLHttpRequest"
30327         };
30328         
30329         for (var headerName in headers) {
30330             var headerValue = headers[headerName];
30331             if (headerValue) {
30332                 this.xhr.setRequestHeader(headerName, headerValue);
30333             }
30334         }
30335         
30336         var _this = this;
30337         
30338         this.xhr.onload = function()
30339         {
30340             _this.xhrOnLoad(_this.xhr);
30341         }
30342         
30343         this.xhr.onerror = function()
30344         {
30345             _this.xhrOnError(_this.xhr);
30346         }
30347         
30348         var formData = new FormData();
30349
30350         formData.append('returnHTML', 'NO');
30351         
30352         if(crop){
30353             formData.append('crop', crop);
30354         }
30355         
30356         formData.append(this.paramName, file, file.name);
30357         
30358         var options = {
30359             file : file, 
30360             manually : false
30361         };
30362         
30363         if(this.fireEvent('prepare', this, formData, options) != false){
30364             
30365             if(options.manually){
30366                 return;
30367             }
30368             
30369             this.xhr.send(formData);
30370             return;
30371         };
30372         
30373         this.uploadCancel();
30374     },
30375     
30376     uploadCancel : function()
30377     {
30378         if (this.xhr) {
30379             this.xhr.abort();
30380         }
30381         
30382         this.delegates = [];
30383         
30384         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30385             el.remove();
30386         }, this);
30387         
30388         this.arrange();
30389     },
30390     
30391     renderPreview : function(file)
30392     {
30393         if(typeof(file.target) != 'undefined' && file.target){
30394             return file;
30395         }
30396         
30397         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
30398         
30399         var previewEl = this.managerEl.createChild({
30400             tag : 'div',
30401             cls : 'roo-document-manager-preview',
30402             cn : [
30403                 {
30404                     tag : 'div',
30405                     tooltip : file[this.toolTipName],
30406                     cls : 'roo-document-manager-thumb',
30407                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
30408                 },
30409                 {
30410                     tag : 'button',
30411                     cls : 'close',
30412                     html : '<i class="fa fa-times-circle"></i>'
30413                 }
30414             ]
30415         });
30416
30417         var close = previewEl.select('button.close', true).first();
30418
30419         close.on('click', this.onRemove, this, file);
30420
30421         file.target = previewEl;
30422
30423         var image = previewEl.select('img', true).first();
30424         
30425         var _this = this;
30426         
30427         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
30428         
30429         image.on('click', this.onClick, this, file);
30430         
30431         this.fireEvent('previewrendered', this, file);
30432         
30433         return file;
30434         
30435     },
30436     
30437     onPreviewLoad : function(file, image)
30438     {
30439         if(typeof(file.target) == 'undefined' || !file.target){
30440             return;
30441         }
30442         
30443         var width = image.dom.naturalWidth || image.dom.width;
30444         var height = image.dom.naturalHeight || image.dom.height;
30445         
30446         if(!this.previewResize) {
30447             return;
30448         }
30449         
30450         if(width > height){
30451             file.target.addClass('wide');
30452             return;
30453         }
30454         
30455         file.target.addClass('tall');
30456         return;
30457         
30458     },
30459     
30460     uploadFromSource : function(file, crop)
30461     {
30462         this.xhr = new XMLHttpRequest();
30463         
30464         this.managerEl.createChild({
30465             tag : 'div',
30466             cls : 'roo-document-manager-loading',
30467             cn : [
30468                 {
30469                     tag : 'div',
30470                     tooltip : file.name,
30471                     cls : 'roo-document-manager-thumb',
30472                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30473                 }
30474             ]
30475
30476         });
30477
30478         this.xhr.open(this.method, this.url, true);
30479         
30480         var headers = {
30481             "Accept": "application/json",
30482             "Cache-Control": "no-cache",
30483             "X-Requested-With": "XMLHttpRequest"
30484         };
30485         
30486         for (var headerName in headers) {
30487             var headerValue = headers[headerName];
30488             if (headerValue) {
30489                 this.xhr.setRequestHeader(headerName, headerValue);
30490             }
30491         }
30492         
30493         var _this = this;
30494         
30495         this.xhr.onload = function()
30496         {
30497             _this.xhrOnLoad(_this.xhr);
30498         }
30499         
30500         this.xhr.onerror = function()
30501         {
30502             _this.xhrOnError(_this.xhr);
30503         }
30504         
30505         var formData = new FormData();
30506
30507         formData.append('returnHTML', 'NO');
30508         
30509         formData.append('crop', crop);
30510         
30511         if(typeof(file.filename) != 'undefined'){
30512             formData.append('filename', file.filename);
30513         }
30514         
30515         if(typeof(file.mimetype) != 'undefined'){
30516             formData.append('mimetype', file.mimetype);
30517         }
30518         
30519         Roo.log(formData);
30520         
30521         if(this.fireEvent('prepare', this, formData) != false){
30522             this.xhr.send(formData);
30523         };
30524     }
30525 });
30526
30527 /*
30528 * Licence: LGPL
30529 */
30530
30531 /**
30532  * @class Roo.bootstrap.DocumentViewer
30533  * @extends Roo.bootstrap.Component
30534  * Bootstrap DocumentViewer class
30535  * @cfg {Boolean} showDownload (true|false) show download button (default true)
30536  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30537  * 
30538  * @constructor
30539  * Create a new DocumentViewer
30540  * @param {Object} config The config object
30541  */
30542
30543 Roo.bootstrap.DocumentViewer = function(config){
30544     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30545     
30546     this.addEvents({
30547         /**
30548          * @event initial
30549          * Fire after initEvent
30550          * @param {Roo.bootstrap.DocumentViewer} this
30551          */
30552         "initial" : true,
30553         /**
30554          * @event click
30555          * Fire after click
30556          * @param {Roo.bootstrap.DocumentViewer} this
30557          */
30558         "click" : true,
30559         /**
30560          * @event download
30561          * Fire after download button
30562          * @param {Roo.bootstrap.DocumentViewer} this
30563          */
30564         "download" : true,
30565         /**
30566          * @event trash
30567          * Fire after trash button
30568          * @param {Roo.bootstrap.DocumentViewer} this
30569          */
30570         "trash" : true
30571         
30572     });
30573 };
30574
30575 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
30576     
30577     showDownload : true,
30578     
30579     showTrash : true,
30580     
30581     getAutoCreate : function()
30582     {
30583         var cfg = {
30584             tag : 'div',
30585             cls : 'roo-document-viewer',
30586             cn : [
30587                 {
30588                     tag : 'div',
30589                     cls : 'roo-document-viewer-body',
30590                     cn : [
30591                         {
30592                             tag : 'div',
30593                             cls : 'roo-document-viewer-thumb',
30594                             cn : [
30595                                 {
30596                                     tag : 'img',
30597                                     cls : 'roo-document-viewer-image'
30598                                 }
30599                             ]
30600                         }
30601                     ]
30602                 },
30603                 {
30604                     tag : 'div',
30605                     cls : 'roo-document-viewer-footer',
30606                     cn : {
30607                         tag : 'div',
30608                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
30609                         cn : [
30610                             {
30611                                 tag : 'div',
30612                                 cls : 'btn-group roo-document-viewer-download',
30613                                 cn : [
30614                                     {
30615                                         tag : 'button',
30616                                         cls : 'btn btn-default',
30617                                         html : '<i class="fa fa-download"></i>'
30618                                     }
30619                                 ]
30620                             },
30621                             {
30622                                 tag : 'div',
30623                                 cls : 'btn-group roo-document-viewer-trash',
30624                                 cn : [
30625                                     {
30626                                         tag : 'button',
30627                                         cls : 'btn btn-default',
30628                                         html : '<i class="fa fa-trash"></i>'
30629                                     }
30630                                 ]
30631                             }
30632                         ]
30633                     }
30634                 }
30635             ]
30636         };
30637         
30638         return cfg;
30639     },
30640     
30641     initEvents : function()
30642     {
30643         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30644         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30645         
30646         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30647         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30648         
30649         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30650         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30651         
30652         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30653         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30654         
30655         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30656         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30657         
30658         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30659         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30660         
30661         this.bodyEl.on('click', this.onClick, this);
30662         this.downloadBtn.on('click', this.onDownload, this);
30663         this.trashBtn.on('click', this.onTrash, this);
30664         
30665         this.downloadBtn.hide();
30666         this.trashBtn.hide();
30667         
30668         if(this.showDownload){
30669             this.downloadBtn.show();
30670         }
30671         
30672         if(this.showTrash){
30673             this.trashBtn.show();
30674         }
30675         
30676         if(!this.showDownload && !this.showTrash) {
30677             this.footerEl.hide();
30678         }
30679         
30680     },
30681     
30682     initial : function()
30683     {
30684         this.fireEvent('initial', this);
30685         
30686     },
30687     
30688     onClick : function(e)
30689     {
30690         e.preventDefault();
30691         
30692         this.fireEvent('click', this);
30693     },
30694     
30695     onDownload : function(e)
30696     {
30697         e.preventDefault();
30698         
30699         this.fireEvent('download', this);
30700     },
30701     
30702     onTrash : function(e)
30703     {
30704         e.preventDefault();
30705         
30706         this.fireEvent('trash', this);
30707     }
30708     
30709 });
30710 /*
30711  * - LGPL
30712  *
30713  * nav progress bar
30714  * 
30715  */
30716
30717 /**
30718  * @class Roo.bootstrap.NavProgressBar
30719  * @extends Roo.bootstrap.Component
30720  * Bootstrap NavProgressBar class
30721  * 
30722  * @constructor
30723  * Create a new nav progress bar
30724  * @param {Object} config The config object
30725  */
30726
30727 Roo.bootstrap.NavProgressBar = function(config){
30728     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30729
30730     this.bullets = this.bullets || [];
30731    
30732 //    Roo.bootstrap.NavProgressBar.register(this);
30733      this.addEvents({
30734         /**
30735              * @event changed
30736              * Fires when the active item changes
30737              * @param {Roo.bootstrap.NavProgressBar} this
30738              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30739              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30740          */
30741         'changed': true
30742      });
30743     
30744 };
30745
30746 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30747     
30748     bullets : [],
30749     barItems : [],
30750     
30751     getAutoCreate : function()
30752     {
30753         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30754         
30755         cfg = {
30756             tag : 'div',
30757             cls : 'roo-navigation-bar-group',
30758             cn : [
30759                 {
30760                     tag : 'div',
30761                     cls : 'roo-navigation-top-bar'
30762                 },
30763                 {
30764                     tag : 'div',
30765                     cls : 'roo-navigation-bullets-bar',
30766                     cn : [
30767                         {
30768                             tag : 'ul',
30769                             cls : 'roo-navigation-bar'
30770                         }
30771                     ]
30772                 },
30773                 
30774                 {
30775                     tag : 'div',
30776                     cls : 'roo-navigation-bottom-bar'
30777                 }
30778             ]
30779             
30780         };
30781         
30782         return cfg;
30783         
30784     },
30785     
30786     initEvents: function() 
30787     {
30788         
30789     },
30790     
30791     onRender : function(ct, position) 
30792     {
30793         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30794         
30795         if(this.bullets.length){
30796             Roo.each(this.bullets, function(b){
30797                this.addItem(b);
30798             }, this);
30799         }
30800         
30801         this.format();
30802         
30803     },
30804     
30805     addItem : function(cfg)
30806     {
30807         var item = new Roo.bootstrap.NavProgressItem(cfg);
30808         
30809         item.parentId = this.id;
30810         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30811         
30812         if(cfg.html){
30813             var top = new Roo.bootstrap.Element({
30814                 tag : 'div',
30815                 cls : 'roo-navigation-bar-text'
30816             });
30817             
30818             var bottom = new Roo.bootstrap.Element({
30819                 tag : 'div',
30820                 cls : 'roo-navigation-bar-text'
30821             });
30822             
30823             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30824             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30825             
30826             var topText = new Roo.bootstrap.Element({
30827                 tag : 'span',
30828                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30829             });
30830             
30831             var bottomText = new Roo.bootstrap.Element({
30832                 tag : 'span',
30833                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30834             });
30835             
30836             topText.onRender(top.el, null);
30837             bottomText.onRender(bottom.el, null);
30838             
30839             item.topEl = top;
30840             item.bottomEl = bottom;
30841         }
30842         
30843         this.barItems.push(item);
30844         
30845         return item;
30846     },
30847     
30848     getActive : function()
30849     {
30850         var active = false;
30851         
30852         Roo.each(this.barItems, function(v){
30853             
30854             if (!v.isActive()) {
30855                 return;
30856             }
30857             
30858             active = v;
30859             return false;
30860             
30861         });
30862         
30863         return active;
30864     },
30865     
30866     setActiveItem : function(item)
30867     {
30868         var prev = false;
30869         
30870         Roo.each(this.barItems, function(v){
30871             if (v.rid == item.rid) {
30872                 return ;
30873             }
30874             
30875             if (v.isActive()) {
30876                 v.setActive(false);
30877                 prev = v;
30878             }
30879         });
30880
30881         item.setActive(true);
30882         
30883         this.fireEvent('changed', this, item, prev);
30884     },
30885     
30886     getBarItem: function(rid)
30887     {
30888         var ret = false;
30889         
30890         Roo.each(this.barItems, function(e) {
30891             if (e.rid != rid) {
30892                 return;
30893             }
30894             
30895             ret =  e;
30896             return false;
30897         });
30898         
30899         return ret;
30900     },
30901     
30902     indexOfItem : function(item)
30903     {
30904         var index = false;
30905         
30906         Roo.each(this.barItems, function(v, i){
30907             
30908             if (v.rid != item.rid) {
30909                 return;
30910             }
30911             
30912             index = i;
30913             return false
30914         });
30915         
30916         return index;
30917     },
30918     
30919     setActiveNext : function()
30920     {
30921         var i = this.indexOfItem(this.getActive());
30922         
30923         if (i > this.barItems.length) {
30924             return;
30925         }
30926         
30927         this.setActiveItem(this.barItems[i+1]);
30928     },
30929     
30930     setActivePrev : function()
30931     {
30932         var i = this.indexOfItem(this.getActive());
30933         
30934         if (i  < 1) {
30935             return;
30936         }
30937         
30938         this.setActiveItem(this.barItems[i-1]);
30939     },
30940     
30941     format : function()
30942     {
30943         if(!this.barItems.length){
30944             return;
30945         }
30946      
30947         var width = 100 / this.barItems.length;
30948         
30949         Roo.each(this.barItems, function(i){
30950             i.el.setStyle('width', width + '%');
30951             i.topEl.el.setStyle('width', width + '%');
30952             i.bottomEl.el.setStyle('width', width + '%');
30953         }, this);
30954         
30955     }
30956     
30957 });
30958 /*
30959  * - LGPL
30960  *
30961  * Nav Progress Item
30962  * 
30963  */
30964
30965 /**
30966  * @class Roo.bootstrap.NavProgressItem
30967  * @extends Roo.bootstrap.Component
30968  * Bootstrap NavProgressItem class
30969  * @cfg {String} rid the reference id
30970  * @cfg {Boolean} active (true|false) Is item active default false
30971  * @cfg {Boolean} disabled (true|false) Is item active default false
30972  * @cfg {String} html
30973  * @cfg {String} position (top|bottom) text position default bottom
30974  * @cfg {String} icon show icon instead of number
30975  * 
30976  * @constructor
30977  * Create a new NavProgressItem
30978  * @param {Object} config The config object
30979  */
30980 Roo.bootstrap.NavProgressItem = function(config){
30981     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30982     this.addEvents({
30983         // raw events
30984         /**
30985          * @event click
30986          * The raw click event for the entire grid.
30987          * @param {Roo.bootstrap.NavProgressItem} this
30988          * @param {Roo.EventObject} e
30989          */
30990         "click" : true
30991     });
30992    
30993 };
30994
30995 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30996     
30997     rid : '',
30998     active : false,
30999     disabled : false,
31000     html : '',
31001     position : 'bottom',
31002     icon : false,
31003     
31004     getAutoCreate : function()
31005     {
31006         var iconCls = 'roo-navigation-bar-item-icon';
31007         
31008         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
31009         
31010         var cfg = {
31011             tag: 'li',
31012             cls: 'roo-navigation-bar-item',
31013             cn : [
31014                 {
31015                     tag : 'i',
31016                     cls : iconCls
31017                 }
31018             ]
31019         };
31020         
31021         if(this.active){
31022             cfg.cls += ' active';
31023         }
31024         if(this.disabled){
31025             cfg.cls += ' disabled';
31026         }
31027         
31028         return cfg;
31029     },
31030     
31031     disable : function()
31032     {
31033         this.setDisabled(true);
31034     },
31035     
31036     enable : function()
31037     {
31038         this.setDisabled(false);
31039     },
31040     
31041     initEvents: function() 
31042     {
31043         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
31044         
31045         this.iconEl.on('click', this.onClick, this);
31046     },
31047     
31048     onClick : function(e)
31049     {
31050         e.preventDefault();
31051         
31052         if(this.disabled){
31053             return;
31054         }
31055         
31056         if(this.fireEvent('click', this, e) === false){
31057             return;
31058         };
31059         
31060         this.parent().setActiveItem(this);
31061     },
31062     
31063     isActive: function () 
31064     {
31065         return this.active;
31066     },
31067     
31068     setActive : function(state)
31069     {
31070         if(this.active == state){
31071             return;
31072         }
31073         
31074         this.active = state;
31075         
31076         if (state) {
31077             this.el.addClass('active');
31078             return;
31079         }
31080         
31081         this.el.removeClass('active');
31082         
31083         return;
31084     },
31085     
31086     setDisabled : function(state)
31087     {
31088         if(this.disabled == state){
31089             return;
31090         }
31091         
31092         this.disabled = state;
31093         
31094         if (state) {
31095             this.el.addClass('disabled');
31096             return;
31097         }
31098         
31099         this.el.removeClass('disabled');
31100     },
31101     
31102     tooltipEl : function()
31103     {
31104         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
31105     }
31106 });
31107  
31108
31109  /*
31110  * - LGPL
31111  *
31112  * FieldLabel
31113  * 
31114  */
31115
31116 /**
31117  * @class Roo.bootstrap.FieldLabel
31118  * @extends Roo.bootstrap.Component
31119  * Bootstrap FieldLabel class
31120  * @cfg {String} html contents of the element
31121  * @cfg {String} tag tag of the element default label
31122  * @cfg {String} cls class of the element
31123  * @cfg {String} target label target 
31124  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
31125  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
31126  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
31127  * @cfg {String} iconTooltip default "This field is required"
31128  * @cfg {String} indicatorpos (left|right) default left
31129  * 
31130  * @constructor
31131  * Create a new FieldLabel
31132  * @param {Object} config The config object
31133  */
31134
31135 Roo.bootstrap.FieldLabel = function(config){
31136     Roo.bootstrap.Element.superclass.constructor.call(this, config);
31137     
31138     this.addEvents({
31139             /**
31140              * @event invalid
31141              * Fires after the field has been marked as invalid.
31142              * @param {Roo.form.FieldLabel} this
31143              * @param {String} msg The validation message
31144              */
31145             invalid : true,
31146             /**
31147              * @event valid
31148              * Fires after the field has been validated with no errors.
31149              * @param {Roo.form.FieldLabel} this
31150              */
31151             valid : true
31152         });
31153 };
31154
31155 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
31156     
31157     tag: 'label',
31158     cls: '',
31159     html: '',
31160     target: '',
31161     allowBlank : true,
31162     invalidClass : 'has-warning',
31163     validClass : 'has-success',
31164     iconTooltip : 'This field is required',
31165     indicatorpos : 'left',
31166     
31167     getAutoCreate : function(){
31168         
31169         var cls = "";
31170         if (!this.allowBlank) {
31171             cls  = "visible";
31172         }
31173         
31174         var cfg = {
31175             tag : this.tag,
31176             cls : 'roo-bootstrap-field-label ' + this.cls,
31177             for : this.target,
31178             cn : [
31179                 {
31180                     tag : 'i',
31181                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
31182                     tooltip : this.iconTooltip
31183                 },
31184                 {
31185                     tag : 'span',
31186                     html : this.html
31187                 }
31188             ] 
31189         };
31190         
31191         if(this.indicatorpos == 'right'){
31192             var cfg = {
31193                 tag : this.tag,
31194                 cls : 'roo-bootstrap-field-label ' + this.cls,
31195                 for : this.target,
31196                 cn : [
31197                     {
31198                         tag : 'span',
31199                         html : this.html
31200                     },
31201                     {
31202                         tag : 'i',
31203                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
31204                         tooltip : this.iconTooltip
31205                     }
31206                 ] 
31207             };
31208         }
31209         
31210         return cfg;
31211     },
31212     
31213     initEvents: function() 
31214     {
31215         Roo.bootstrap.Element.superclass.initEvents.call(this);
31216         
31217         this.indicator = this.indicatorEl();
31218         
31219         if(this.indicator){
31220             this.indicator.removeClass('visible');
31221             this.indicator.addClass('invisible');
31222         }
31223         
31224         Roo.bootstrap.FieldLabel.register(this);
31225     },
31226     
31227     indicatorEl : function()
31228     {
31229         var indicator = this.el.select('i.roo-required-indicator',true).first();
31230         
31231         if(!indicator){
31232             return false;
31233         }
31234         
31235         return indicator;
31236         
31237     },
31238     
31239     /**
31240      * Mark this field as valid
31241      */
31242     markValid : function()
31243     {
31244         if(this.indicator){
31245             this.indicator.removeClass('visible');
31246             this.indicator.addClass('invisible');
31247         }
31248         if (Roo.bootstrap.version == 3) {
31249             this.el.removeClass(this.invalidClass);
31250             this.el.addClass(this.validClass);
31251         } else {
31252             this.el.removeClass('is-invalid');
31253             this.el.addClass('is-valid');
31254         }
31255         
31256         
31257         this.fireEvent('valid', this);
31258     },
31259     
31260     /**
31261      * Mark this field as invalid
31262      * @param {String} msg The validation message
31263      */
31264     markInvalid : function(msg)
31265     {
31266         if(this.indicator){
31267             this.indicator.removeClass('invisible');
31268             this.indicator.addClass('visible');
31269         }
31270           if (Roo.bootstrap.version == 3) {
31271             this.el.removeClass(this.validClass);
31272             this.el.addClass(this.invalidClass);
31273         } else {
31274             this.el.removeClass('is-valid');
31275             this.el.addClass('is-invalid');
31276         }
31277         
31278         
31279         this.fireEvent('invalid', this, msg);
31280     }
31281     
31282    
31283 });
31284
31285 Roo.apply(Roo.bootstrap.FieldLabel, {
31286     
31287     groups: {},
31288     
31289      /**
31290     * register a FieldLabel Group
31291     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
31292     */
31293     register : function(label)
31294     {
31295         if(this.groups.hasOwnProperty(label.target)){
31296             return;
31297         }
31298      
31299         this.groups[label.target] = label;
31300         
31301     },
31302     /**
31303     * fetch a FieldLabel Group based on the target
31304     * @param {string} target
31305     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
31306     */
31307     get: function(target) {
31308         if (typeof(this.groups[target]) == 'undefined') {
31309             return false;
31310         }
31311         
31312         return this.groups[target] ;
31313     }
31314 });
31315
31316  
31317
31318  /*
31319  * - LGPL
31320  *
31321  * page DateSplitField.
31322  * 
31323  */
31324
31325
31326 /**
31327  * @class Roo.bootstrap.DateSplitField
31328  * @extends Roo.bootstrap.Component
31329  * Bootstrap DateSplitField class
31330  * @cfg {string} fieldLabel - the label associated
31331  * @cfg {Number} labelWidth set the width of label (0-12)
31332  * @cfg {String} labelAlign (top|left)
31333  * @cfg {Boolean} dayAllowBlank (true|false) default false
31334  * @cfg {Boolean} monthAllowBlank (true|false) default false
31335  * @cfg {Boolean} yearAllowBlank (true|false) default false
31336  * @cfg {string} dayPlaceholder 
31337  * @cfg {string} monthPlaceholder
31338  * @cfg {string} yearPlaceholder
31339  * @cfg {string} dayFormat default 'd'
31340  * @cfg {string} monthFormat default 'm'
31341  * @cfg {string} yearFormat default 'Y'
31342  * @cfg {Number} labellg set the width of label (1-12)
31343  * @cfg {Number} labelmd set the width of label (1-12)
31344  * @cfg {Number} labelsm set the width of label (1-12)
31345  * @cfg {Number} labelxs set the width of label (1-12)
31346
31347  *     
31348  * @constructor
31349  * Create a new DateSplitField
31350  * @param {Object} config The config object
31351  */
31352
31353 Roo.bootstrap.DateSplitField = function(config){
31354     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
31355     
31356     this.addEvents({
31357         // raw events
31358          /**
31359          * @event years
31360          * getting the data of years
31361          * @param {Roo.bootstrap.DateSplitField} this
31362          * @param {Object} years
31363          */
31364         "years" : true,
31365         /**
31366          * @event days
31367          * getting the data of days
31368          * @param {Roo.bootstrap.DateSplitField} this
31369          * @param {Object} days
31370          */
31371         "days" : true,
31372         /**
31373          * @event invalid
31374          * Fires after the field has been marked as invalid.
31375          * @param {Roo.form.Field} this
31376          * @param {String} msg The validation message
31377          */
31378         invalid : true,
31379        /**
31380          * @event valid
31381          * Fires after the field has been validated with no errors.
31382          * @param {Roo.form.Field} this
31383          */
31384         valid : true
31385     });
31386 };
31387
31388 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
31389     
31390     fieldLabel : '',
31391     labelAlign : 'top',
31392     labelWidth : 3,
31393     dayAllowBlank : false,
31394     monthAllowBlank : false,
31395     yearAllowBlank : false,
31396     dayPlaceholder : '',
31397     monthPlaceholder : '',
31398     yearPlaceholder : '',
31399     dayFormat : 'd',
31400     monthFormat : 'm',
31401     yearFormat : 'Y',
31402     isFormField : true,
31403     labellg : 0,
31404     labelmd : 0,
31405     labelsm : 0,
31406     labelxs : 0,
31407     
31408     getAutoCreate : function()
31409     {
31410         var cfg = {
31411             tag : 'div',
31412             cls : 'row roo-date-split-field-group',
31413             cn : [
31414                 {
31415                     tag : 'input',
31416                     type : 'hidden',
31417                     cls : 'form-hidden-field roo-date-split-field-group-value',
31418                     name : this.name
31419                 }
31420             ]
31421         };
31422         
31423         var labelCls = 'col-md-12';
31424         var contentCls = 'col-md-4';
31425         
31426         if(this.fieldLabel){
31427             
31428             var label = {
31429                 tag : 'div',
31430                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
31431                 cn : [
31432                     {
31433                         tag : 'label',
31434                         html : this.fieldLabel
31435                     }
31436                 ]
31437             };
31438             
31439             if(this.labelAlign == 'left'){
31440             
31441                 if(this.labelWidth > 12){
31442                     label.style = "width: " + this.labelWidth + 'px';
31443                 }
31444
31445                 if(this.labelWidth < 13 && this.labelmd == 0){
31446                     this.labelmd = this.labelWidth;
31447                 }
31448
31449                 if(this.labellg > 0){
31450                     labelCls = ' col-lg-' + this.labellg;
31451                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
31452                 }
31453
31454                 if(this.labelmd > 0){
31455                     labelCls = ' col-md-' + this.labelmd;
31456                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
31457                 }
31458
31459                 if(this.labelsm > 0){
31460                     labelCls = ' col-sm-' + this.labelsm;
31461                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
31462                 }
31463
31464                 if(this.labelxs > 0){
31465                     labelCls = ' col-xs-' + this.labelxs;
31466                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
31467                 }
31468             }
31469             
31470             label.cls += ' ' + labelCls;
31471             
31472             cfg.cn.push(label);
31473         }
31474         
31475         Roo.each(['day', 'month', 'year'], function(t){
31476             cfg.cn.push({
31477                 tag : 'div',
31478                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
31479             });
31480         }, this);
31481         
31482         return cfg;
31483     },
31484     
31485     inputEl: function ()
31486     {
31487         return this.el.select('.roo-date-split-field-group-value', true).first();
31488     },
31489     
31490     onRender : function(ct, position) 
31491     {
31492         var _this = this;
31493         
31494         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31495         
31496         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
31497         
31498         this.dayField = new Roo.bootstrap.ComboBox({
31499             allowBlank : this.dayAllowBlank,
31500             alwaysQuery : true,
31501             displayField : 'value',
31502             editable : false,
31503             fieldLabel : '',
31504             forceSelection : true,
31505             mode : 'local',
31506             placeholder : this.dayPlaceholder,
31507             selectOnFocus : true,
31508             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31509             triggerAction : 'all',
31510             typeAhead : true,
31511             valueField : 'value',
31512             store : new Roo.data.SimpleStore({
31513                 data : (function() {    
31514                     var days = [];
31515                     _this.fireEvent('days', _this, days);
31516                     return days;
31517                 })(),
31518                 fields : [ 'value' ]
31519             }),
31520             listeners : {
31521                 select : function (_self, record, index)
31522                 {
31523                     _this.setValue(_this.getValue());
31524                 }
31525             }
31526         });
31527
31528         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
31529         
31530         this.monthField = new Roo.bootstrap.MonthField({
31531             after : '<i class=\"fa fa-calendar\"></i>',
31532             allowBlank : this.monthAllowBlank,
31533             placeholder : this.monthPlaceholder,
31534             readOnly : true,
31535             listeners : {
31536                 render : function (_self)
31537                 {
31538                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
31539                         e.preventDefault();
31540                         _self.focus();
31541                     });
31542                 },
31543                 select : function (_self, oldvalue, newvalue)
31544                 {
31545                     _this.setValue(_this.getValue());
31546                 }
31547             }
31548         });
31549         
31550         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31551         
31552         this.yearField = new Roo.bootstrap.ComboBox({
31553             allowBlank : this.yearAllowBlank,
31554             alwaysQuery : true,
31555             displayField : 'value',
31556             editable : false,
31557             fieldLabel : '',
31558             forceSelection : true,
31559             mode : 'local',
31560             placeholder : this.yearPlaceholder,
31561             selectOnFocus : true,
31562             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31563             triggerAction : 'all',
31564             typeAhead : true,
31565             valueField : 'value',
31566             store : new Roo.data.SimpleStore({
31567                 data : (function() {
31568                     var years = [];
31569                     _this.fireEvent('years', _this, years);
31570                     return years;
31571                 })(),
31572                 fields : [ 'value' ]
31573             }),
31574             listeners : {
31575                 select : function (_self, record, index)
31576                 {
31577                     _this.setValue(_this.getValue());
31578                 }
31579             }
31580         });
31581
31582         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
31583     },
31584     
31585     setValue : function(v, format)
31586     {
31587         this.inputEl.dom.value = v;
31588         
31589         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31590         
31591         var d = Date.parseDate(v, f);
31592         
31593         if(!d){
31594             this.validate();
31595             return;
31596         }
31597         
31598         this.setDay(d.format(this.dayFormat));
31599         this.setMonth(d.format(this.monthFormat));
31600         this.setYear(d.format(this.yearFormat));
31601         
31602         this.validate();
31603         
31604         return;
31605     },
31606     
31607     setDay : function(v)
31608     {
31609         this.dayField.setValue(v);
31610         this.inputEl.dom.value = this.getValue();
31611         this.validate();
31612         return;
31613     },
31614     
31615     setMonth : function(v)
31616     {
31617         this.monthField.setValue(v, true);
31618         this.inputEl.dom.value = this.getValue();
31619         this.validate();
31620         return;
31621     },
31622     
31623     setYear : function(v)
31624     {
31625         this.yearField.setValue(v);
31626         this.inputEl.dom.value = this.getValue();
31627         this.validate();
31628         return;
31629     },
31630     
31631     getDay : function()
31632     {
31633         return this.dayField.getValue();
31634     },
31635     
31636     getMonth : function()
31637     {
31638         return this.monthField.getValue();
31639     },
31640     
31641     getYear : function()
31642     {
31643         return this.yearField.getValue();
31644     },
31645     
31646     getValue : function()
31647     {
31648         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31649         
31650         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31651         
31652         return date;
31653     },
31654     
31655     reset : function()
31656     {
31657         this.setDay('');
31658         this.setMonth('');
31659         this.setYear('');
31660         this.inputEl.dom.value = '';
31661         this.validate();
31662         return;
31663     },
31664     
31665     validate : function()
31666     {
31667         var d = this.dayField.validate();
31668         var m = this.monthField.validate();
31669         var y = this.yearField.validate();
31670         
31671         var valid = true;
31672         
31673         if(
31674                 (!this.dayAllowBlank && !d) ||
31675                 (!this.monthAllowBlank && !m) ||
31676                 (!this.yearAllowBlank && !y)
31677         ){
31678             valid = false;
31679         }
31680         
31681         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31682             return valid;
31683         }
31684         
31685         if(valid){
31686             this.markValid();
31687             return valid;
31688         }
31689         
31690         this.markInvalid();
31691         
31692         return valid;
31693     },
31694     
31695     markValid : function()
31696     {
31697         
31698         var label = this.el.select('label', true).first();
31699         var icon = this.el.select('i.fa-star', true).first();
31700
31701         if(label && icon){
31702             icon.remove();
31703         }
31704         
31705         this.fireEvent('valid', this);
31706     },
31707     
31708      /**
31709      * Mark this field as invalid
31710      * @param {String} msg The validation message
31711      */
31712     markInvalid : function(msg)
31713     {
31714         
31715         var label = this.el.select('label', true).first();
31716         var icon = this.el.select('i.fa-star', true).first();
31717
31718         if(label && !icon){
31719             this.el.select('.roo-date-split-field-label', true).createChild({
31720                 tag : 'i',
31721                 cls : 'text-danger fa fa-lg fa-star',
31722                 tooltip : 'This field is required',
31723                 style : 'margin-right:5px;'
31724             }, label, true);
31725         }
31726         
31727         this.fireEvent('invalid', this, msg);
31728     },
31729     
31730     clearInvalid : function()
31731     {
31732         var label = this.el.select('label', true).first();
31733         var icon = this.el.select('i.fa-star', true).first();
31734
31735         if(label && icon){
31736             icon.remove();
31737         }
31738         
31739         this.fireEvent('valid', this);
31740     },
31741     
31742     getName: function()
31743     {
31744         return this.name;
31745     }
31746     
31747 });
31748
31749  /**
31750  *
31751  * This is based on 
31752  * http://masonry.desandro.com
31753  *
31754  * The idea is to render all the bricks based on vertical width...
31755  *
31756  * The original code extends 'outlayer' - we might need to use that....
31757  * 
31758  */
31759
31760
31761 /**
31762  * @class Roo.bootstrap.LayoutMasonry
31763  * @extends Roo.bootstrap.Component
31764  * Bootstrap Layout Masonry class
31765  * 
31766  * @constructor
31767  * Create a new Element
31768  * @param {Object} config The config object
31769  */
31770
31771 Roo.bootstrap.LayoutMasonry = function(config){
31772     
31773     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31774     
31775     this.bricks = [];
31776     
31777     Roo.bootstrap.LayoutMasonry.register(this);
31778     
31779     this.addEvents({
31780         // raw events
31781         /**
31782          * @event layout
31783          * Fire after layout the items
31784          * @param {Roo.bootstrap.LayoutMasonry} this
31785          * @param {Roo.EventObject} e
31786          */
31787         "layout" : true
31788     });
31789     
31790 };
31791
31792 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31793     
31794     /**
31795      * @cfg {Boolean} isLayoutInstant = no animation?
31796      */   
31797     isLayoutInstant : false, // needed?
31798    
31799     /**
31800      * @cfg {Number} boxWidth  width of the columns
31801      */   
31802     boxWidth : 450,
31803     
31804       /**
31805      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31806      */   
31807     boxHeight : 0,
31808     
31809     /**
31810      * @cfg {Number} padWidth padding below box..
31811      */   
31812     padWidth : 10, 
31813     
31814     /**
31815      * @cfg {Number} gutter gutter width..
31816      */   
31817     gutter : 10,
31818     
31819      /**
31820      * @cfg {Number} maxCols maximum number of columns
31821      */   
31822     
31823     maxCols: 0,
31824     
31825     /**
31826      * @cfg {Boolean} isAutoInitial defalut true
31827      */   
31828     isAutoInitial : true, 
31829     
31830     containerWidth: 0,
31831     
31832     /**
31833      * @cfg {Boolean} isHorizontal defalut false
31834      */   
31835     isHorizontal : false, 
31836
31837     currentSize : null,
31838     
31839     tag: 'div',
31840     
31841     cls: '',
31842     
31843     bricks: null, //CompositeElement
31844     
31845     cols : 1,
31846     
31847     _isLayoutInited : false,
31848     
31849 //    isAlternative : false, // only use for vertical layout...
31850     
31851     /**
31852      * @cfg {Number} alternativePadWidth padding below box..
31853      */   
31854     alternativePadWidth : 50,
31855     
31856     selectedBrick : [],
31857     
31858     getAutoCreate : function(){
31859         
31860         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31861         
31862         var cfg = {
31863             tag: this.tag,
31864             cls: 'blog-masonary-wrapper ' + this.cls,
31865             cn : {
31866                 cls : 'mas-boxes masonary'
31867             }
31868         };
31869         
31870         return cfg;
31871     },
31872     
31873     getChildContainer: function( )
31874     {
31875         if (this.boxesEl) {
31876             return this.boxesEl;
31877         }
31878         
31879         this.boxesEl = this.el.select('.mas-boxes').first();
31880         
31881         return this.boxesEl;
31882     },
31883     
31884     
31885     initEvents : function()
31886     {
31887         var _this = this;
31888         
31889         if(this.isAutoInitial){
31890             Roo.log('hook children rendered');
31891             this.on('childrenrendered', function() {
31892                 Roo.log('children rendered');
31893                 _this.initial();
31894             } ,this);
31895         }
31896     },
31897     
31898     initial : function()
31899     {
31900         this.selectedBrick = [];
31901         
31902         this.currentSize = this.el.getBox(true);
31903         
31904         Roo.EventManager.onWindowResize(this.resize, this); 
31905
31906         if(!this.isAutoInitial){
31907             this.layout();
31908             return;
31909         }
31910         
31911         this.layout();
31912         
31913         return;
31914         //this.layout.defer(500,this);
31915         
31916     },
31917     
31918     resize : function()
31919     {
31920         var cs = this.el.getBox(true);
31921         
31922         if (
31923                 this.currentSize.width == cs.width && 
31924                 this.currentSize.x == cs.x && 
31925                 this.currentSize.height == cs.height && 
31926                 this.currentSize.y == cs.y 
31927         ) {
31928             Roo.log("no change in with or X or Y");
31929             return;
31930         }
31931         
31932         this.currentSize = cs;
31933         
31934         this.layout();
31935         
31936     },
31937     
31938     layout : function()
31939     {   
31940         this._resetLayout();
31941         
31942         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31943         
31944         this.layoutItems( isInstant );
31945       
31946         this._isLayoutInited = true;
31947         
31948         this.fireEvent('layout', this);
31949         
31950     },
31951     
31952     _resetLayout : function()
31953     {
31954         if(this.isHorizontal){
31955             this.horizontalMeasureColumns();
31956             return;
31957         }
31958         
31959         this.verticalMeasureColumns();
31960         
31961     },
31962     
31963     verticalMeasureColumns : function()
31964     {
31965         this.getContainerWidth();
31966         
31967 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31968 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31969 //            return;
31970 //        }
31971         
31972         var boxWidth = this.boxWidth + this.padWidth;
31973         
31974         if(this.containerWidth < this.boxWidth){
31975             boxWidth = this.containerWidth
31976         }
31977         
31978         var containerWidth = this.containerWidth;
31979         
31980         var cols = Math.floor(containerWidth / boxWidth);
31981         
31982         this.cols = Math.max( cols, 1 );
31983         
31984         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31985         
31986         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31987         
31988         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31989         
31990         this.colWidth = boxWidth + avail - this.padWidth;
31991         
31992         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31993         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31994     },
31995     
31996     horizontalMeasureColumns : function()
31997     {
31998         this.getContainerWidth();
31999         
32000         var boxWidth = this.boxWidth;
32001         
32002         if(this.containerWidth < boxWidth){
32003             boxWidth = this.containerWidth;
32004         }
32005         
32006         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
32007         
32008         this.el.setHeight(boxWidth);
32009         
32010     },
32011     
32012     getContainerWidth : function()
32013     {
32014         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32015     },
32016     
32017     layoutItems : function( isInstant )
32018     {
32019         Roo.log(this.bricks);
32020         
32021         var items = Roo.apply([], this.bricks);
32022         
32023         if(this.isHorizontal){
32024             this._horizontalLayoutItems( items , isInstant );
32025             return;
32026         }
32027         
32028 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
32029 //            this._verticalAlternativeLayoutItems( items , isInstant );
32030 //            return;
32031 //        }
32032         
32033         this._verticalLayoutItems( items , isInstant );
32034         
32035     },
32036     
32037     _verticalLayoutItems : function ( items , isInstant)
32038     {
32039         if ( !items || !items.length ) {
32040             return;
32041         }
32042         
32043         var standard = [
32044             ['xs', 'xs', 'xs', 'tall'],
32045             ['xs', 'xs', 'tall'],
32046             ['xs', 'xs', 'sm'],
32047             ['xs', 'xs', 'xs'],
32048             ['xs', 'tall'],
32049             ['xs', 'sm'],
32050             ['xs', 'xs'],
32051             ['xs'],
32052             
32053             ['sm', 'xs', 'xs'],
32054             ['sm', 'xs'],
32055             ['sm'],
32056             
32057             ['tall', 'xs', 'xs', 'xs'],
32058             ['tall', 'xs', 'xs'],
32059             ['tall', 'xs'],
32060             ['tall']
32061             
32062         ];
32063         
32064         var queue = [];
32065         
32066         var boxes = [];
32067         
32068         var box = [];
32069         
32070         Roo.each(items, function(item, k){
32071             
32072             switch (item.size) {
32073                 // these layouts take up a full box,
32074                 case 'md' :
32075                 case 'md-left' :
32076                 case 'md-right' :
32077                 case 'wide' :
32078                     
32079                     if(box.length){
32080                         boxes.push(box);
32081                         box = [];
32082                     }
32083                     
32084                     boxes.push([item]);
32085                     
32086                     break;
32087                     
32088                 case 'xs' :
32089                 case 'sm' :
32090                 case 'tall' :
32091                     
32092                     box.push(item);
32093                     
32094                     break;
32095                 default :
32096                     break;
32097                     
32098             }
32099             
32100         }, this);
32101         
32102         if(box.length){
32103             boxes.push(box);
32104             box = [];
32105         }
32106         
32107         var filterPattern = function(box, length)
32108         {
32109             if(!box.length){
32110                 return;
32111             }
32112             
32113             var match = false;
32114             
32115             var pattern = box.slice(0, length);
32116             
32117             var format = [];
32118             
32119             Roo.each(pattern, function(i){
32120                 format.push(i.size);
32121             }, this);
32122             
32123             Roo.each(standard, function(s){
32124                 
32125                 if(String(s) != String(format)){
32126                     return;
32127                 }
32128                 
32129                 match = true;
32130                 return false;
32131                 
32132             }, this);
32133             
32134             if(!match && length == 1){
32135                 return;
32136             }
32137             
32138             if(!match){
32139                 filterPattern(box, length - 1);
32140                 return;
32141             }
32142                 
32143             queue.push(pattern);
32144
32145             box = box.slice(length, box.length);
32146
32147             filterPattern(box, 4);
32148
32149             return;
32150             
32151         }
32152         
32153         Roo.each(boxes, function(box, k){
32154             
32155             if(!box.length){
32156                 return;
32157             }
32158             
32159             if(box.length == 1){
32160                 queue.push(box);
32161                 return;
32162             }
32163             
32164             filterPattern(box, 4);
32165             
32166         }, this);
32167         
32168         this._processVerticalLayoutQueue( queue, isInstant );
32169         
32170     },
32171     
32172 //    _verticalAlternativeLayoutItems : function( items , isInstant )
32173 //    {
32174 //        if ( !items || !items.length ) {
32175 //            return;
32176 //        }
32177 //
32178 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
32179 //        
32180 //    },
32181     
32182     _horizontalLayoutItems : function ( items , isInstant)
32183     {
32184         if ( !items || !items.length || items.length < 3) {
32185             return;
32186         }
32187         
32188         items.reverse();
32189         
32190         var eItems = items.slice(0, 3);
32191         
32192         items = items.slice(3, items.length);
32193         
32194         var standard = [
32195             ['xs', 'xs', 'xs', 'wide'],
32196             ['xs', 'xs', 'wide'],
32197             ['xs', 'xs', 'sm'],
32198             ['xs', 'xs', 'xs'],
32199             ['xs', 'wide'],
32200             ['xs', 'sm'],
32201             ['xs', 'xs'],
32202             ['xs'],
32203             
32204             ['sm', 'xs', 'xs'],
32205             ['sm', 'xs'],
32206             ['sm'],
32207             
32208             ['wide', 'xs', 'xs', 'xs'],
32209             ['wide', 'xs', 'xs'],
32210             ['wide', 'xs'],
32211             ['wide'],
32212             
32213             ['wide-thin']
32214         ];
32215         
32216         var queue = [];
32217         
32218         var boxes = [];
32219         
32220         var box = [];
32221         
32222         Roo.each(items, function(item, k){
32223             
32224             switch (item.size) {
32225                 case 'md' :
32226                 case 'md-left' :
32227                 case 'md-right' :
32228                 case 'tall' :
32229                     
32230                     if(box.length){
32231                         boxes.push(box);
32232                         box = [];
32233                     }
32234                     
32235                     boxes.push([item]);
32236                     
32237                     break;
32238                     
32239                 case 'xs' :
32240                 case 'sm' :
32241                 case 'wide' :
32242                 case 'wide-thin' :
32243                     
32244                     box.push(item);
32245                     
32246                     break;
32247                 default :
32248                     break;
32249                     
32250             }
32251             
32252         }, this);
32253         
32254         if(box.length){
32255             boxes.push(box);
32256             box = [];
32257         }
32258         
32259         var filterPattern = function(box, length)
32260         {
32261             if(!box.length){
32262                 return;
32263             }
32264             
32265             var match = false;
32266             
32267             var pattern = box.slice(0, length);
32268             
32269             var format = [];
32270             
32271             Roo.each(pattern, function(i){
32272                 format.push(i.size);
32273             }, this);
32274             
32275             Roo.each(standard, function(s){
32276                 
32277                 if(String(s) != String(format)){
32278                     return;
32279                 }
32280                 
32281                 match = true;
32282                 return false;
32283                 
32284             }, this);
32285             
32286             if(!match && length == 1){
32287                 return;
32288             }
32289             
32290             if(!match){
32291                 filterPattern(box, length - 1);
32292                 return;
32293             }
32294                 
32295             queue.push(pattern);
32296
32297             box = box.slice(length, box.length);
32298
32299             filterPattern(box, 4);
32300
32301             return;
32302             
32303         }
32304         
32305         Roo.each(boxes, function(box, k){
32306             
32307             if(!box.length){
32308                 return;
32309             }
32310             
32311             if(box.length == 1){
32312                 queue.push(box);
32313                 return;
32314             }
32315             
32316             filterPattern(box, 4);
32317             
32318         }, this);
32319         
32320         
32321         var prune = [];
32322         
32323         var pos = this.el.getBox(true);
32324         
32325         var minX = pos.x;
32326         
32327         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32328         
32329         var hit_end = false;
32330         
32331         Roo.each(queue, function(box){
32332             
32333             if(hit_end){
32334                 
32335                 Roo.each(box, function(b){
32336                 
32337                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
32338                     b.el.hide();
32339
32340                 }, this);
32341
32342                 return;
32343             }
32344             
32345             var mx = 0;
32346             
32347             Roo.each(box, function(b){
32348                 
32349                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32350                 b.el.show();
32351
32352                 mx = Math.max(mx, b.x);
32353                 
32354             }, this);
32355             
32356             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
32357             
32358             if(maxX < minX){
32359                 
32360                 Roo.each(box, function(b){
32361                 
32362                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
32363                     b.el.hide();
32364                     
32365                 }, this);
32366                 
32367                 hit_end = true;
32368                 
32369                 return;
32370             }
32371             
32372             prune.push(box);
32373             
32374         }, this);
32375         
32376         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
32377     },
32378     
32379     /** Sets position of item in DOM
32380     * @param {Element} item
32381     * @param {Number} x - horizontal position
32382     * @param {Number} y - vertical position
32383     * @param {Boolean} isInstant - disables transitions
32384     */
32385     _processVerticalLayoutQueue : function( queue, isInstant )
32386     {
32387         var pos = this.el.getBox(true);
32388         var x = pos.x;
32389         var y = pos.y;
32390         var maxY = [];
32391         
32392         for (var i = 0; i < this.cols; i++){
32393             maxY[i] = pos.y;
32394         }
32395         
32396         Roo.each(queue, function(box, k){
32397             
32398             var col = k % this.cols;
32399             
32400             Roo.each(box, function(b,kk){
32401                 
32402                 b.el.position('absolute');
32403                 
32404                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32405                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32406                 
32407                 if(b.size == 'md-left' || b.size == 'md-right'){
32408                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32409                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32410                 }
32411                 
32412                 b.el.setWidth(width);
32413                 b.el.setHeight(height);
32414                 // iframe?
32415                 b.el.select('iframe',true).setSize(width,height);
32416                 
32417             }, this);
32418             
32419             for (var i = 0; i < this.cols; i++){
32420                 
32421                 if(maxY[i] < maxY[col]){
32422                     col = i;
32423                     continue;
32424                 }
32425                 
32426                 col = Math.min(col, i);
32427                 
32428             }
32429             
32430             x = pos.x + col * (this.colWidth + this.padWidth);
32431             
32432             y = maxY[col];
32433             
32434             var positions = [];
32435             
32436             switch (box.length){
32437                 case 1 :
32438                     positions = this.getVerticalOneBoxColPositions(x, y, box);
32439                     break;
32440                 case 2 :
32441                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
32442                     break;
32443                 case 3 :
32444                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
32445                     break;
32446                 case 4 :
32447                     positions = this.getVerticalFourBoxColPositions(x, y, box);
32448                     break;
32449                 default :
32450                     break;
32451             }
32452             
32453             Roo.each(box, function(b,kk){
32454                 
32455                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32456                 
32457                 var sz = b.el.getSize();
32458                 
32459                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
32460                 
32461             }, this);
32462             
32463         }, this);
32464         
32465         var mY = 0;
32466         
32467         for (var i = 0; i < this.cols; i++){
32468             mY = Math.max(mY, maxY[i]);
32469         }
32470         
32471         this.el.setHeight(mY - pos.y);
32472         
32473     },
32474     
32475 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
32476 //    {
32477 //        var pos = this.el.getBox(true);
32478 //        var x = pos.x;
32479 //        var y = pos.y;
32480 //        var maxX = pos.right;
32481 //        
32482 //        var maxHeight = 0;
32483 //        
32484 //        Roo.each(items, function(item, k){
32485 //            
32486 //            var c = k % 2;
32487 //            
32488 //            item.el.position('absolute');
32489 //                
32490 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
32491 //
32492 //            item.el.setWidth(width);
32493 //
32494 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
32495 //
32496 //            item.el.setHeight(height);
32497 //            
32498 //            if(c == 0){
32499 //                item.el.setXY([x, y], isInstant ? false : true);
32500 //            } else {
32501 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
32502 //            }
32503 //            
32504 //            y = y + height + this.alternativePadWidth;
32505 //            
32506 //            maxHeight = maxHeight + height + this.alternativePadWidth;
32507 //            
32508 //        }, this);
32509 //        
32510 //        this.el.setHeight(maxHeight);
32511 //        
32512 //    },
32513     
32514     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
32515     {
32516         var pos = this.el.getBox(true);
32517         
32518         var minX = pos.x;
32519         var minY = pos.y;
32520         
32521         var maxX = pos.right;
32522         
32523         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
32524         
32525         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32526         
32527         Roo.each(queue, function(box, k){
32528             
32529             Roo.each(box, function(b, kk){
32530                 
32531                 b.el.position('absolute');
32532                 
32533                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32534                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32535                 
32536                 if(b.size == 'md-left' || b.size == 'md-right'){
32537                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32538                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32539                 }
32540                 
32541                 b.el.setWidth(width);
32542                 b.el.setHeight(height);
32543                 
32544             }, this);
32545             
32546             if(!box.length){
32547                 return;
32548             }
32549             
32550             var positions = [];
32551             
32552             switch (box.length){
32553                 case 1 :
32554                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32555                     break;
32556                 case 2 :
32557                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32558                     break;
32559                 case 3 :
32560                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
32561                     break;
32562                 case 4 :
32563                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
32564                     break;
32565                 default :
32566                     break;
32567             }
32568             
32569             Roo.each(box, function(b,kk){
32570                 
32571                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32572                 
32573                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
32574                 
32575             }, this);
32576             
32577         }, this);
32578         
32579     },
32580     
32581     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
32582     {
32583         Roo.each(eItems, function(b,k){
32584             
32585             b.size = (k == 0) ? 'sm' : 'xs';
32586             b.x = (k == 0) ? 2 : 1;
32587             b.y = (k == 0) ? 2 : 1;
32588             
32589             b.el.position('absolute');
32590             
32591             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32592                 
32593             b.el.setWidth(width);
32594             
32595             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32596             
32597             b.el.setHeight(height);
32598             
32599         }, this);
32600
32601         var positions = [];
32602         
32603         positions.push({
32604             x : maxX - this.unitWidth * 2 - this.gutter,
32605             y : minY
32606         });
32607         
32608         positions.push({
32609             x : maxX - this.unitWidth,
32610             y : minY + (this.unitWidth + this.gutter) * 2
32611         });
32612         
32613         positions.push({
32614             x : maxX - this.unitWidth * 3 - this.gutter * 2,
32615             y : minY
32616         });
32617         
32618         Roo.each(eItems, function(b,k){
32619             
32620             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32621
32622         }, this);
32623         
32624     },
32625     
32626     getVerticalOneBoxColPositions : function(x, y, box)
32627     {
32628         var pos = [];
32629         
32630         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32631         
32632         if(box[0].size == 'md-left'){
32633             rand = 0;
32634         }
32635         
32636         if(box[0].size == 'md-right'){
32637             rand = 1;
32638         }
32639         
32640         pos.push({
32641             x : x + (this.unitWidth + this.gutter) * rand,
32642             y : y
32643         });
32644         
32645         return pos;
32646     },
32647     
32648     getVerticalTwoBoxColPositions : function(x, y, box)
32649     {
32650         var pos = [];
32651         
32652         if(box[0].size == 'xs'){
32653             
32654             pos.push({
32655                 x : x,
32656                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32657             });
32658
32659             pos.push({
32660                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32661                 y : y
32662             });
32663             
32664             return pos;
32665             
32666         }
32667         
32668         pos.push({
32669             x : x,
32670             y : y
32671         });
32672
32673         pos.push({
32674             x : x + (this.unitWidth + this.gutter) * 2,
32675             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32676         });
32677         
32678         return pos;
32679         
32680     },
32681     
32682     getVerticalThreeBoxColPositions : function(x, y, box)
32683     {
32684         var pos = [];
32685         
32686         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32687             
32688             pos.push({
32689                 x : x,
32690                 y : y
32691             });
32692
32693             pos.push({
32694                 x : x + (this.unitWidth + this.gutter) * 1,
32695                 y : y
32696             });
32697             
32698             pos.push({
32699                 x : x + (this.unitWidth + this.gutter) * 2,
32700                 y : y
32701             });
32702             
32703             return pos;
32704             
32705         }
32706         
32707         if(box[0].size == 'xs' && box[1].size == 'xs'){
32708             
32709             pos.push({
32710                 x : x,
32711                 y : y
32712             });
32713
32714             pos.push({
32715                 x : x,
32716                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32717             });
32718             
32719             pos.push({
32720                 x : x + (this.unitWidth + this.gutter) * 1,
32721                 y : y
32722             });
32723             
32724             return pos;
32725             
32726         }
32727         
32728         pos.push({
32729             x : x,
32730             y : y
32731         });
32732
32733         pos.push({
32734             x : x + (this.unitWidth + this.gutter) * 2,
32735             y : y
32736         });
32737
32738         pos.push({
32739             x : x + (this.unitWidth + this.gutter) * 2,
32740             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32741         });
32742             
32743         return pos;
32744         
32745     },
32746     
32747     getVerticalFourBoxColPositions : function(x, y, box)
32748     {
32749         var pos = [];
32750         
32751         if(box[0].size == 'xs'){
32752             
32753             pos.push({
32754                 x : x,
32755                 y : y
32756             });
32757
32758             pos.push({
32759                 x : x,
32760                 y : y + (this.unitHeight + this.gutter) * 1
32761             });
32762             
32763             pos.push({
32764                 x : x,
32765                 y : y + (this.unitHeight + this.gutter) * 2
32766             });
32767             
32768             pos.push({
32769                 x : x + (this.unitWidth + this.gutter) * 1,
32770                 y : y
32771             });
32772             
32773             return pos;
32774             
32775         }
32776         
32777         pos.push({
32778             x : x,
32779             y : y
32780         });
32781
32782         pos.push({
32783             x : x + (this.unitWidth + this.gutter) * 2,
32784             y : y
32785         });
32786
32787         pos.push({
32788             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32789             y : y + (this.unitHeight + this.gutter) * 1
32790         });
32791
32792         pos.push({
32793             x : x + (this.unitWidth + this.gutter) * 2,
32794             y : y + (this.unitWidth + this.gutter) * 2
32795         });
32796
32797         return pos;
32798         
32799     },
32800     
32801     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32802     {
32803         var pos = [];
32804         
32805         if(box[0].size == 'md-left'){
32806             pos.push({
32807                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32808                 y : minY
32809             });
32810             
32811             return pos;
32812         }
32813         
32814         if(box[0].size == 'md-right'){
32815             pos.push({
32816                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32817                 y : minY + (this.unitWidth + this.gutter) * 1
32818             });
32819             
32820             return pos;
32821         }
32822         
32823         var rand = Math.floor(Math.random() * (4 - box[0].y));
32824         
32825         pos.push({
32826             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32827             y : minY + (this.unitWidth + this.gutter) * rand
32828         });
32829         
32830         return pos;
32831         
32832     },
32833     
32834     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32835     {
32836         var pos = [];
32837         
32838         if(box[0].size == 'xs'){
32839             
32840             pos.push({
32841                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32842                 y : minY
32843             });
32844
32845             pos.push({
32846                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32847                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32848             });
32849             
32850             return pos;
32851             
32852         }
32853         
32854         pos.push({
32855             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32856             y : minY
32857         });
32858
32859         pos.push({
32860             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32861             y : minY + (this.unitWidth + this.gutter) * 2
32862         });
32863         
32864         return pos;
32865         
32866     },
32867     
32868     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32869     {
32870         var pos = [];
32871         
32872         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32873             
32874             pos.push({
32875                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32876                 y : minY
32877             });
32878
32879             pos.push({
32880                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32881                 y : minY + (this.unitWidth + this.gutter) * 1
32882             });
32883             
32884             pos.push({
32885                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32886                 y : minY + (this.unitWidth + this.gutter) * 2
32887             });
32888             
32889             return pos;
32890             
32891         }
32892         
32893         if(box[0].size == 'xs' && box[1].size == 'xs'){
32894             
32895             pos.push({
32896                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32897                 y : minY
32898             });
32899
32900             pos.push({
32901                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32902                 y : minY
32903             });
32904             
32905             pos.push({
32906                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32907                 y : minY + (this.unitWidth + this.gutter) * 1
32908             });
32909             
32910             return pos;
32911             
32912         }
32913         
32914         pos.push({
32915             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32916             y : minY
32917         });
32918
32919         pos.push({
32920             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32921             y : minY + (this.unitWidth + this.gutter) * 2
32922         });
32923
32924         pos.push({
32925             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32926             y : minY + (this.unitWidth + this.gutter) * 2
32927         });
32928             
32929         return pos;
32930         
32931     },
32932     
32933     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32934     {
32935         var pos = [];
32936         
32937         if(box[0].size == 'xs'){
32938             
32939             pos.push({
32940                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32941                 y : minY
32942             });
32943
32944             pos.push({
32945                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32946                 y : minY
32947             });
32948             
32949             pos.push({
32950                 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),
32951                 y : minY
32952             });
32953             
32954             pos.push({
32955                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32956                 y : minY + (this.unitWidth + this.gutter) * 1
32957             });
32958             
32959             return pos;
32960             
32961         }
32962         
32963         pos.push({
32964             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32965             y : minY
32966         });
32967         
32968         pos.push({
32969             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32970             y : minY + (this.unitWidth + this.gutter) * 2
32971         });
32972         
32973         pos.push({
32974             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32975             y : minY + (this.unitWidth + this.gutter) * 2
32976         });
32977         
32978         pos.push({
32979             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),
32980             y : minY + (this.unitWidth + this.gutter) * 2
32981         });
32982
32983         return pos;
32984         
32985     },
32986     
32987     /**
32988     * remove a Masonry Brick
32989     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32990     */
32991     removeBrick : function(brick_id)
32992     {
32993         if (!brick_id) {
32994             return;
32995         }
32996         
32997         for (var i = 0; i<this.bricks.length; i++) {
32998             if (this.bricks[i].id == brick_id) {
32999                 this.bricks.splice(i,1);
33000                 this.el.dom.removeChild(Roo.get(brick_id).dom);
33001                 this.initial();
33002             }
33003         }
33004     },
33005     
33006     /**
33007     * adds a Masonry Brick
33008     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33009     */
33010     addBrick : function(cfg)
33011     {
33012         var cn = new Roo.bootstrap.MasonryBrick(cfg);
33013         //this.register(cn);
33014         cn.parentId = this.id;
33015         cn.render(this.el);
33016         return cn;
33017     },
33018     
33019     /**
33020     * register a Masonry Brick
33021     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33022     */
33023     
33024     register : function(brick)
33025     {
33026         this.bricks.push(brick);
33027         brick.masonryId = this.id;
33028     },
33029     
33030     /**
33031     * clear all the Masonry Brick
33032     */
33033     clearAll : function()
33034     {
33035         this.bricks = [];
33036         //this.getChildContainer().dom.innerHTML = "";
33037         this.el.dom.innerHTML = '';
33038     },
33039     
33040     getSelected : function()
33041     {
33042         if (!this.selectedBrick) {
33043             return false;
33044         }
33045         
33046         return this.selectedBrick;
33047     }
33048 });
33049
33050 Roo.apply(Roo.bootstrap.LayoutMasonry, {
33051     
33052     groups: {},
33053      /**
33054     * register a Masonry Layout
33055     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
33056     */
33057     
33058     register : function(layout)
33059     {
33060         this.groups[layout.id] = layout;
33061     },
33062     /**
33063     * fetch a  Masonry Layout based on the masonry layout ID
33064     * @param {string} the masonry layout to add
33065     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
33066     */
33067     
33068     get: function(layout_id) {
33069         if (typeof(this.groups[layout_id]) == 'undefined') {
33070             return false;
33071         }
33072         return this.groups[layout_id] ;
33073     }
33074     
33075     
33076     
33077 });
33078
33079  
33080
33081  /**
33082  *
33083  * This is based on 
33084  * http://masonry.desandro.com
33085  *
33086  * The idea is to render all the bricks based on vertical width...
33087  *
33088  * The original code extends 'outlayer' - we might need to use that....
33089  * 
33090  */
33091
33092
33093 /**
33094  * @class Roo.bootstrap.LayoutMasonryAuto
33095  * @extends Roo.bootstrap.Component
33096  * Bootstrap Layout Masonry class
33097  * 
33098  * @constructor
33099  * Create a new Element
33100  * @param {Object} config The config object
33101  */
33102
33103 Roo.bootstrap.LayoutMasonryAuto = function(config){
33104     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
33105 };
33106
33107 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
33108     
33109       /**
33110      * @cfg {Boolean} isFitWidth  - resize the width..
33111      */   
33112     isFitWidth : false,  // options..
33113     /**
33114      * @cfg {Boolean} isOriginLeft = left align?
33115      */   
33116     isOriginLeft : true,
33117     /**
33118      * @cfg {Boolean} isOriginTop = top align?
33119      */   
33120     isOriginTop : false,
33121     /**
33122      * @cfg {Boolean} isLayoutInstant = no animation?
33123      */   
33124     isLayoutInstant : false, // needed?
33125     /**
33126      * @cfg {Boolean} isResizingContainer = not sure if this is used..
33127      */   
33128     isResizingContainer : true,
33129     /**
33130      * @cfg {Number} columnWidth  width of the columns 
33131      */   
33132     
33133     columnWidth : 0,
33134     
33135     /**
33136      * @cfg {Number} maxCols maximum number of columns
33137      */   
33138     
33139     maxCols: 0,
33140     /**
33141      * @cfg {Number} padHeight padding below box..
33142      */   
33143     
33144     padHeight : 10, 
33145     
33146     /**
33147      * @cfg {Boolean} isAutoInitial defalut true
33148      */   
33149     
33150     isAutoInitial : true, 
33151     
33152     // private?
33153     gutter : 0,
33154     
33155     containerWidth: 0,
33156     initialColumnWidth : 0,
33157     currentSize : null,
33158     
33159     colYs : null, // array.
33160     maxY : 0,
33161     padWidth: 10,
33162     
33163     
33164     tag: 'div',
33165     cls: '',
33166     bricks: null, //CompositeElement
33167     cols : 0, // array?
33168     // element : null, // wrapped now this.el
33169     _isLayoutInited : null, 
33170     
33171     
33172     getAutoCreate : function(){
33173         
33174         var cfg = {
33175             tag: this.tag,
33176             cls: 'blog-masonary-wrapper ' + this.cls,
33177             cn : {
33178                 cls : 'mas-boxes masonary'
33179             }
33180         };
33181         
33182         return cfg;
33183     },
33184     
33185     getChildContainer: function( )
33186     {
33187         if (this.boxesEl) {
33188             return this.boxesEl;
33189         }
33190         
33191         this.boxesEl = this.el.select('.mas-boxes').first();
33192         
33193         return this.boxesEl;
33194     },
33195     
33196     
33197     initEvents : function()
33198     {
33199         var _this = this;
33200         
33201         if(this.isAutoInitial){
33202             Roo.log('hook children rendered');
33203             this.on('childrenrendered', function() {
33204                 Roo.log('children rendered');
33205                 _this.initial();
33206             } ,this);
33207         }
33208         
33209     },
33210     
33211     initial : function()
33212     {
33213         this.reloadItems();
33214
33215         this.currentSize = this.el.getBox(true);
33216
33217         /// was window resize... - let's see if this works..
33218         Roo.EventManager.onWindowResize(this.resize, this); 
33219
33220         if(!this.isAutoInitial){
33221             this.layout();
33222             return;
33223         }
33224         
33225         this.layout.defer(500,this);
33226     },
33227     
33228     reloadItems: function()
33229     {
33230         this.bricks = this.el.select('.masonry-brick', true);
33231         
33232         this.bricks.each(function(b) {
33233             //Roo.log(b.getSize());
33234             if (!b.attr('originalwidth')) {
33235                 b.attr('originalwidth',  b.getSize().width);
33236             }
33237             
33238         });
33239         
33240         Roo.log(this.bricks.elements.length);
33241     },
33242     
33243     resize : function()
33244     {
33245         Roo.log('resize');
33246         var cs = this.el.getBox(true);
33247         
33248         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
33249             Roo.log("no change in with or X");
33250             return;
33251         }
33252         this.currentSize = cs;
33253         this.layout();
33254     },
33255     
33256     layout : function()
33257     {
33258          Roo.log('layout');
33259         this._resetLayout();
33260         //this._manageStamps();
33261       
33262         // don't animate first layout
33263         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33264         this.layoutItems( isInstant );
33265       
33266         // flag for initalized
33267         this._isLayoutInited = true;
33268     },
33269     
33270     layoutItems : function( isInstant )
33271     {
33272         //var items = this._getItemsForLayout( this.items );
33273         // original code supports filtering layout items.. we just ignore it..
33274         
33275         this._layoutItems( this.bricks , isInstant );
33276       
33277         this._postLayout();
33278     },
33279     _layoutItems : function ( items , isInstant)
33280     {
33281        //this.fireEvent( 'layout', this, items );
33282     
33283
33284         if ( !items || !items.elements.length ) {
33285           // no items, emit event with empty array
33286             return;
33287         }
33288
33289         var queue = [];
33290         items.each(function(item) {
33291             Roo.log("layout item");
33292             Roo.log(item);
33293             // get x/y object from method
33294             var position = this._getItemLayoutPosition( item );
33295             // enqueue
33296             position.item = item;
33297             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
33298             queue.push( position );
33299         }, this);
33300       
33301         this._processLayoutQueue( queue );
33302     },
33303     /** Sets position of item in DOM
33304     * @param {Element} item
33305     * @param {Number} x - horizontal position
33306     * @param {Number} y - vertical position
33307     * @param {Boolean} isInstant - disables transitions
33308     */
33309     _processLayoutQueue : function( queue )
33310     {
33311         for ( var i=0, len = queue.length; i < len; i++ ) {
33312             var obj = queue[i];
33313             obj.item.position('absolute');
33314             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
33315         }
33316     },
33317       
33318     
33319     /**
33320     * Any logic you want to do after each layout,
33321     * i.e. size the container
33322     */
33323     _postLayout : function()
33324     {
33325         this.resizeContainer();
33326     },
33327     
33328     resizeContainer : function()
33329     {
33330         if ( !this.isResizingContainer ) {
33331             return;
33332         }
33333         var size = this._getContainerSize();
33334         if ( size ) {
33335             this.el.setSize(size.width,size.height);
33336             this.boxesEl.setSize(size.width,size.height);
33337         }
33338     },
33339     
33340     
33341     
33342     _resetLayout : function()
33343     {
33344         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
33345         this.colWidth = this.el.getWidth();
33346         //this.gutter = this.el.getWidth(); 
33347         
33348         this.measureColumns();
33349
33350         // reset column Y
33351         var i = this.cols;
33352         this.colYs = [];
33353         while (i--) {
33354             this.colYs.push( 0 );
33355         }
33356     
33357         this.maxY = 0;
33358     },
33359
33360     measureColumns : function()
33361     {
33362         this.getContainerWidth();
33363       // if columnWidth is 0, default to outerWidth of first item
33364         if ( !this.columnWidth ) {
33365             var firstItem = this.bricks.first();
33366             Roo.log(firstItem);
33367             this.columnWidth  = this.containerWidth;
33368             if (firstItem && firstItem.attr('originalwidth') ) {
33369                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
33370             }
33371             // columnWidth fall back to item of first element
33372             Roo.log("set column width?");
33373                         this.initialColumnWidth = this.columnWidth  ;
33374
33375             // if first elem has no width, default to size of container
33376             
33377         }
33378         
33379         
33380         if (this.initialColumnWidth) {
33381             this.columnWidth = this.initialColumnWidth;
33382         }
33383         
33384         
33385             
33386         // column width is fixed at the top - however if container width get's smaller we should
33387         // reduce it...
33388         
33389         // this bit calcs how man columns..
33390             
33391         var columnWidth = this.columnWidth += this.gutter;
33392       
33393         // calculate columns
33394         var containerWidth = this.containerWidth + this.gutter;
33395         
33396         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
33397         // fix rounding errors, typically with gutters
33398         var excess = columnWidth - containerWidth % columnWidth;
33399         
33400         
33401         // if overshoot is less than a pixel, round up, otherwise floor it
33402         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
33403         cols = Math[ mathMethod ]( cols );
33404         this.cols = Math.max( cols, 1 );
33405         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33406         
33407          // padding positioning..
33408         var totalColWidth = this.cols * this.columnWidth;
33409         var padavail = this.containerWidth - totalColWidth;
33410         // so for 2 columns - we need 3 'pads'
33411         
33412         var padNeeded = (1+this.cols) * this.padWidth;
33413         
33414         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
33415         
33416         this.columnWidth += padExtra
33417         //this.padWidth = Math.floor(padavail /  ( this.cols));
33418         
33419         // adjust colum width so that padding is fixed??
33420         
33421         // we have 3 columns ... total = width * 3
33422         // we have X left over... that should be used by 
33423         
33424         //if (this.expandC) {
33425             
33426         //}
33427         
33428         
33429         
33430     },
33431     
33432     getContainerWidth : function()
33433     {
33434        /* // container is parent if fit width
33435         var container = this.isFitWidth ? this.element.parentNode : this.element;
33436         // check that this.size and size are there
33437         // IE8 triggers resize on body size change, so they might not be
33438         
33439         var size = getSize( container );  //FIXME
33440         this.containerWidth = size && size.innerWidth; //FIXME
33441         */
33442          
33443         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33444         
33445     },
33446     
33447     _getItemLayoutPosition : function( item )  // what is item?
33448     {
33449         // we resize the item to our columnWidth..
33450       
33451         item.setWidth(this.columnWidth);
33452         item.autoBoxAdjust  = false;
33453         
33454         var sz = item.getSize();
33455  
33456         // how many columns does this brick span
33457         var remainder = this.containerWidth % this.columnWidth;
33458         
33459         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
33460         // round if off by 1 pixel, otherwise use ceil
33461         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
33462         colSpan = Math.min( colSpan, this.cols );
33463         
33464         // normally this should be '1' as we dont' currently allow multi width columns..
33465         
33466         var colGroup = this._getColGroup( colSpan );
33467         // get the minimum Y value from the columns
33468         var minimumY = Math.min.apply( Math, colGroup );
33469         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
33470         
33471         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
33472          
33473         // position the brick
33474         var position = {
33475             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
33476             y: this.currentSize.y + minimumY + this.padHeight
33477         };
33478         
33479         Roo.log(position);
33480         // apply setHeight to necessary columns
33481         var setHeight = minimumY + sz.height + this.padHeight;
33482         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
33483         
33484         var setSpan = this.cols + 1 - colGroup.length;
33485         for ( var i = 0; i < setSpan; i++ ) {
33486           this.colYs[ shortColIndex + i ] = setHeight ;
33487         }
33488       
33489         return position;
33490     },
33491     
33492     /**
33493      * @param {Number} colSpan - number of columns the element spans
33494      * @returns {Array} colGroup
33495      */
33496     _getColGroup : function( colSpan )
33497     {
33498         if ( colSpan < 2 ) {
33499           // if brick spans only one column, use all the column Ys
33500           return this.colYs;
33501         }
33502       
33503         var colGroup = [];
33504         // how many different places could this brick fit horizontally
33505         var groupCount = this.cols + 1 - colSpan;
33506         // for each group potential horizontal position
33507         for ( var i = 0; i < groupCount; i++ ) {
33508           // make an array of colY values for that one group
33509           var groupColYs = this.colYs.slice( i, i + colSpan );
33510           // and get the max value of the array
33511           colGroup[i] = Math.max.apply( Math, groupColYs );
33512         }
33513         return colGroup;
33514     },
33515     /*
33516     _manageStamp : function( stamp )
33517     {
33518         var stampSize =  stamp.getSize();
33519         var offset = stamp.getBox();
33520         // get the columns that this stamp affects
33521         var firstX = this.isOriginLeft ? offset.x : offset.right;
33522         var lastX = firstX + stampSize.width;
33523         var firstCol = Math.floor( firstX / this.columnWidth );
33524         firstCol = Math.max( 0, firstCol );
33525         
33526         var lastCol = Math.floor( lastX / this.columnWidth );
33527         // lastCol should not go over if multiple of columnWidth #425
33528         lastCol -= lastX % this.columnWidth ? 0 : 1;
33529         lastCol = Math.min( this.cols - 1, lastCol );
33530         
33531         // set colYs to bottom of the stamp
33532         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
33533             stampSize.height;
33534             
33535         for ( var i = firstCol; i <= lastCol; i++ ) {
33536           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33537         }
33538     },
33539     */
33540     
33541     _getContainerSize : function()
33542     {
33543         this.maxY = Math.max.apply( Math, this.colYs );
33544         var size = {
33545             height: this.maxY
33546         };
33547       
33548         if ( this.isFitWidth ) {
33549             size.width = this._getContainerFitWidth();
33550         }
33551       
33552         return size;
33553     },
33554     
33555     _getContainerFitWidth : function()
33556     {
33557         var unusedCols = 0;
33558         // count unused columns
33559         var i = this.cols;
33560         while ( --i ) {
33561           if ( this.colYs[i] !== 0 ) {
33562             break;
33563           }
33564           unusedCols++;
33565         }
33566         // fit container to columns that have been used
33567         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
33568     },
33569     
33570     needsResizeLayout : function()
33571     {
33572         var previousWidth = this.containerWidth;
33573         this.getContainerWidth();
33574         return previousWidth !== this.containerWidth;
33575     }
33576  
33577 });
33578
33579  
33580
33581  /*
33582  * - LGPL
33583  *
33584  * element
33585  * 
33586  */
33587
33588 /**
33589  * @class Roo.bootstrap.MasonryBrick
33590  * @extends Roo.bootstrap.Component
33591  * Bootstrap MasonryBrick class
33592  * 
33593  * @constructor
33594  * Create a new MasonryBrick
33595  * @param {Object} config The config object
33596  */
33597
33598 Roo.bootstrap.MasonryBrick = function(config){
33599     
33600     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
33601     
33602     Roo.bootstrap.MasonryBrick.register(this);
33603     
33604     this.addEvents({
33605         // raw events
33606         /**
33607          * @event click
33608          * When a MasonryBrick is clcik
33609          * @param {Roo.bootstrap.MasonryBrick} this
33610          * @param {Roo.EventObject} e
33611          */
33612         "click" : true
33613     });
33614 };
33615
33616 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
33617     
33618     /**
33619      * @cfg {String} title
33620      */   
33621     title : '',
33622     /**
33623      * @cfg {String} html
33624      */   
33625     html : '',
33626     /**
33627      * @cfg {String} bgimage
33628      */   
33629     bgimage : '',
33630     /**
33631      * @cfg {String} videourl
33632      */   
33633     videourl : '',
33634     /**
33635      * @cfg {String} cls
33636      */   
33637     cls : '',
33638     /**
33639      * @cfg {String} href
33640      */   
33641     href : '',
33642     /**
33643      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33644      */   
33645     size : 'xs',
33646     
33647     /**
33648      * @cfg {String} placetitle (center|bottom)
33649      */   
33650     placetitle : '',
33651     
33652     /**
33653      * @cfg {Boolean} isFitContainer defalut true
33654      */   
33655     isFitContainer : true, 
33656     
33657     /**
33658      * @cfg {Boolean} preventDefault defalut false
33659      */   
33660     preventDefault : false, 
33661     
33662     /**
33663      * @cfg {Boolean} inverse defalut false
33664      */   
33665     maskInverse : false, 
33666     
33667     getAutoCreate : function()
33668     {
33669         if(!this.isFitContainer){
33670             return this.getSplitAutoCreate();
33671         }
33672         
33673         var cls = 'masonry-brick masonry-brick-full';
33674         
33675         if(this.href.length){
33676             cls += ' masonry-brick-link';
33677         }
33678         
33679         if(this.bgimage.length){
33680             cls += ' masonry-brick-image';
33681         }
33682         
33683         if(this.maskInverse){
33684             cls += ' mask-inverse';
33685         }
33686         
33687         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33688             cls += ' enable-mask';
33689         }
33690         
33691         if(this.size){
33692             cls += ' masonry-' + this.size + '-brick';
33693         }
33694         
33695         if(this.placetitle.length){
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                     break;
33706             }
33707             
33708         } else {
33709             if(!this.html.length && !this.bgimage.length){
33710                 cls += ' masonry-center-title';
33711             }
33712
33713             if(!this.html.length && this.bgimage.length){
33714                 cls += ' masonry-bottom-title';
33715             }
33716         }
33717         
33718         if(this.cls){
33719             cls += ' ' + this.cls;
33720         }
33721         
33722         var cfg = {
33723             tag: (this.href.length) ? 'a' : 'div',
33724             cls: cls,
33725             cn: [
33726                 {
33727                     tag: 'div',
33728                     cls: 'masonry-brick-mask'
33729                 },
33730                 {
33731                     tag: 'div',
33732                     cls: 'masonry-brick-paragraph',
33733                     cn: []
33734                 }
33735             ]
33736         };
33737         
33738         if(this.href.length){
33739             cfg.href = this.href;
33740         }
33741         
33742         var cn = cfg.cn[1].cn;
33743         
33744         if(this.title.length){
33745             cn.push({
33746                 tag: 'h4',
33747                 cls: 'masonry-brick-title',
33748                 html: this.title
33749             });
33750         }
33751         
33752         if(this.html.length){
33753             cn.push({
33754                 tag: 'p',
33755                 cls: 'masonry-brick-text',
33756                 html: this.html
33757             });
33758         }
33759         
33760         if (!this.title.length && !this.html.length) {
33761             cfg.cn[1].cls += ' hide';
33762         }
33763         
33764         if(this.bgimage.length){
33765             cfg.cn.push({
33766                 tag: 'img',
33767                 cls: 'masonry-brick-image-view',
33768                 src: this.bgimage
33769             });
33770         }
33771         
33772         if(this.videourl.length){
33773             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33774             // youtube support only?
33775             cfg.cn.push({
33776                 tag: 'iframe',
33777                 cls: 'masonry-brick-image-view',
33778                 src: vurl,
33779                 frameborder : 0,
33780                 allowfullscreen : true
33781             });
33782         }
33783         
33784         return cfg;
33785         
33786     },
33787     
33788     getSplitAutoCreate : function()
33789     {
33790         var cls = 'masonry-brick masonry-brick-split';
33791         
33792         if(this.href.length){
33793             cls += ' masonry-brick-link';
33794         }
33795         
33796         if(this.bgimage.length){
33797             cls += ' masonry-brick-image';
33798         }
33799         
33800         if(this.size){
33801             cls += ' masonry-' + this.size + '-brick';
33802         }
33803         
33804         switch (this.placetitle) {
33805             case 'center' :
33806                 cls += ' masonry-center-title';
33807                 break;
33808             case 'bottom' :
33809                 cls += ' masonry-bottom-title';
33810                 break;
33811             default:
33812                 if(!this.bgimage.length){
33813                     cls += ' masonry-center-title';
33814                 }
33815
33816                 if(this.bgimage.length){
33817                     cls += ' masonry-bottom-title';
33818                 }
33819                 break;
33820         }
33821         
33822         if(this.cls){
33823             cls += ' ' + this.cls;
33824         }
33825         
33826         var cfg = {
33827             tag: (this.href.length) ? 'a' : 'div',
33828             cls: cls,
33829             cn: [
33830                 {
33831                     tag: 'div',
33832                     cls: 'masonry-brick-split-head',
33833                     cn: [
33834                         {
33835                             tag: 'div',
33836                             cls: 'masonry-brick-paragraph',
33837                             cn: []
33838                         }
33839                     ]
33840                 },
33841                 {
33842                     tag: 'div',
33843                     cls: 'masonry-brick-split-body',
33844                     cn: []
33845                 }
33846             ]
33847         };
33848         
33849         if(this.href.length){
33850             cfg.href = this.href;
33851         }
33852         
33853         if(this.title.length){
33854             cfg.cn[0].cn[0].cn.push({
33855                 tag: 'h4',
33856                 cls: 'masonry-brick-title',
33857                 html: this.title
33858             });
33859         }
33860         
33861         if(this.html.length){
33862             cfg.cn[1].cn.push({
33863                 tag: 'p',
33864                 cls: 'masonry-brick-text',
33865                 html: this.html
33866             });
33867         }
33868
33869         if(this.bgimage.length){
33870             cfg.cn[0].cn.push({
33871                 tag: 'img',
33872                 cls: 'masonry-brick-image-view',
33873                 src: this.bgimage
33874             });
33875         }
33876         
33877         if(this.videourl.length){
33878             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33879             // youtube support only?
33880             cfg.cn[0].cn.cn.push({
33881                 tag: 'iframe',
33882                 cls: 'masonry-brick-image-view',
33883                 src: vurl,
33884                 frameborder : 0,
33885                 allowfullscreen : true
33886             });
33887         }
33888         
33889         return cfg;
33890     },
33891     
33892     initEvents: function() 
33893     {
33894         switch (this.size) {
33895             case 'xs' :
33896                 this.x = 1;
33897                 this.y = 1;
33898                 break;
33899             case 'sm' :
33900                 this.x = 2;
33901                 this.y = 2;
33902                 break;
33903             case 'md' :
33904             case 'md-left' :
33905             case 'md-right' :
33906                 this.x = 3;
33907                 this.y = 3;
33908                 break;
33909             case 'tall' :
33910                 this.x = 2;
33911                 this.y = 3;
33912                 break;
33913             case 'wide' :
33914                 this.x = 3;
33915                 this.y = 2;
33916                 break;
33917             case 'wide-thin' :
33918                 this.x = 3;
33919                 this.y = 1;
33920                 break;
33921                         
33922             default :
33923                 break;
33924         }
33925         
33926         if(Roo.isTouch){
33927             this.el.on('touchstart', this.onTouchStart, this);
33928             this.el.on('touchmove', this.onTouchMove, this);
33929             this.el.on('touchend', this.onTouchEnd, this);
33930             this.el.on('contextmenu', this.onContextMenu, this);
33931         } else {
33932             this.el.on('mouseenter'  ,this.enter, this);
33933             this.el.on('mouseleave', this.leave, this);
33934             this.el.on('click', this.onClick, this);
33935         }
33936         
33937         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33938             this.parent().bricks.push(this);   
33939         }
33940         
33941     },
33942     
33943     onClick: function(e, el)
33944     {
33945         var time = this.endTimer - this.startTimer;
33946         // Roo.log(e.preventDefault());
33947         if(Roo.isTouch){
33948             if(time > 1000){
33949                 e.preventDefault();
33950                 return;
33951             }
33952         }
33953         
33954         if(!this.preventDefault){
33955             return;
33956         }
33957         
33958         e.preventDefault();
33959         
33960         if (this.activeClass != '') {
33961             this.selectBrick();
33962         }
33963         
33964         this.fireEvent('click', this, e);
33965     },
33966     
33967     enter: function(e, el)
33968     {
33969         e.preventDefault();
33970         
33971         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33972             return;
33973         }
33974         
33975         if(this.bgimage.length && this.html.length){
33976             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33977         }
33978     },
33979     
33980     leave: function(e, el)
33981     {
33982         e.preventDefault();
33983         
33984         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33985             return;
33986         }
33987         
33988         if(this.bgimage.length && this.html.length){
33989             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33990         }
33991     },
33992     
33993     onTouchStart: function(e, el)
33994     {
33995 //        e.preventDefault();
33996         
33997         this.touchmoved = false;
33998         
33999         if(!this.isFitContainer){
34000             return;
34001         }
34002         
34003         if(!this.bgimage.length || !this.html.length){
34004             return;
34005         }
34006         
34007         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
34008         
34009         this.timer = new Date().getTime();
34010         
34011     },
34012     
34013     onTouchMove: function(e, el)
34014     {
34015         this.touchmoved = true;
34016     },
34017     
34018     onContextMenu : function(e,el)
34019     {
34020         e.preventDefault();
34021         e.stopPropagation();
34022         return false;
34023     },
34024     
34025     onTouchEnd: function(e, el)
34026     {
34027 //        e.preventDefault();
34028         
34029         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
34030         
34031             this.leave(e,el);
34032             
34033             return;
34034         }
34035         
34036         if(!this.bgimage.length || !this.html.length){
34037             
34038             if(this.href.length){
34039                 window.location.href = this.href;
34040             }
34041             
34042             return;
34043         }
34044         
34045         if(!this.isFitContainer){
34046             return;
34047         }
34048         
34049         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
34050         
34051         window.location.href = this.href;
34052     },
34053     
34054     //selection on single brick only
34055     selectBrick : function() {
34056         
34057         if (!this.parentId) {
34058             return;
34059         }
34060         
34061         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
34062         var index = m.selectedBrick.indexOf(this.id);
34063         
34064         if ( index > -1) {
34065             m.selectedBrick.splice(index,1);
34066             this.el.removeClass(this.activeClass);
34067             return;
34068         }
34069         
34070         for(var i = 0; i < m.selectedBrick.length; i++) {
34071             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
34072             b.el.removeClass(b.activeClass);
34073         }
34074         
34075         m.selectedBrick = [];
34076         
34077         m.selectedBrick.push(this.id);
34078         this.el.addClass(this.activeClass);
34079         return;
34080     },
34081     
34082     isSelected : function(){
34083         return this.el.hasClass(this.activeClass);
34084         
34085     }
34086 });
34087
34088 Roo.apply(Roo.bootstrap.MasonryBrick, {
34089     
34090     //groups: {},
34091     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
34092      /**
34093     * register a Masonry Brick
34094     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34095     */
34096     
34097     register : function(brick)
34098     {
34099         //this.groups[brick.id] = brick;
34100         this.groups.add(brick.id, brick);
34101     },
34102     /**
34103     * fetch a  masonry brick based on the masonry brick ID
34104     * @param {string} the masonry brick to add
34105     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
34106     */
34107     
34108     get: function(brick_id) 
34109     {
34110         // if (typeof(this.groups[brick_id]) == 'undefined') {
34111         //     return false;
34112         // }
34113         // return this.groups[brick_id] ;
34114         
34115         if(this.groups.key(brick_id)) {
34116             return this.groups.key(brick_id);
34117         }
34118         
34119         return false;
34120     }
34121     
34122     
34123     
34124 });
34125
34126  /*
34127  * - LGPL
34128  *
34129  * element
34130  * 
34131  */
34132
34133 /**
34134  * @class Roo.bootstrap.Brick
34135  * @extends Roo.bootstrap.Component
34136  * Bootstrap Brick class
34137  * 
34138  * @constructor
34139  * Create a new Brick
34140  * @param {Object} config The config object
34141  */
34142
34143 Roo.bootstrap.Brick = function(config){
34144     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
34145     
34146     this.addEvents({
34147         // raw events
34148         /**
34149          * @event click
34150          * When a Brick is click
34151          * @param {Roo.bootstrap.Brick} this
34152          * @param {Roo.EventObject} e
34153          */
34154         "click" : true
34155     });
34156 };
34157
34158 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
34159     
34160     /**
34161      * @cfg {String} title
34162      */   
34163     title : '',
34164     /**
34165      * @cfg {String} html
34166      */   
34167     html : '',
34168     /**
34169      * @cfg {String} bgimage
34170      */   
34171     bgimage : '',
34172     /**
34173      * @cfg {String} cls
34174      */   
34175     cls : '',
34176     /**
34177      * @cfg {String} href
34178      */   
34179     href : '',
34180     /**
34181      * @cfg {String} video
34182      */   
34183     video : '',
34184     /**
34185      * @cfg {Boolean} square
34186      */   
34187     square : true,
34188     
34189     getAutoCreate : function()
34190     {
34191         var cls = 'roo-brick';
34192         
34193         if(this.href.length){
34194             cls += ' roo-brick-link';
34195         }
34196         
34197         if(this.bgimage.length){
34198             cls += ' roo-brick-image';
34199         }
34200         
34201         if(!this.html.length && !this.bgimage.length){
34202             cls += ' roo-brick-center-title';
34203         }
34204         
34205         if(!this.html.length && this.bgimage.length){
34206             cls += ' roo-brick-bottom-title';
34207         }
34208         
34209         if(this.cls){
34210             cls += ' ' + this.cls;
34211         }
34212         
34213         var cfg = {
34214             tag: (this.href.length) ? 'a' : 'div',
34215             cls: cls,
34216             cn: [
34217                 {
34218                     tag: 'div',
34219                     cls: 'roo-brick-paragraph',
34220                     cn: []
34221                 }
34222             ]
34223         };
34224         
34225         if(this.href.length){
34226             cfg.href = this.href;
34227         }
34228         
34229         var cn = cfg.cn[0].cn;
34230         
34231         if(this.title.length){
34232             cn.push({
34233                 tag: 'h4',
34234                 cls: 'roo-brick-title',
34235                 html: this.title
34236             });
34237         }
34238         
34239         if(this.html.length){
34240             cn.push({
34241                 tag: 'p',
34242                 cls: 'roo-brick-text',
34243                 html: this.html
34244             });
34245         } else {
34246             cn.cls += ' hide';
34247         }
34248         
34249         if(this.bgimage.length){
34250             cfg.cn.push({
34251                 tag: 'img',
34252                 cls: 'roo-brick-image-view',
34253                 src: this.bgimage
34254             });
34255         }
34256         
34257         return cfg;
34258     },
34259     
34260     initEvents: function() 
34261     {
34262         if(this.title.length || this.html.length){
34263             this.el.on('mouseenter'  ,this.enter, this);
34264             this.el.on('mouseleave', this.leave, this);
34265         }
34266         
34267         Roo.EventManager.onWindowResize(this.resize, this); 
34268         
34269         if(this.bgimage.length){
34270             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
34271             this.imageEl.on('load', this.onImageLoad, this);
34272             return;
34273         }
34274         
34275         this.resize();
34276     },
34277     
34278     onImageLoad : function()
34279     {
34280         this.resize();
34281     },
34282     
34283     resize : function()
34284     {
34285         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
34286         
34287         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
34288         
34289         if(this.bgimage.length){
34290             var image = this.el.select('.roo-brick-image-view', true).first();
34291             
34292             image.setWidth(paragraph.getWidth());
34293             
34294             if(this.square){
34295                 image.setHeight(paragraph.getWidth());
34296             }
34297             
34298             this.el.setHeight(image.getHeight());
34299             paragraph.setHeight(image.getHeight());
34300             
34301         }
34302         
34303     },
34304     
34305     enter: function(e, el)
34306     {
34307         e.preventDefault();
34308         
34309         if(this.bgimage.length){
34310             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
34311             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
34312         }
34313     },
34314     
34315     leave: function(e, el)
34316     {
34317         e.preventDefault();
34318         
34319         if(this.bgimage.length){
34320             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
34321             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
34322         }
34323     }
34324     
34325 });
34326
34327  
34328
34329  /*
34330  * - LGPL
34331  *
34332  * Number field 
34333  */
34334
34335 /**
34336  * @class Roo.bootstrap.NumberField
34337  * @extends Roo.bootstrap.Input
34338  * Bootstrap NumberField class
34339  * 
34340  * 
34341  * 
34342  * 
34343  * @constructor
34344  * Create a new NumberField
34345  * @param {Object} config The config object
34346  */
34347
34348 Roo.bootstrap.NumberField = function(config){
34349     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
34350 };
34351
34352 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
34353     
34354     /**
34355      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
34356      */
34357     allowDecimals : true,
34358     /**
34359      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
34360      */
34361     decimalSeparator : ".",
34362     /**
34363      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
34364      */
34365     decimalPrecision : 2,
34366     /**
34367      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
34368      */
34369     allowNegative : true,
34370     
34371     /**
34372      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
34373      */
34374     allowZero: true,
34375     /**
34376      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
34377      */
34378     minValue : Number.NEGATIVE_INFINITY,
34379     /**
34380      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
34381      */
34382     maxValue : Number.MAX_VALUE,
34383     /**
34384      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
34385      */
34386     minText : "The minimum value for this field is {0}",
34387     /**
34388      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
34389      */
34390     maxText : "The maximum value for this field is {0}",
34391     /**
34392      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
34393      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
34394      */
34395     nanText : "{0} is not a valid number",
34396     /**
34397      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
34398      */
34399     thousandsDelimiter : false,
34400     /**
34401      * @cfg {String} valueAlign alignment of value
34402      */
34403     valueAlign : "left",
34404
34405     getAutoCreate : function()
34406     {
34407         var hiddenInput = {
34408             tag: 'input',
34409             type: 'hidden',
34410             id: Roo.id(),
34411             cls: 'hidden-number-input'
34412         };
34413         
34414         if (this.name) {
34415             hiddenInput.name = this.name;
34416         }
34417         
34418         this.name = '';
34419         
34420         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
34421         
34422         this.name = hiddenInput.name;
34423         
34424         if(cfg.cn.length > 0) {
34425             cfg.cn.push(hiddenInput);
34426         }
34427         
34428         return cfg;
34429     },
34430
34431     // private
34432     initEvents : function()
34433     {   
34434         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
34435         
34436         var allowed = "0123456789";
34437         
34438         if(this.allowDecimals){
34439             allowed += this.decimalSeparator;
34440         }
34441         
34442         if(this.allowNegative){
34443             allowed += "-";
34444         }
34445         
34446         if(this.thousandsDelimiter) {
34447             allowed += ",";
34448         }
34449         
34450         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
34451         
34452         var keyPress = function(e){
34453             
34454             var k = e.getKey();
34455             
34456             var c = e.getCharCode();
34457             
34458             if(
34459                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
34460                     allowed.indexOf(String.fromCharCode(c)) === -1
34461             ){
34462                 e.stopEvent();
34463                 return;
34464             }
34465             
34466             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
34467                 return;
34468             }
34469             
34470             if(allowed.indexOf(String.fromCharCode(c)) === -1){
34471                 e.stopEvent();
34472             }
34473         };
34474         
34475         this.el.on("keypress", keyPress, this);
34476     },
34477     
34478     validateValue : function(value)
34479     {
34480         
34481         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
34482             return false;
34483         }
34484         
34485         var num = this.parseValue(value);
34486         
34487         if(isNaN(num)){
34488             this.markInvalid(String.format(this.nanText, value));
34489             return false;
34490         }
34491         
34492         if(num < this.minValue){
34493             this.markInvalid(String.format(this.minText, this.minValue));
34494             return false;
34495         }
34496         
34497         if(num > this.maxValue){
34498             this.markInvalid(String.format(this.maxText, this.maxValue));
34499             return false;
34500         }
34501         
34502         return true;
34503     },
34504
34505     getValue : function()
34506     {
34507         var v = this.hiddenEl().getValue();
34508         
34509         return this.fixPrecision(this.parseValue(v));
34510     },
34511
34512     parseValue : function(value)
34513     {
34514         if(this.thousandsDelimiter) {
34515             value += "";
34516             r = new RegExp(",", "g");
34517             value = value.replace(r, "");
34518         }
34519         
34520         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
34521         return isNaN(value) ? '' : value;
34522     },
34523
34524     fixPrecision : function(value)
34525     {
34526         if(this.thousandsDelimiter) {
34527             value += "";
34528             r = new RegExp(",", "g");
34529             value = value.replace(r, "");
34530         }
34531         
34532         var nan = isNaN(value);
34533         
34534         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34535             return nan ? '' : value;
34536         }
34537         return parseFloat(value).toFixed(this.decimalPrecision);
34538     },
34539
34540     setValue : function(v)
34541     {
34542         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34543         
34544         this.value = v;
34545         
34546         if(this.rendered){
34547             
34548             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34549             
34550             this.inputEl().dom.value = (v == '') ? '' :
34551                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34552             
34553             if(!this.allowZero && v === '0') {
34554                 this.hiddenEl().dom.value = '';
34555                 this.inputEl().dom.value = '';
34556             }
34557             
34558             this.validate();
34559         }
34560     },
34561
34562     decimalPrecisionFcn : function(v)
34563     {
34564         return Math.floor(v);
34565     },
34566
34567     beforeBlur : function()
34568     {
34569         var v = this.parseValue(this.getRawValue());
34570         
34571         if(v || v === 0 || v === ''){
34572             this.setValue(v);
34573         }
34574     },
34575     
34576     hiddenEl : function()
34577     {
34578         return this.el.select('input.hidden-number-input',true).first();
34579     }
34580     
34581 });
34582
34583  
34584
34585 /*
34586 * Licence: LGPL
34587 */
34588
34589 /**
34590  * @class Roo.bootstrap.DocumentSlider
34591  * @extends Roo.bootstrap.Component
34592  * Bootstrap DocumentSlider class
34593  * 
34594  * @constructor
34595  * Create a new DocumentViewer
34596  * @param {Object} config The config object
34597  */
34598
34599 Roo.bootstrap.DocumentSlider = function(config){
34600     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
34601     
34602     this.files = [];
34603     
34604     this.addEvents({
34605         /**
34606          * @event initial
34607          * Fire after initEvent
34608          * @param {Roo.bootstrap.DocumentSlider} this
34609          */
34610         "initial" : true,
34611         /**
34612          * @event update
34613          * Fire after update
34614          * @param {Roo.bootstrap.DocumentSlider} this
34615          */
34616         "update" : true,
34617         /**
34618          * @event click
34619          * Fire after click
34620          * @param {Roo.bootstrap.DocumentSlider} this
34621          */
34622         "click" : true
34623     });
34624 };
34625
34626 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
34627     
34628     files : false,
34629     
34630     indicator : 0,
34631     
34632     getAutoCreate : function()
34633     {
34634         var cfg = {
34635             tag : 'div',
34636             cls : 'roo-document-slider',
34637             cn : [
34638                 {
34639                     tag : 'div',
34640                     cls : 'roo-document-slider-header',
34641                     cn : [
34642                         {
34643                             tag : 'div',
34644                             cls : 'roo-document-slider-header-title'
34645                         }
34646                     ]
34647                 },
34648                 {
34649                     tag : 'div',
34650                     cls : 'roo-document-slider-body',
34651                     cn : [
34652                         {
34653                             tag : 'div',
34654                             cls : 'roo-document-slider-prev',
34655                             cn : [
34656                                 {
34657                                     tag : 'i',
34658                                     cls : 'fa fa-chevron-left'
34659                                 }
34660                             ]
34661                         },
34662                         {
34663                             tag : 'div',
34664                             cls : 'roo-document-slider-thumb',
34665                             cn : [
34666                                 {
34667                                     tag : 'img',
34668                                     cls : 'roo-document-slider-image'
34669                                 }
34670                             ]
34671                         },
34672                         {
34673                             tag : 'div',
34674                             cls : 'roo-document-slider-next',
34675                             cn : [
34676                                 {
34677                                     tag : 'i',
34678                                     cls : 'fa fa-chevron-right'
34679                                 }
34680                             ]
34681                         }
34682                     ]
34683                 }
34684             ]
34685         };
34686         
34687         return cfg;
34688     },
34689     
34690     initEvents : function()
34691     {
34692         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34693         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34694         
34695         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34696         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34697         
34698         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34699         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34700         
34701         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34702         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34703         
34704         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34705         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34706         
34707         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34708         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34709         
34710         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34711         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34712         
34713         this.thumbEl.on('click', this.onClick, this);
34714         
34715         this.prevIndicator.on('click', this.prev, this);
34716         
34717         this.nextIndicator.on('click', this.next, this);
34718         
34719     },
34720     
34721     initial : function()
34722     {
34723         if(this.files.length){
34724             this.indicator = 1;
34725             this.update()
34726         }
34727         
34728         this.fireEvent('initial', this);
34729     },
34730     
34731     update : function()
34732     {
34733         this.imageEl.attr('src', this.files[this.indicator - 1]);
34734         
34735         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34736         
34737         this.prevIndicator.show();
34738         
34739         if(this.indicator == 1){
34740             this.prevIndicator.hide();
34741         }
34742         
34743         this.nextIndicator.show();
34744         
34745         if(this.indicator == this.files.length){
34746             this.nextIndicator.hide();
34747         }
34748         
34749         this.thumbEl.scrollTo('top');
34750         
34751         this.fireEvent('update', this);
34752     },
34753     
34754     onClick : function(e)
34755     {
34756         e.preventDefault();
34757         
34758         this.fireEvent('click', this);
34759     },
34760     
34761     prev : function(e)
34762     {
34763         e.preventDefault();
34764         
34765         this.indicator = Math.max(1, this.indicator - 1);
34766         
34767         this.update();
34768     },
34769     
34770     next : function(e)
34771     {
34772         e.preventDefault();
34773         
34774         this.indicator = Math.min(this.files.length, this.indicator + 1);
34775         
34776         this.update();
34777     }
34778 });
34779 /*
34780  * - LGPL
34781  *
34782  * RadioSet
34783  *
34784  *
34785  */
34786
34787 /**
34788  * @class Roo.bootstrap.RadioSet
34789  * @extends Roo.bootstrap.Input
34790  * Bootstrap RadioSet class
34791  * @cfg {String} indicatorpos (left|right) default left
34792  * @cfg {Boolean} inline (true|false) inline the element (default true)
34793  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34794  * @constructor
34795  * Create a new RadioSet
34796  * @param {Object} config The config object
34797  */
34798
34799 Roo.bootstrap.RadioSet = function(config){
34800     
34801     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34802     
34803     this.radioes = [];
34804     
34805     Roo.bootstrap.RadioSet.register(this);
34806     
34807     this.addEvents({
34808         /**
34809         * @event check
34810         * Fires when the element is checked or unchecked.
34811         * @param {Roo.bootstrap.RadioSet} this This radio
34812         * @param {Roo.bootstrap.Radio} item The checked item
34813         */
34814        check : true,
34815        /**
34816         * @event click
34817         * Fires when the element is click.
34818         * @param {Roo.bootstrap.RadioSet} this This radio set
34819         * @param {Roo.bootstrap.Radio} item The checked item
34820         * @param {Roo.EventObject} e The event object
34821         */
34822        click : true
34823     });
34824     
34825 };
34826
34827 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34828
34829     radioes : false,
34830     
34831     inline : true,
34832     
34833     weight : '',
34834     
34835     indicatorpos : 'left',
34836     
34837     getAutoCreate : function()
34838     {
34839         var label = {
34840             tag : 'label',
34841             cls : 'roo-radio-set-label',
34842             cn : [
34843                 {
34844                     tag : 'span',
34845                     html : this.fieldLabel
34846                 }
34847             ]
34848         };
34849         if (Roo.bootstrap.version == 3) {
34850             
34851             
34852             if(this.indicatorpos == 'left'){
34853                 label.cn.unshift({
34854                     tag : 'i',
34855                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34856                     tooltip : 'This field is required'
34857                 });
34858             } else {
34859                 label.cn.push({
34860                     tag : 'i',
34861                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34862                     tooltip : 'This field is required'
34863                 });
34864             }
34865         }
34866         var items = {
34867             tag : 'div',
34868             cls : 'roo-radio-set-items'
34869         };
34870         
34871         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34872         
34873         if (align === 'left' && this.fieldLabel.length) {
34874             
34875             items = {
34876                 cls : "roo-radio-set-right", 
34877                 cn: [
34878                     items
34879                 ]
34880             };
34881             
34882             if(this.labelWidth > 12){
34883                 label.style = "width: " + this.labelWidth + 'px';
34884             }
34885             
34886             if(this.labelWidth < 13 && this.labelmd == 0){
34887                 this.labelmd = this.labelWidth;
34888             }
34889             
34890             if(this.labellg > 0){
34891                 label.cls += ' col-lg-' + this.labellg;
34892                 items.cls += ' col-lg-' + (12 - this.labellg);
34893             }
34894             
34895             if(this.labelmd > 0){
34896                 label.cls += ' col-md-' + this.labelmd;
34897                 items.cls += ' col-md-' + (12 - this.labelmd);
34898             }
34899             
34900             if(this.labelsm > 0){
34901                 label.cls += ' col-sm-' + this.labelsm;
34902                 items.cls += ' col-sm-' + (12 - this.labelsm);
34903             }
34904             
34905             if(this.labelxs > 0){
34906                 label.cls += ' col-xs-' + this.labelxs;
34907                 items.cls += ' col-xs-' + (12 - this.labelxs);
34908             }
34909         }
34910         
34911         var cfg = {
34912             tag : 'div',
34913             cls : 'roo-radio-set',
34914             cn : [
34915                 {
34916                     tag : 'input',
34917                     cls : 'roo-radio-set-input',
34918                     type : 'hidden',
34919                     name : this.name,
34920                     value : this.value ? this.value :  ''
34921                 },
34922                 label,
34923                 items
34924             ]
34925         };
34926         
34927         if(this.weight.length){
34928             cfg.cls += ' roo-radio-' + this.weight;
34929         }
34930         
34931         if(this.inline) {
34932             cfg.cls += ' roo-radio-set-inline';
34933         }
34934         
34935         var settings=this;
34936         ['xs','sm','md','lg'].map(function(size){
34937             if (settings[size]) {
34938                 cfg.cls += ' col-' + size + '-' + settings[size];
34939             }
34940         });
34941         
34942         return cfg;
34943         
34944     },
34945
34946     initEvents : function()
34947     {
34948         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34949         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34950         
34951         if(!this.fieldLabel.length){
34952             this.labelEl.hide();
34953         }
34954         
34955         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34956         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34957         
34958         this.indicator = this.indicatorEl();
34959         
34960         if(this.indicator){
34961             this.indicator.addClass('invisible');
34962         }
34963         
34964         this.originalValue = this.getValue();
34965         
34966     },
34967     
34968     inputEl: function ()
34969     {
34970         return this.el.select('.roo-radio-set-input', true).first();
34971     },
34972     
34973     getChildContainer : function()
34974     {
34975         return this.itemsEl;
34976     },
34977     
34978     register : function(item)
34979     {
34980         this.radioes.push(item);
34981         
34982     },
34983     
34984     validate : function()
34985     {   
34986         if(this.getVisibilityEl().hasClass('hidden')){
34987             return true;
34988         }
34989         
34990         var valid = false;
34991         
34992         Roo.each(this.radioes, function(i){
34993             if(!i.checked){
34994                 return;
34995             }
34996             
34997             valid = true;
34998             return false;
34999         });
35000         
35001         if(this.allowBlank) {
35002             return true;
35003         }
35004         
35005         if(this.disabled || valid){
35006             this.markValid();
35007             return true;
35008         }
35009         
35010         this.markInvalid();
35011         return false;
35012         
35013     },
35014     
35015     markValid : function()
35016     {
35017         if(this.labelEl.isVisible(true) && this.indicatorEl()){
35018             this.indicatorEl().removeClass('visible');
35019             this.indicatorEl().addClass('invisible');
35020         }
35021         
35022         
35023         if (Roo.bootstrap.version == 3) {
35024             this.el.removeClass([this.invalidClass, this.validClass]);
35025             this.el.addClass(this.validClass);
35026         } else {
35027             this.el.removeClass(['is-invalid','is-valid']);
35028             this.el.addClass(['is-valid']);
35029         }
35030         this.fireEvent('valid', this);
35031     },
35032     
35033     markInvalid : function(msg)
35034     {
35035         if(this.allowBlank || this.disabled){
35036             return;
35037         }
35038         
35039         if(this.labelEl.isVisible(true) && this.indicatorEl()){
35040             this.indicatorEl().removeClass('invisible');
35041             this.indicatorEl().addClass('visible');
35042         }
35043         if (Roo.bootstrap.version == 3) {
35044             this.el.removeClass([this.invalidClass, this.validClass]);
35045             this.el.addClass(this.invalidClass);
35046         } else {
35047             this.el.removeClass(['is-invalid','is-valid']);
35048             this.el.addClass(['is-invalid']);
35049         }
35050         
35051         this.fireEvent('invalid', this, msg);
35052         
35053     },
35054     
35055     setValue : function(v, suppressEvent)
35056     {   
35057         if(this.value === v){
35058             return;
35059         }
35060         
35061         this.value = v;
35062         
35063         if(this.rendered){
35064             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
35065         }
35066         
35067         Roo.each(this.radioes, function(i){
35068             i.checked = false;
35069             i.el.removeClass('checked');
35070         });
35071         
35072         Roo.each(this.radioes, function(i){
35073             
35074             if(i.value === v || i.value.toString() === v.toString()){
35075                 i.checked = true;
35076                 i.el.addClass('checked');
35077                 
35078                 if(suppressEvent !== true){
35079                     this.fireEvent('check', this, i);
35080                 }
35081                 
35082                 return false;
35083             }
35084             
35085         }, this);
35086         
35087         this.validate();
35088     },
35089     
35090     clearInvalid : function(){
35091         
35092         if(!this.el || this.preventMark){
35093             return;
35094         }
35095         
35096         this.el.removeClass([this.invalidClass]);
35097         
35098         this.fireEvent('valid', this);
35099     }
35100     
35101 });
35102
35103 Roo.apply(Roo.bootstrap.RadioSet, {
35104     
35105     groups: {},
35106     
35107     register : function(set)
35108     {
35109         this.groups[set.name] = set;
35110     },
35111     
35112     get: function(name) 
35113     {
35114         if (typeof(this.groups[name]) == 'undefined') {
35115             return false;
35116         }
35117         
35118         return this.groups[name] ;
35119     }
35120     
35121 });
35122 /*
35123  * Based on:
35124  * Ext JS Library 1.1.1
35125  * Copyright(c) 2006-2007, Ext JS, LLC.
35126  *
35127  * Originally Released Under LGPL - original licence link has changed is not relivant.
35128  *
35129  * Fork - LGPL
35130  * <script type="text/javascript">
35131  */
35132
35133
35134 /**
35135  * @class Roo.bootstrap.SplitBar
35136  * @extends Roo.util.Observable
35137  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
35138  * <br><br>
35139  * Usage:
35140  * <pre><code>
35141 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
35142                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
35143 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
35144 split.minSize = 100;
35145 split.maxSize = 600;
35146 split.animate = true;
35147 split.on('moved', splitterMoved);
35148 </code></pre>
35149  * @constructor
35150  * Create a new SplitBar
35151  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
35152  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
35153  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
35154  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
35155                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
35156                         position of the SplitBar).
35157  */
35158 Roo.bootstrap.SplitBar = function(cfg){
35159     
35160     /** @private */
35161     
35162     //{
35163     //  dragElement : elm
35164     //  resizingElement: el,
35165         // optional..
35166     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
35167     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
35168         // existingProxy ???
35169     //}
35170     
35171     this.el = Roo.get(cfg.dragElement, true);
35172     this.el.dom.unselectable = "on";
35173     /** @private */
35174     this.resizingEl = Roo.get(cfg.resizingElement, true);
35175
35176     /**
35177      * @private
35178      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
35179      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
35180      * @type Number
35181      */
35182     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
35183     
35184     /**
35185      * The minimum size of the resizing element. (Defaults to 0)
35186      * @type Number
35187      */
35188     this.minSize = 0;
35189     
35190     /**
35191      * The maximum size of the resizing element. (Defaults to 2000)
35192      * @type Number
35193      */
35194     this.maxSize = 2000;
35195     
35196     /**
35197      * Whether to animate the transition to the new size
35198      * @type Boolean
35199      */
35200     this.animate = false;
35201     
35202     /**
35203      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
35204      * @type Boolean
35205      */
35206     this.useShim = false;
35207     
35208     /** @private */
35209     this.shim = null;
35210     
35211     if(!cfg.existingProxy){
35212         /** @private */
35213         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
35214     }else{
35215         this.proxy = Roo.get(cfg.existingProxy).dom;
35216     }
35217     /** @private */
35218     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
35219     
35220     /** @private */
35221     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
35222     
35223     /** @private */
35224     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
35225     
35226     /** @private */
35227     this.dragSpecs = {};
35228     
35229     /**
35230      * @private The adapter to use to positon and resize elements
35231      */
35232     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35233     this.adapter.init(this);
35234     
35235     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35236         /** @private */
35237         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
35238         this.el.addClass("roo-splitbar-h");
35239     }else{
35240         /** @private */
35241         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
35242         this.el.addClass("roo-splitbar-v");
35243     }
35244     
35245     this.addEvents({
35246         /**
35247          * @event resize
35248          * Fires when the splitter is moved (alias for {@link #event-moved})
35249          * @param {Roo.bootstrap.SplitBar} this
35250          * @param {Number} newSize the new width or height
35251          */
35252         "resize" : true,
35253         /**
35254          * @event moved
35255          * Fires when the splitter is moved
35256          * @param {Roo.bootstrap.SplitBar} this
35257          * @param {Number} newSize the new width or height
35258          */
35259         "moved" : true,
35260         /**
35261          * @event beforeresize
35262          * Fires before the splitter is dragged
35263          * @param {Roo.bootstrap.SplitBar} this
35264          */
35265         "beforeresize" : true,
35266
35267         "beforeapply" : true
35268     });
35269
35270     Roo.util.Observable.call(this);
35271 };
35272
35273 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
35274     onStartProxyDrag : function(x, y){
35275         this.fireEvent("beforeresize", this);
35276         if(!this.overlay){
35277             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
35278             o.unselectable();
35279             o.enableDisplayMode("block");
35280             // all splitbars share the same overlay
35281             Roo.bootstrap.SplitBar.prototype.overlay = o;
35282         }
35283         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
35284         this.overlay.show();
35285         Roo.get(this.proxy).setDisplayed("block");
35286         var size = this.adapter.getElementSize(this);
35287         this.activeMinSize = this.getMinimumSize();;
35288         this.activeMaxSize = this.getMaximumSize();;
35289         var c1 = size - this.activeMinSize;
35290         var c2 = Math.max(this.activeMaxSize - size, 0);
35291         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35292             this.dd.resetConstraints();
35293             this.dd.setXConstraint(
35294                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
35295                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
35296             );
35297             this.dd.setYConstraint(0, 0);
35298         }else{
35299             this.dd.resetConstraints();
35300             this.dd.setXConstraint(0, 0);
35301             this.dd.setYConstraint(
35302                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
35303                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
35304             );
35305          }
35306         this.dragSpecs.startSize = size;
35307         this.dragSpecs.startPoint = [x, y];
35308         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
35309     },
35310     
35311     /** 
35312      * @private Called after the drag operation by the DDProxy
35313      */
35314     onEndProxyDrag : function(e){
35315         Roo.get(this.proxy).setDisplayed(false);
35316         var endPoint = Roo.lib.Event.getXY(e);
35317         if(this.overlay){
35318             this.overlay.hide();
35319         }
35320         var newSize;
35321         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35322             newSize = this.dragSpecs.startSize + 
35323                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
35324                     endPoint[0] - this.dragSpecs.startPoint[0] :
35325                     this.dragSpecs.startPoint[0] - endPoint[0]
35326                 );
35327         }else{
35328             newSize = this.dragSpecs.startSize + 
35329                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
35330                     endPoint[1] - this.dragSpecs.startPoint[1] :
35331                     this.dragSpecs.startPoint[1] - endPoint[1]
35332                 );
35333         }
35334         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
35335         if(newSize != this.dragSpecs.startSize){
35336             if(this.fireEvent('beforeapply', this, newSize) !== false){
35337                 this.adapter.setElementSize(this, newSize);
35338                 this.fireEvent("moved", this, newSize);
35339                 this.fireEvent("resize", this, newSize);
35340             }
35341         }
35342     },
35343     
35344     /**
35345      * Get the adapter this SplitBar uses
35346      * @return The adapter object
35347      */
35348     getAdapter : function(){
35349         return this.adapter;
35350     },
35351     
35352     /**
35353      * Set the adapter this SplitBar uses
35354      * @param {Object} adapter A SplitBar adapter object
35355      */
35356     setAdapter : function(adapter){
35357         this.adapter = adapter;
35358         this.adapter.init(this);
35359     },
35360     
35361     /**
35362      * Gets the minimum size for the resizing element
35363      * @return {Number} The minimum size
35364      */
35365     getMinimumSize : function(){
35366         return this.minSize;
35367     },
35368     
35369     /**
35370      * Sets the minimum size for the resizing element
35371      * @param {Number} minSize The minimum size
35372      */
35373     setMinimumSize : function(minSize){
35374         this.minSize = minSize;
35375     },
35376     
35377     /**
35378      * Gets the maximum size for the resizing element
35379      * @return {Number} The maximum size
35380      */
35381     getMaximumSize : function(){
35382         return this.maxSize;
35383     },
35384     
35385     /**
35386      * Sets the maximum size for the resizing element
35387      * @param {Number} maxSize The maximum size
35388      */
35389     setMaximumSize : function(maxSize){
35390         this.maxSize = maxSize;
35391     },
35392     
35393     /**
35394      * Sets the initialize size for the resizing element
35395      * @param {Number} size The initial size
35396      */
35397     setCurrentSize : function(size){
35398         var oldAnimate = this.animate;
35399         this.animate = false;
35400         this.adapter.setElementSize(this, size);
35401         this.animate = oldAnimate;
35402     },
35403     
35404     /**
35405      * Destroy this splitbar. 
35406      * @param {Boolean} removeEl True to remove the element
35407      */
35408     destroy : function(removeEl){
35409         if(this.shim){
35410             this.shim.remove();
35411         }
35412         this.dd.unreg();
35413         this.proxy.parentNode.removeChild(this.proxy);
35414         if(removeEl){
35415             this.el.remove();
35416         }
35417     }
35418 });
35419
35420 /**
35421  * @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.
35422  */
35423 Roo.bootstrap.SplitBar.createProxy = function(dir){
35424     var proxy = new Roo.Element(document.createElement("div"));
35425     proxy.unselectable();
35426     var cls = 'roo-splitbar-proxy';
35427     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
35428     document.body.appendChild(proxy.dom);
35429     return proxy.dom;
35430 };
35431
35432 /** 
35433  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
35434  * Default Adapter. It assumes the splitter and resizing element are not positioned
35435  * elements and only gets/sets the width of the element. Generally used for table based layouts.
35436  */
35437 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
35438 };
35439
35440 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
35441     // do nothing for now
35442     init : function(s){
35443     
35444     },
35445     /**
35446      * Called before drag operations to get the current size of the resizing element. 
35447      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35448      */
35449      getElementSize : function(s){
35450         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35451             return s.resizingEl.getWidth();
35452         }else{
35453             return s.resizingEl.getHeight();
35454         }
35455     },
35456     
35457     /**
35458      * Called after drag operations to set the size of the resizing element.
35459      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35460      * @param {Number} newSize The new size to set
35461      * @param {Function} onComplete A function to be invoked when resizing is complete
35462      */
35463     setElementSize : function(s, newSize, onComplete){
35464         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35465             if(!s.animate){
35466                 s.resizingEl.setWidth(newSize);
35467                 if(onComplete){
35468                     onComplete(s, newSize);
35469                 }
35470             }else{
35471                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
35472             }
35473         }else{
35474             
35475             if(!s.animate){
35476                 s.resizingEl.setHeight(newSize);
35477                 if(onComplete){
35478                     onComplete(s, newSize);
35479                 }
35480             }else{
35481                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
35482             }
35483         }
35484     }
35485 };
35486
35487 /** 
35488  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
35489  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
35490  * Adapter that  moves the splitter element to align with the resized sizing element. 
35491  * Used with an absolute positioned SplitBar.
35492  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
35493  * document.body, make sure you assign an id to the body element.
35494  */
35495 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
35496     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35497     this.container = Roo.get(container);
35498 };
35499
35500 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
35501     init : function(s){
35502         this.basic.init(s);
35503     },
35504     
35505     getElementSize : function(s){
35506         return this.basic.getElementSize(s);
35507     },
35508     
35509     setElementSize : function(s, newSize, onComplete){
35510         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
35511     },
35512     
35513     moveSplitter : function(s){
35514         var yes = Roo.bootstrap.SplitBar;
35515         switch(s.placement){
35516             case yes.LEFT:
35517                 s.el.setX(s.resizingEl.getRight());
35518                 break;
35519             case yes.RIGHT:
35520                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
35521                 break;
35522             case yes.TOP:
35523                 s.el.setY(s.resizingEl.getBottom());
35524                 break;
35525             case yes.BOTTOM:
35526                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
35527                 break;
35528         }
35529     }
35530 };
35531
35532 /**
35533  * Orientation constant - Create a vertical SplitBar
35534  * @static
35535  * @type Number
35536  */
35537 Roo.bootstrap.SplitBar.VERTICAL = 1;
35538
35539 /**
35540  * Orientation constant - Create a horizontal SplitBar
35541  * @static
35542  * @type Number
35543  */
35544 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35545
35546 /**
35547  * Placement constant - The resizing element is to the left of the splitter element
35548  * @static
35549  * @type Number
35550  */
35551 Roo.bootstrap.SplitBar.LEFT = 1;
35552
35553 /**
35554  * Placement constant - The resizing element is to the right of the splitter element
35555  * @static
35556  * @type Number
35557  */
35558 Roo.bootstrap.SplitBar.RIGHT = 2;
35559
35560 /**
35561  * Placement constant - The resizing element is positioned above the splitter element
35562  * @static
35563  * @type Number
35564  */
35565 Roo.bootstrap.SplitBar.TOP = 3;
35566
35567 /**
35568  * Placement constant - The resizing element is positioned under splitter element
35569  * @static
35570  * @type Number
35571  */
35572 Roo.bootstrap.SplitBar.BOTTOM = 4;
35573 Roo.namespace("Roo.bootstrap.layout");/*
35574  * Based on:
35575  * Ext JS Library 1.1.1
35576  * Copyright(c) 2006-2007, Ext JS, LLC.
35577  *
35578  * Originally Released Under LGPL - original licence link has changed is not relivant.
35579  *
35580  * Fork - LGPL
35581  * <script type="text/javascript">
35582  */
35583
35584 /**
35585  * @class Roo.bootstrap.layout.Manager
35586  * @extends Roo.bootstrap.Component
35587  * Base class for layout managers.
35588  */
35589 Roo.bootstrap.layout.Manager = function(config)
35590 {
35591     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
35592
35593
35594
35595
35596
35597     /** false to disable window resize monitoring @type Boolean */
35598     this.monitorWindowResize = true;
35599     this.regions = {};
35600     this.addEvents({
35601         /**
35602          * @event layout
35603          * Fires when a layout is performed.
35604          * @param {Roo.LayoutManager} this
35605          */
35606         "layout" : true,
35607         /**
35608          * @event regionresized
35609          * Fires when the user resizes a region.
35610          * @param {Roo.LayoutRegion} region The resized region
35611          * @param {Number} newSize The new size (width for east/west, height for north/south)
35612          */
35613         "regionresized" : true,
35614         /**
35615          * @event regioncollapsed
35616          * Fires when a region is collapsed.
35617          * @param {Roo.LayoutRegion} region The collapsed region
35618          */
35619         "regioncollapsed" : true,
35620         /**
35621          * @event regionexpanded
35622          * Fires when a region is expanded.
35623          * @param {Roo.LayoutRegion} region The expanded region
35624          */
35625         "regionexpanded" : true
35626     });
35627     this.updating = false;
35628
35629     if (config.el) {
35630         this.el = Roo.get(config.el);
35631         this.initEvents();
35632     }
35633
35634 };
35635
35636 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35637
35638
35639     regions : null,
35640
35641     monitorWindowResize : true,
35642
35643
35644     updating : false,
35645
35646
35647     onRender : function(ct, position)
35648     {
35649         if(!this.el){
35650             this.el = Roo.get(ct);
35651             this.initEvents();
35652         }
35653         //this.fireEvent('render',this);
35654     },
35655
35656
35657     initEvents: function()
35658     {
35659
35660
35661         // ie scrollbar fix
35662         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35663             document.body.scroll = "no";
35664         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35665             this.el.position('relative');
35666         }
35667         this.id = this.el.id;
35668         this.el.addClass("roo-layout-container");
35669         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35670         if(this.el.dom != document.body ) {
35671             this.el.on('resize', this.layout,this);
35672             this.el.on('show', this.layout,this);
35673         }
35674
35675     },
35676
35677     /**
35678      * Returns true if this layout is currently being updated
35679      * @return {Boolean}
35680      */
35681     isUpdating : function(){
35682         return this.updating;
35683     },
35684
35685     /**
35686      * Suspend the LayoutManager from doing auto-layouts while
35687      * making multiple add or remove calls
35688      */
35689     beginUpdate : function(){
35690         this.updating = true;
35691     },
35692
35693     /**
35694      * Restore auto-layouts and optionally disable the manager from performing a layout
35695      * @param {Boolean} noLayout true to disable a layout update
35696      */
35697     endUpdate : function(noLayout){
35698         this.updating = false;
35699         if(!noLayout){
35700             this.layout();
35701         }
35702     },
35703
35704     layout: function(){
35705         // abstract...
35706     },
35707
35708     onRegionResized : function(region, newSize){
35709         this.fireEvent("regionresized", region, newSize);
35710         this.layout();
35711     },
35712
35713     onRegionCollapsed : function(region){
35714         this.fireEvent("regioncollapsed", region);
35715     },
35716
35717     onRegionExpanded : function(region){
35718         this.fireEvent("regionexpanded", region);
35719     },
35720
35721     /**
35722      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35723      * performs box-model adjustments.
35724      * @return {Object} The size as an object {width: (the width), height: (the height)}
35725      */
35726     getViewSize : function()
35727     {
35728         var size;
35729         if(this.el.dom != document.body){
35730             size = this.el.getSize();
35731         }else{
35732             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35733         }
35734         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35735         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35736         return size;
35737     },
35738
35739     /**
35740      * Returns the Element this layout is bound to.
35741      * @return {Roo.Element}
35742      */
35743     getEl : function(){
35744         return this.el;
35745     },
35746
35747     /**
35748      * Returns the specified region.
35749      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35750      * @return {Roo.LayoutRegion}
35751      */
35752     getRegion : function(target){
35753         return this.regions[target.toLowerCase()];
35754     },
35755
35756     onWindowResize : function(){
35757         if(this.monitorWindowResize){
35758             this.layout();
35759         }
35760     }
35761 });
35762 /*
35763  * Based on:
35764  * Ext JS Library 1.1.1
35765  * Copyright(c) 2006-2007, Ext JS, LLC.
35766  *
35767  * Originally Released Under LGPL - original licence link has changed is not relivant.
35768  *
35769  * Fork - LGPL
35770  * <script type="text/javascript">
35771  */
35772 /**
35773  * @class Roo.bootstrap.layout.Border
35774  * @extends Roo.bootstrap.layout.Manager
35775  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35776  * please see: examples/bootstrap/nested.html<br><br>
35777  
35778 <b>The container the layout is rendered into can be either the body element or any other element.
35779 If it is not the body element, the container needs to either be an absolute positioned element,
35780 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35781 the container size if it is not the body element.</b>
35782
35783 * @constructor
35784 * Create a new Border
35785 * @param {Object} config Configuration options
35786  */
35787 Roo.bootstrap.layout.Border = function(config){
35788     config = config || {};
35789     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35790     
35791     
35792     
35793     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35794         if(config[region]){
35795             config[region].region = region;
35796             this.addRegion(config[region]);
35797         }
35798     },this);
35799     
35800 };
35801
35802 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35803
35804 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35805     
35806     parent : false, // this might point to a 'nest' or a ???
35807     
35808     /**
35809      * Creates and adds a new region if it doesn't already exist.
35810      * @param {String} target The target region key (north, south, east, west or center).
35811      * @param {Object} config The regions config object
35812      * @return {BorderLayoutRegion} The new region
35813      */
35814     addRegion : function(config)
35815     {
35816         if(!this.regions[config.region]){
35817             var r = this.factory(config);
35818             this.bindRegion(r);
35819         }
35820         return this.regions[config.region];
35821     },
35822
35823     // private (kinda)
35824     bindRegion : function(r){
35825         this.regions[r.config.region] = r;
35826         
35827         r.on("visibilitychange",    this.layout, this);
35828         r.on("paneladded",          this.layout, this);
35829         r.on("panelremoved",        this.layout, this);
35830         r.on("invalidated",         this.layout, this);
35831         r.on("resized",             this.onRegionResized, this);
35832         r.on("collapsed",           this.onRegionCollapsed, this);
35833         r.on("expanded",            this.onRegionExpanded, this);
35834     },
35835
35836     /**
35837      * Performs a layout update.
35838      */
35839     layout : function()
35840     {
35841         if(this.updating) {
35842             return;
35843         }
35844         
35845         // render all the rebions if they have not been done alreayd?
35846         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35847             if(this.regions[region] && !this.regions[region].bodyEl){
35848                 this.regions[region].onRender(this.el)
35849             }
35850         },this);
35851         
35852         var size = this.getViewSize();
35853         var w = size.width;
35854         var h = size.height;
35855         var centerW = w;
35856         var centerH = h;
35857         var centerY = 0;
35858         var centerX = 0;
35859         //var x = 0, y = 0;
35860
35861         var rs = this.regions;
35862         var north = rs["north"];
35863         var south = rs["south"]; 
35864         var west = rs["west"];
35865         var east = rs["east"];
35866         var center = rs["center"];
35867         //if(this.hideOnLayout){ // not supported anymore
35868             //c.el.setStyle("display", "none");
35869         //}
35870         if(north && north.isVisible()){
35871             var b = north.getBox();
35872             var m = north.getMargins();
35873             b.width = w - (m.left+m.right);
35874             b.x = m.left;
35875             b.y = m.top;
35876             centerY = b.height + b.y + m.bottom;
35877             centerH -= centerY;
35878             north.updateBox(this.safeBox(b));
35879         }
35880         if(south && south.isVisible()){
35881             var b = south.getBox();
35882             var m = south.getMargins();
35883             b.width = w - (m.left+m.right);
35884             b.x = m.left;
35885             var totalHeight = (b.height + m.top + m.bottom);
35886             b.y = h - totalHeight + m.top;
35887             centerH -= totalHeight;
35888             south.updateBox(this.safeBox(b));
35889         }
35890         if(west && west.isVisible()){
35891             var b = west.getBox();
35892             var m = west.getMargins();
35893             b.height = centerH - (m.top+m.bottom);
35894             b.x = m.left;
35895             b.y = centerY + m.top;
35896             var totalWidth = (b.width + m.left + m.right);
35897             centerX += totalWidth;
35898             centerW -= totalWidth;
35899             west.updateBox(this.safeBox(b));
35900         }
35901         if(east && east.isVisible()){
35902             var b = east.getBox();
35903             var m = east.getMargins();
35904             b.height = centerH - (m.top+m.bottom);
35905             var totalWidth = (b.width + m.left + m.right);
35906             b.x = w - totalWidth + m.left;
35907             b.y = centerY + m.top;
35908             centerW -= totalWidth;
35909             east.updateBox(this.safeBox(b));
35910         }
35911         if(center){
35912             var m = center.getMargins();
35913             var centerBox = {
35914                 x: centerX + m.left,
35915                 y: centerY + m.top,
35916                 width: centerW - (m.left+m.right),
35917                 height: centerH - (m.top+m.bottom)
35918             };
35919             //if(this.hideOnLayout){
35920                 //center.el.setStyle("display", "block");
35921             //}
35922             center.updateBox(this.safeBox(centerBox));
35923         }
35924         this.el.repaint();
35925         this.fireEvent("layout", this);
35926     },
35927
35928     // private
35929     safeBox : function(box){
35930         box.width = Math.max(0, box.width);
35931         box.height = Math.max(0, box.height);
35932         return box;
35933     },
35934
35935     /**
35936      * Adds a ContentPanel (or subclass) to this layout.
35937      * @param {String} target The target region key (north, south, east, west or center).
35938      * @param {Roo.ContentPanel} panel The panel to add
35939      * @return {Roo.ContentPanel} The added panel
35940      */
35941     add : function(target, panel){
35942          
35943         target = target.toLowerCase();
35944         return this.regions[target].add(panel);
35945     },
35946
35947     /**
35948      * Remove a ContentPanel (or subclass) to this layout.
35949      * @param {String} target The target region key (north, south, east, west or center).
35950      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35951      * @return {Roo.ContentPanel} The removed panel
35952      */
35953     remove : function(target, panel){
35954         target = target.toLowerCase();
35955         return this.regions[target].remove(panel);
35956     },
35957
35958     /**
35959      * Searches all regions for a panel with the specified id
35960      * @param {String} panelId
35961      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35962      */
35963     findPanel : function(panelId){
35964         var rs = this.regions;
35965         for(var target in rs){
35966             if(typeof rs[target] != "function"){
35967                 var p = rs[target].getPanel(panelId);
35968                 if(p){
35969                     return p;
35970                 }
35971             }
35972         }
35973         return null;
35974     },
35975
35976     /**
35977      * Searches all regions for a panel with the specified id and activates (shows) it.
35978      * @param {String/ContentPanel} panelId The panels id or the panel itself
35979      * @return {Roo.ContentPanel} The shown panel or null
35980      */
35981     showPanel : function(panelId) {
35982       var rs = this.regions;
35983       for(var target in rs){
35984          var r = rs[target];
35985          if(typeof r != "function"){
35986             if(r.hasPanel(panelId)){
35987                return r.showPanel(panelId);
35988             }
35989          }
35990       }
35991       return null;
35992    },
35993
35994    /**
35995      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35996      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35997      */
35998    /*
35999     restoreState : function(provider){
36000         if(!provider){
36001             provider = Roo.state.Manager;
36002         }
36003         var sm = new Roo.LayoutStateManager();
36004         sm.init(this, provider);
36005     },
36006 */
36007  
36008  
36009     /**
36010      * Adds a xtype elements to the layout.
36011      * <pre><code>
36012
36013 layout.addxtype({
36014        xtype : 'ContentPanel',
36015        region: 'west',
36016        items: [ .... ]
36017    }
36018 );
36019
36020 layout.addxtype({
36021         xtype : 'NestedLayoutPanel',
36022         region: 'west',
36023         layout: {
36024            center: { },
36025            west: { }   
36026         },
36027         items : [ ... list of content panels or nested layout panels.. ]
36028    }
36029 );
36030 </code></pre>
36031      * @param {Object} cfg Xtype definition of item to add.
36032      */
36033     addxtype : function(cfg)
36034     {
36035         // basically accepts a pannel...
36036         // can accept a layout region..!?!?
36037         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
36038         
36039         
36040         // theory?  children can only be panels??
36041         
36042         //if (!cfg.xtype.match(/Panel$/)) {
36043         //    return false;
36044         //}
36045         var ret = false;
36046         
36047         if (typeof(cfg.region) == 'undefined') {
36048             Roo.log("Failed to add Panel, region was not set");
36049             Roo.log(cfg);
36050             return false;
36051         }
36052         var region = cfg.region;
36053         delete cfg.region;
36054         
36055           
36056         var xitems = [];
36057         if (cfg.items) {
36058             xitems = cfg.items;
36059             delete cfg.items;
36060         }
36061         var nb = false;
36062         
36063         if ( region == 'center') {
36064             Roo.log("Center: " + cfg.title);
36065         }
36066         
36067         
36068         switch(cfg.xtype) 
36069         {
36070             case 'Content':  // ContentPanel (el, cfg)
36071             case 'Scroll':  // ContentPanel (el, cfg)
36072             case 'View': 
36073                 cfg.autoCreate = cfg.autoCreate || true;
36074                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36075                 //} else {
36076                 //    var el = this.el.createChild();
36077                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
36078                 //}
36079                 
36080                 this.add(region, ret);
36081                 break;
36082             
36083             /*
36084             case 'TreePanel': // our new panel!
36085                 cfg.el = this.el.createChild();
36086                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
36087                 this.add(region, ret);
36088                 break;
36089             */
36090             
36091             case 'Nest': 
36092                 // create a new Layout (which is  a Border Layout...
36093                 
36094                 var clayout = cfg.layout;
36095                 clayout.el  = this.el.createChild();
36096                 clayout.items   = clayout.items  || [];
36097                 
36098                 delete cfg.layout;
36099                 
36100                 // replace this exitems with the clayout ones..
36101                 xitems = clayout.items;
36102                  
36103                 // force background off if it's in center...
36104                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
36105                     cfg.background = false;
36106                 }
36107                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
36108                 
36109                 
36110                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36111                 //console.log('adding nested layout panel '  + cfg.toSource());
36112                 this.add(region, ret);
36113                 nb = {}; /// find first...
36114                 break;
36115             
36116             case 'Grid':
36117                 
36118                 // needs grid and region
36119                 
36120                 //var el = this.getRegion(region).el.createChild();
36121                 /*
36122                  *var el = this.el.createChild();
36123                 // create the grid first...
36124                 cfg.grid.container = el;
36125                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
36126                 */
36127                 
36128                 if (region == 'center' && this.active ) {
36129                     cfg.background = false;
36130                 }
36131                 
36132                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
36133                 
36134                 this.add(region, ret);
36135                 /*
36136                 if (cfg.background) {
36137                     // render grid on panel activation (if panel background)
36138                     ret.on('activate', function(gp) {
36139                         if (!gp.grid.rendered) {
36140                     //        gp.grid.render(el);
36141                         }
36142                     });
36143                 } else {
36144                   //  cfg.grid.render(el);
36145                 }
36146                 */
36147                 break;
36148            
36149            
36150             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
36151                 // it was the old xcomponent building that caused this before.
36152                 // espeically if border is the top element in the tree.
36153                 ret = this;
36154                 break; 
36155                 
36156                     
36157                 
36158                 
36159                 
36160             default:
36161                 /*
36162                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
36163                     
36164                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
36165                     this.add(region, ret);
36166                 } else {
36167                 */
36168                     Roo.log(cfg);
36169                     throw "Can not add '" + cfg.xtype + "' to Border";
36170                     return null;
36171              
36172                                 
36173              
36174         }
36175         this.beginUpdate();
36176         // add children..
36177         var region = '';
36178         var abn = {};
36179         Roo.each(xitems, function(i)  {
36180             region = nb && i.region ? i.region : false;
36181             
36182             var add = ret.addxtype(i);
36183            
36184             if (region) {
36185                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
36186                 if (!i.background) {
36187                     abn[region] = nb[region] ;
36188                 }
36189             }
36190             
36191         });
36192         this.endUpdate();
36193
36194         // make the last non-background panel active..
36195         //if (nb) { Roo.log(abn); }
36196         if (nb) {
36197             
36198             for(var r in abn) {
36199                 region = this.getRegion(r);
36200                 if (region) {
36201                     // tried using nb[r], but it does not work..
36202                      
36203                     region.showPanel(abn[r]);
36204                    
36205                 }
36206             }
36207         }
36208         return ret;
36209         
36210     },
36211     
36212     
36213 // private
36214     factory : function(cfg)
36215     {
36216         
36217         var validRegions = Roo.bootstrap.layout.Border.regions;
36218
36219         var target = cfg.region;
36220         cfg.mgr = this;
36221         
36222         var r = Roo.bootstrap.layout;
36223         Roo.log(target);
36224         switch(target){
36225             case "north":
36226                 return new r.North(cfg);
36227             case "south":
36228                 return new r.South(cfg);
36229             case "east":
36230                 return new r.East(cfg);
36231             case "west":
36232                 return new r.West(cfg);
36233             case "center":
36234                 return new r.Center(cfg);
36235         }
36236         throw 'Layout region "'+target+'" not supported.';
36237     }
36238     
36239     
36240 });
36241  /*
36242  * Based on:
36243  * Ext JS Library 1.1.1
36244  * Copyright(c) 2006-2007, Ext JS, LLC.
36245  *
36246  * Originally Released Under LGPL - original licence link has changed is not relivant.
36247  *
36248  * Fork - LGPL
36249  * <script type="text/javascript">
36250  */
36251  
36252 /**
36253  * @class Roo.bootstrap.layout.Basic
36254  * @extends Roo.util.Observable
36255  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
36256  * and does not have a titlebar, tabs or any other features. All it does is size and position 
36257  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
36258  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
36259  * @cfg {string}   region  the region that it inhabits..
36260  * @cfg {bool}   skipConfig skip config?
36261  * 
36262
36263  */
36264 Roo.bootstrap.layout.Basic = function(config){
36265     
36266     this.mgr = config.mgr;
36267     
36268     this.position = config.region;
36269     
36270     var skipConfig = config.skipConfig;
36271     
36272     this.events = {
36273         /**
36274          * @scope Roo.BasicLayoutRegion
36275          */
36276         
36277         /**
36278          * @event beforeremove
36279          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
36280          * @param {Roo.LayoutRegion} this
36281          * @param {Roo.ContentPanel} panel The panel
36282          * @param {Object} e The cancel event object
36283          */
36284         "beforeremove" : true,
36285         /**
36286          * @event invalidated
36287          * Fires when the layout for this region is changed.
36288          * @param {Roo.LayoutRegion} this
36289          */
36290         "invalidated" : true,
36291         /**
36292          * @event visibilitychange
36293          * Fires when this region is shown or hidden 
36294          * @param {Roo.LayoutRegion} this
36295          * @param {Boolean} visibility true or false
36296          */
36297         "visibilitychange" : true,
36298         /**
36299          * @event paneladded
36300          * Fires when a panel is added. 
36301          * @param {Roo.LayoutRegion} this
36302          * @param {Roo.ContentPanel} panel The panel
36303          */
36304         "paneladded" : true,
36305         /**
36306          * @event panelremoved
36307          * Fires when a panel is removed. 
36308          * @param {Roo.LayoutRegion} this
36309          * @param {Roo.ContentPanel} panel The panel
36310          */
36311         "panelremoved" : true,
36312         /**
36313          * @event beforecollapse
36314          * Fires when this region before collapse.
36315          * @param {Roo.LayoutRegion} this
36316          */
36317         "beforecollapse" : true,
36318         /**
36319          * @event collapsed
36320          * Fires when this region is collapsed.
36321          * @param {Roo.LayoutRegion} this
36322          */
36323         "collapsed" : true,
36324         /**
36325          * @event expanded
36326          * Fires when this region is expanded.
36327          * @param {Roo.LayoutRegion} this
36328          */
36329         "expanded" : true,
36330         /**
36331          * @event slideshow
36332          * Fires when this region is slid into view.
36333          * @param {Roo.LayoutRegion} this
36334          */
36335         "slideshow" : true,
36336         /**
36337          * @event slidehide
36338          * Fires when this region slides out of view. 
36339          * @param {Roo.LayoutRegion} this
36340          */
36341         "slidehide" : true,
36342         /**
36343          * @event panelactivated
36344          * Fires when a panel is activated. 
36345          * @param {Roo.LayoutRegion} this
36346          * @param {Roo.ContentPanel} panel The activated panel
36347          */
36348         "panelactivated" : true,
36349         /**
36350          * @event resized
36351          * Fires when the user resizes this region. 
36352          * @param {Roo.LayoutRegion} this
36353          * @param {Number} newSize The new size (width for east/west, height for north/south)
36354          */
36355         "resized" : true
36356     };
36357     /** A collection of panels in this region. @type Roo.util.MixedCollection */
36358     this.panels = new Roo.util.MixedCollection();
36359     this.panels.getKey = this.getPanelId.createDelegate(this);
36360     this.box = null;
36361     this.activePanel = null;
36362     // ensure listeners are added...
36363     
36364     if (config.listeners || config.events) {
36365         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
36366             listeners : config.listeners || {},
36367             events : config.events || {}
36368         });
36369     }
36370     
36371     if(skipConfig !== true){
36372         this.applyConfig(config);
36373     }
36374 };
36375
36376 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
36377 {
36378     getPanelId : function(p){
36379         return p.getId();
36380     },
36381     
36382     applyConfig : function(config){
36383         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36384         this.config = config;
36385         
36386     },
36387     
36388     /**
36389      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
36390      * the width, for horizontal (north, south) the height.
36391      * @param {Number} newSize The new width or height
36392      */
36393     resizeTo : function(newSize){
36394         var el = this.el ? this.el :
36395                  (this.activePanel ? this.activePanel.getEl() : null);
36396         if(el){
36397             switch(this.position){
36398                 case "east":
36399                 case "west":
36400                     el.setWidth(newSize);
36401                     this.fireEvent("resized", this, newSize);
36402                 break;
36403                 case "north":
36404                 case "south":
36405                     el.setHeight(newSize);
36406                     this.fireEvent("resized", this, newSize);
36407                 break;                
36408             }
36409         }
36410     },
36411     
36412     getBox : function(){
36413         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
36414     },
36415     
36416     getMargins : function(){
36417         return this.margins;
36418     },
36419     
36420     updateBox : function(box){
36421         this.box = box;
36422         var el = this.activePanel.getEl();
36423         el.dom.style.left = box.x + "px";
36424         el.dom.style.top = box.y + "px";
36425         this.activePanel.setSize(box.width, box.height);
36426     },
36427     
36428     /**
36429      * Returns the container element for this region.
36430      * @return {Roo.Element}
36431      */
36432     getEl : function(){
36433         return this.activePanel;
36434     },
36435     
36436     /**
36437      * Returns true if this region is currently visible.
36438      * @return {Boolean}
36439      */
36440     isVisible : function(){
36441         return this.activePanel ? true : false;
36442     },
36443     
36444     setActivePanel : function(panel){
36445         panel = this.getPanel(panel);
36446         if(this.activePanel && this.activePanel != panel){
36447             this.activePanel.setActiveState(false);
36448             this.activePanel.getEl().setLeftTop(-10000,-10000);
36449         }
36450         this.activePanel = panel;
36451         panel.setActiveState(true);
36452         if(this.box){
36453             panel.setSize(this.box.width, this.box.height);
36454         }
36455         this.fireEvent("panelactivated", this, panel);
36456         this.fireEvent("invalidated");
36457     },
36458     
36459     /**
36460      * Show the specified panel.
36461      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
36462      * @return {Roo.ContentPanel} The shown panel or null
36463      */
36464     showPanel : function(panel){
36465         panel = this.getPanel(panel);
36466         if(panel){
36467             this.setActivePanel(panel);
36468         }
36469         return panel;
36470     },
36471     
36472     /**
36473      * Get the active panel for this region.
36474      * @return {Roo.ContentPanel} The active panel or null
36475      */
36476     getActivePanel : function(){
36477         return this.activePanel;
36478     },
36479     
36480     /**
36481      * Add the passed ContentPanel(s)
36482      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36483      * @return {Roo.ContentPanel} The panel added (if only one was added)
36484      */
36485     add : function(panel){
36486         if(arguments.length > 1){
36487             for(var i = 0, len = arguments.length; i < len; i++) {
36488                 this.add(arguments[i]);
36489             }
36490             return null;
36491         }
36492         if(this.hasPanel(panel)){
36493             this.showPanel(panel);
36494             return panel;
36495         }
36496         var el = panel.getEl();
36497         if(el.dom.parentNode != this.mgr.el.dom){
36498             this.mgr.el.dom.appendChild(el.dom);
36499         }
36500         if(panel.setRegion){
36501             panel.setRegion(this);
36502         }
36503         this.panels.add(panel);
36504         el.setStyle("position", "absolute");
36505         if(!panel.background){
36506             this.setActivePanel(panel);
36507             if(this.config.initialSize && this.panels.getCount()==1){
36508                 this.resizeTo(this.config.initialSize);
36509             }
36510         }
36511         this.fireEvent("paneladded", this, panel);
36512         return panel;
36513     },
36514     
36515     /**
36516      * Returns true if the panel is in this region.
36517      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36518      * @return {Boolean}
36519      */
36520     hasPanel : function(panel){
36521         if(typeof panel == "object"){ // must be panel obj
36522             panel = panel.getId();
36523         }
36524         return this.getPanel(panel) ? true : false;
36525     },
36526     
36527     /**
36528      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36529      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36530      * @param {Boolean} preservePanel Overrides the config preservePanel option
36531      * @return {Roo.ContentPanel} The panel that was removed
36532      */
36533     remove : function(panel, preservePanel){
36534         panel = this.getPanel(panel);
36535         if(!panel){
36536             return null;
36537         }
36538         var e = {};
36539         this.fireEvent("beforeremove", this, panel, e);
36540         if(e.cancel === true){
36541             return null;
36542         }
36543         var panelId = panel.getId();
36544         this.panels.removeKey(panelId);
36545         return panel;
36546     },
36547     
36548     /**
36549      * Returns the panel specified or null if it's not in this region.
36550      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36551      * @return {Roo.ContentPanel}
36552      */
36553     getPanel : function(id){
36554         if(typeof id == "object"){ // must be panel obj
36555             return id;
36556         }
36557         return this.panels.get(id);
36558     },
36559     
36560     /**
36561      * Returns this regions position (north/south/east/west/center).
36562      * @return {String} 
36563      */
36564     getPosition: function(){
36565         return this.position;    
36566     }
36567 });/*
36568  * Based on:
36569  * Ext JS Library 1.1.1
36570  * Copyright(c) 2006-2007, Ext JS, LLC.
36571  *
36572  * Originally Released Under LGPL - original licence link has changed is not relivant.
36573  *
36574  * Fork - LGPL
36575  * <script type="text/javascript">
36576  */
36577  
36578 /**
36579  * @class Roo.bootstrap.layout.Region
36580  * @extends Roo.bootstrap.layout.Basic
36581  * This class represents a region in a layout manager.
36582  
36583  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36584  * @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})
36585  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
36586  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
36587  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
36588  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
36589  * @cfg {String}    title           The title for the region (overrides panel titles)
36590  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
36591  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36592  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
36593  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36594  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
36595  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36596  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
36597  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
36598  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
36599  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
36600
36601  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
36602  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
36603  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
36604  * @cfg {Number}    width           For East/West panels
36605  * @cfg {Number}    height          For North/South panels
36606  * @cfg {Boolean}   split           To show the splitter
36607  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
36608  * 
36609  * @cfg {string}   cls             Extra CSS classes to add to region
36610  * 
36611  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
36612  * @cfg {string}   region  the region that it inhabits..
36613  *
36614
36615  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
36616  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
36617
36618  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
36619  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
36620  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
36621  */
36622 Roo.bootstrap.layout.Region = function(config)
36623 {
36624     this.applyConfig(config);
36625
36626     var mgr = config.mgr;
36627     var pos = config.region;
36628     config.skipConfig = true;
36629     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36630     
36631     if (mgr.el) {
36632         this.onRender(mgr.el);   
36633     }
36634      
36635     this.visible = true;
36636     this.collapsed = false;
36637     this.unrendered_panels = [];
36638 };
36639
36640 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36641
36642     position: '', // set by wrapper (eg. north/south etc..)
36643     unrendered_panels : null,  // unrendered panels.
36644     
36645     tabPosition : false,
36646     
36647     mgr: false, // points to 'Border'
36648     
36649     
36650     createBody : function(){
36651         /** This region's body element 
36652         * @type Roo.Element */
36653         this.bodyEl = this.el.createChild({
36654                 tag: "div",
36655                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36656         });
36657     },
36658
36659     onRender: function(ctr, pos)
36660     {
36661         var dh = Roo.DomHelper;
36662         /** This region's container element 
36663         * @type Roo.Element */
36664         this.el = dh.append(ctr.dom, {
36665                 tag: "div",
36666                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36667             }, true);
36668         /** This region's title element 
36669         * @type Roo.Element */
36670     
36671         this.titleEl = dh.append(this.el.dom,  {
36672                 tag: "div",
36673                 unselectable: "on",
36674                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36675                 children:[
36676                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
36677                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36678                 ]
36679             }, true);
36680         
36681         this.titleEl.enableDisplayMode();
36682         /** This region's title text element 
36683         * @type HTMLElement */
36684         this.titleTextEl = this.titleEl.dom.firstChild;
36685         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36686         /*
36687         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36688         this.closeBtn.enableDisplayMode();
36689         this.closeBtn.on("click", this.closeClicked, this);
36690         this.closeBtn.hide();
36691     */
36692         this.createBody(this.config);
36693         if(this.config.hideWhenEmpty){
36694             this.hide();
36695             this.on("paneladded", this.validateVisibility, this);
36696             this.on("panelremoved", this.validateVisibility, this);
36697         }
36698         if(this.autoScroll){
36699             this.bodyEl.setStyle("overflow", "auto");
36700         }else{
36701             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36702         }
36703         //if(c.titlebar !== false){
36704             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36705                 this.titleEl.hide();
36706             }else{
36707                 this.titleEl.show();
36708                 if(this.config.title){
36709                     this.titleTextEl.innerHTML = this.config.title;
36710                 }
36711             }
36712         //}
36713         if(this.config.collapsed){
36714             this.collapse(true);
36715         }
36716         if(this.config.hidden){
36717             this.hide();
36718         }
36719         
36720         if (this.unrendered_panels && this.unrendered_panels.length) {
36721             for (var i =0;i< this.unrendered_panels.length; i++) {
36722                 this.add(this.unrendered_panels[i]);
36723             }
36724             this.unrendered_panels = null;
36725             
36726         }
36727         
36728     },
36729     
36730     applyConfig : function(c)
36731     {
36732         /*
36733          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36734             var dh = Roo.DomHelper;
36735             if(c.titlebar !== false){
36736                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36737                 this.collapseBtn.on("click", this.collapse, this);
36738                 this.collapseBtn.enableDisplayMode();
36739                 /*
36740                 if(c.showPin === true || this.showPin){
36741                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36742                     this.stickBtn.enableDisplayMode();
36743                     this.stickBtn.on("click", this.expand, this);
36744                     this.stickBtn.hide();
36745                 }
36746                 
36747             }
36748             */
36749             /** This region's collapsed element
36750             * @type Roo.Element */
36751             /*
36752              *
36753             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36754                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36755             ]}, true);
36756             
36757             if(c.floatable !== false){
36758                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36759                this.collapsedEl.on("click", this.collapseClick, this);
36760             }
36761
36762             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36763                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36764                    id: "message", unselectable: "on", style:{"float":"left"}});
36765                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36766              }
36767             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36768             this.expandBtn.on("click", this.expand, this);
36769             
36770         }
36771         
36772         if(this.collapseBtn){
36773             this.collapseBtn.setVisible(c.collapsible == true);
36774         }
36775         
36776         this.cmargins = c.cmargins || this.cmargins ||
36777                          (this.position == "west" || this.position == "east" ?
36778                              {top: 0, left: 2, right:2, bottom: 0} :
36779                              {top: 2, left: 0, right:0, bottom: 2});
36780         */
36781         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36782         
36783         
36784         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36785         
36786         this.autoScroll = c.autoScroll || false;
36787         
36788         
36789        
36790         
36791         this.duration = c.duration || .30;
36792         this.slideDuration = c.slideDuration || .45;
36793         this.config = c;
36794        
36795     },
36796     /**
36797      * Returns true if this region is currently visible.
36798      * @return {Boolean}
36799      */
36800     isVisible : function(){
36801         return this.visible;
36802     },
36803
36804     /**
36805      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36806      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36807      */
36808     //setCollapsedTitle : function(title){
36809     //    title = title || "&#160;";
36810      //   if(this.collapsedTitleTextEl){
36811       //      this.collapsedTitleTextEl.innerHTML = title;
36812        // }
36813     //},
36814
36815     getBox : function(){
36816         var b;
36817       //  if(!this.collapsed){
36818             b = this.el.getBox(false, true);
36819        // }else{
36820           //  b = this.collapsedEl.getBox(false, true);
36821         //}
36822         return b;
36823     },
36824
36825     getMargins : function(){
36826         return this.margins;
36827         //return this.collapsed ? this.cmargins : this.margins;
36828     },
36829 /*
36830     highlight : function(){
36831         this.el.addClass("x-layout-panel-dragover");
36832     },
36833
36834     unhighlight : function(){
36835         this.el.removeClass("x-layout-panel-dragover");
36836     },
36837 */
36838     updateBox : function(box)
36839     {
36840         if (!this.bodyEl) {
36841             return; // not rendered yet..
36842         }
36843         
36844         this.box = box;
36845         if(!this.collapsed){
36846             this.el.dom.style.left = box.x + "px";
36847             this.el.dom.style.top = box.y + "px";
36848             this.updateBody(box.width, box.height);
36849         }else{
36850             this.collapsedEl.dom.style.left = box.x + "px";
36851             this.collapsedEl.dom.style.top = box.y + "px";
36852             this.collapsedEl.setSize(box.width, box.height);
36853         }
36854         if(this.tabs){
36855             this.tabs.autoSizeTabs();
36856         }
36857     },
36858
36859     updateBody : function(w, h)
36860     {
36861         if(w !== null){
36862             this.el.setWidth(w);
36863             w -= this.el.getBorderWidth("rl");
36864             if(this.config.adjustments){
36865                 w += this.config.adjustments[0];
36866             }
36867         }
36868         if(h !== null && h > 0){
36869             this.el.setHeight(h);
36870             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36871             h -= this.el.getBorderWidth("tb");
36872             if(this.config.adjustments){
36873                 h += this.config.adjustments[1];
36874             }
36875             this.bodyEl.setHeight(h);
36876             if(this.tabs){
36877                 h = this.tabs.syncHeight(h);
36878             }
36879         }
36880         if(this.panelSize){
36881             w = w !== null ? w : this.panelSize.width;
36882             h = h !== null ? h : this.panelSize.height;
36883         }
36884         if(this.activePanel){
36885             var el = this.activePanel.getEl();
36886             w = w !== null ? w : el.getWidth();
36887             h = h !== null ? h : el.getHeight();
36888             this.panelSize = {width: w, height: h};
36889             this.activePanel.setSize(w, h);
36890         }
36891         if(Roo.isIE && this.tabs){
36892             this.tabs.el.repaint();
36893         }
36894     },
36895
36896     /**
36897      * Returns the container element for this region.
36898      * @return {Roo.Element}
36899      */
36900     getEl : function(){
36901         return this.el;
36902     },
36903
36904     /**
36905      * Hides this region.
36906      */
36907     hide : function(){
36908         //if(!this.collapsed){
36909             this.el.dom.style.left = "-2000px";
36910             this.el.hide();
36911         //}else{
36912          //   this.collapsedEl.dom.style.left = "-2000px";
36913          //   this.collapsedEl.hide();
36914        // }
36915         this.visible = false;
36916         this.fireEvent("visibilitychange", this, false);
36917     },
36918
36919     /**
36920      * Shows this region if it was previously hidden.
36921      */
36922     show : function(){
36923         //if(!this.collapsed){
36924             this.el.show();
36925         //}else{
36926         //    this.collapsedEl.show();
36927        // }
36928         this.visible = true;
36929         this.fireEvent("visibilitychange", this, true);
36930     },
36931 /*
36932     closeClicked : function(){
36933         if(this.activePanel){
36934             this.remove(this.activePanel);
36935         }
36936     },
36937
36938     collapseClick : function(e){
36939         if(this.isSlid){
36940            e.stopPropagation();
36941            this.slideIn();
36942         }else{
36943            e.stopPropagation();
36944            this.slideOut();
36945         }
36946     },
36947 */
36948     /**
36949      * Collapses this region.
36950      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36951      */
36952     /*
36953     collapse : function(skipAnim, skipCheck = false){
36954         if(this.collapsed) {
36955             return;
36956         }
36957         
36958         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36959             
36960             this.collapsed = true;
36961             if(this.split){
36962                 this.split.el.hide();
36963             }
36964             if(this.config.animate && skipAnim !== true){
36965                 this.fireEvent("invalidated", this);
36966                 this.animateCollapse();
36967             }else{
36968                 this.el.setLocation(-20000,-20000);
36969                 this.el.hide();
36970                 this.collapsedEl.show();
36971                 this.fireEvent("collapsed", this);
36972                 this.fireEvent("invalidated", this);
36973             }
36974         }
36975         
36976     },
36977 */
36978     animateCollapse : function(){
36979         // overridden
36980     },
36981
36982     /**
36983      * Expands this region if it was previously collapsed.
36984      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36985      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36986      */
36987     /*
36988     expand : function(e, skipAnim){
36989         if(e) {
36990             e.stopPropagation();
36991         }
36992         if(!this.collapsed || this.el.hasActiveFx()) {
36993             return;
36994         }
36995         if(this.isSlid){
36996             this.afterSlideIn();
36997             skipAnim = true;
36998         }
36999         this.collapsed = false;
37000         if(this.config.animate && skipAnim !== true){
37001             this.animateExpand();
37002         }else{
37003             this.el.show();
37004             if(this.split){
37005                 this.split.el.show();
37006             }
37007             this.collapsedEl.setLocation(-2000,-2000);
37008             this.collapsedEl.hide();
37009             this.fireEvent("invalidated", this);
37010             this.fireEvent("expanded", this);
37011         }
37012     },
37013 */
37014     animateExpand : function(){
37015         // overridden
37016     },
37017
37018     initTabs : function()
37019     {
37020         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
37021         
37022         var ts = new Roo.bootstrap.panel.Tabs({
37023             el: this.bodyEl.dom,
37024             region : this,
37025             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
37026             disableTooltips: this.config.disableTabTips,
37027             toolbar : this.config.toolbar
37028         });
37029         
37030         if(this.config.hideTabs){
37031             ts.stripWrap.setDisplayed(false);
37032         }
37033         this.tabs = ts;
37034         ts.resizeTabs = this.config.resizeTabs === true;
37035         ts.minTabWidth = this.config.minTabWidth || 40;
37036         ts.maxTabWidth = this.config.maxTabWidth || 250;
37037         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
37038         ts.monitorResize = false;
37039         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
37040         ts.bodyEl.addClass('roo-layout-tabs-body');
37041         this.panels.each(this.initPanelAsTab, this);
37042     },
37043
37044     initPanelAsTab : function(panel){
37045         var ti = this.tabs.addTab(
37046             panel.getEl().id,
37047             panel.getTitle(),
37048             null,
37049             this.config.closeOnTab && panel.isClosable(),
37050             panel.tpl
37051         );
37052         if(panel.tabTip !== undefined){
37053             ti.setTooltip(panel.tabTip);
37054         }
37055         ti.on("activate", function(){
37056               this.setActivePanel(panel);
37057         }, this);
37058         
37059         if(this.config.closeOnTab){
37060             ti.on("beforeclose", function(t, e){
37061                 e.cancel = true;
37062                 this.remove(panel);
37063             }, this);
37064         }
37065         
37066         panel.tabItem = ti;
37067         
37068         return ti;
37069     },
37070
37071     updatePanelTitle : function(panel, title)
37072     {
37073         if(this.activePanel == panel){
37074             this.updateTitle(title);
37075         }
37076         if(this.tabs){
37077             var ti = this.tabs.getTab(panel.getEl().id);
37078             ti.setText(title);
37079             if(panel.tabTip !== undefined){
37080                 ti.setTooltip(panel.tabTip);
37081             }
37082         }
37083     },
37084
37085     updateTitle : function(title){
37086         if(this.titleTextEl && !this.config.title){
37087             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
37088         }
37089     },
37090
37091     setActivePanel : function(panel)
37092     {
37093         panel = this.getPanel(panel);
37094         if(this.activePanel && this.activePanel != panel){
37095             if(this.activePanel.setActiveState(false) === false){
37096                 return;
37097             }
37098         }
37099         this.activePanel = panel;
37100         panel.setActiveState(true);
37101         if(this.panelSize){
37102             panel.setSize(this.panelSize.width, this.panelSize.height);
37103         }
37104         if(this.closeBtn){
37105             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
37106         }
37107         this.updateTitle(panel.getTitle());
37108         if(this.tabs){
37109             this.fireEvent("invalidated", this);
37110         }
37111         this.fireEvent("panelactivated", this, panel);
37112     },
37113
37114     /**
37115      * Shows the specified panel.
37116      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
37117      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
37118      */
37119     showPanel : function(panel)
37120     {
37121         panel = this.getPanel(panel);
37122         if(panel){
37123             if(this.tabs){
37124                 var tab = this.tabs.getTab(panel.getEl().id);
37125                 if(tab.isHidden()){
37126                     this.tabs.unhideTab(tab.id);
37127                 }
37128                 tab.activate();
37129             }else{
37130                 this.setActivePanel(panel);
37131             }
37132         }
37133         return panel;
37134     },
37135
37136     /**
37137      * Get the active panel for this region.
37138      * @return {Roo.ContentPanel} The active panel or null
37139      */
37140     getActivePanel : function(){
37141         return this.activePanel;
37142     },
37143
37144     validateVisibility : function(){
37145         if(this.panels.getCount() < 1){
37146             this.updateTitle("&#160;");
37147             this.closeBtn.hide();
37148             this.hide();
37149         }else{
37150             if(!this.isVisible()){
37151                 this.show();
37152             }
37153         }
37154     },
37155
37156     /**
37157      * Adds the passed ContentPanel(s) to this region.
37158      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37159      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
37160      */
37161     add : function(panel)
37162     {
37163         if(arguments.length > 1){
37164             for(var i = 0, len = arguments.length; i < len; i++) {
37165                 this.add(arguments[i]);
37166             }
37167             return null;
37168         }
37169         
37170         // if we have not been rendered yet, then we can not really do much of this..
37171         if (!this.bodyEl) {
37172             this.unrendered_panels.push(panel);
37173             return panel;
37174         }
37175         
37176         
37177         
37178         
37179         if(this.hasPanel(panel)){
37180             this.showPanel(panel);
37181             return panel;
37182         }
37183         panel.setRegion(this);
37184         this.panels.add(panel);
37185        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
37186             // sinle panel - no tab...?? would it not be better to render it with the tabs,
37187             // and hide them... ???
37188             this.bodyEl.dom.appendChild(panel.getEl().dom);
37189             if(panel.background !== true){
37190                 this.setActivePanel(panel);
37191             }
37192             this.fireEvent("paneladded", this, panel);
37193             return panel;
37194         }
37195         */
37196         if(!this.tabs){
37197             this.initTabs();
37198         }else{
37199             this.initPanelAsTab(panel);
37200         }
37201         
37202         
37203         if(panel.background !== true){
37204             this.tabs.activate(panel.getEl().id);
37205         }
37206         this.fireEvent("paneladded", this, panel);
37207         return panel;
37208     },
37209
37210     /**
37211      * Hides the tab for the specified panel.
37212      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37213      */
37214     hidePanel : function(panel){
37215         if(this.tabs && (panel = this.getPanel(panel))){
37216             this.tabs.hideTab(panel.getEl().id);
37217         }
37218     },
37219
37220     /**
37221      * Unhides the tab for a previously hidden panel.
37222      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37223      */
37224     unhidePanel : function(panel){
37225         if(this.tabs && (panel = this.getPanel(panel))){
37226             this.tabs.unhideTab(panel.getEl().id);
37227         }
37228     },
37229
37230     clearPanels : function(){
37231         while(this.panels.getCount() > 0){
37232              this.remove(this.panels.first());
37233         }
37234     },
37235
37236     /**
37237      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37238      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37239      * @param {Boolean} preservePanel Overrides the config preservePanel option
37240      * @return {Roo.ContentPanel} The panel that was removed
37241      */
37242     remove : function(panel, preservePanel)
37243     {
37244         panel = this.getPanel(panel);
37245         if(!panel){
37246             return null;
37247         }
37248         var e = {};
37249         this.fireEvent("beforeremove", this, panel, e);
37250         if(e.cancel === true){
37251             return null;
37252         }
37253         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
37254         var panelId = panel.getId();
37255         this.panels.removeKey(panelId);
37256         if(preservePanel){
37257             document.body.appendChild(panel.getEl().dom);
37258         }
37259         if(this.tabs){
37260             this.tabs.removeTab(panel.getEl().id);
37261         }else if (!preservePanel){
37262             this.bodyEl.dom.removeChild(panel.getEl().dom);
37263         }
37264         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
37265             var p = this.panels.first();
37266             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
37267             tempEl.appendChild(p.getEl().dom);
37268             this.bodyEl.update("");
37269             this.bodyEl.dom.appendChild(p.getEl().dom);
37270             tempEl = null;
37271             this.updateTitle(p.getTitle());
37272             this.tabs = null;
37273             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
37274             this.setActivePanel(p);
37275         }
37276         panel.setRegion(null);
37277         if(this.activePanel == panel){
37278             this.activePanel = null;
37279         }
37280         if(this.config.autoDestroy !== false && preservePanel !== true){
37281             try{panel.destroy();}catch(e){}
37282         }
37283         this.fireEvent("panelremoved", this, panel);
37284         return panel;
37285     },
37286
37287     /**
37288      * Returns the TabPanel component used by this region
37289      * @return {Roo.TabPanel}
37290      */
37291     getTabs : function(){
37292         return this.tabs;
37293     },
37294
37295     createTool : function(parentEl, className){
37296         var btn = Roo.DomHelper.append(parentEl, {
37297             tag: "div",
37298             cls: "x-layout-tools-button",
37299             children: [ {
37300                 tag: "div",
37301                 cls: "roo-layout-tools-button-inner " + className,
37302                 html: "&#160;"
37303             }]
37304         }, true);
37305         btn.addClassOnOver("roo-layout-tools-button-over");
37306         return btn;
37307     }
37308 });/*
37309  * Based on:
37310  * Ext JS Library 1.1.1
37311  * Copyright(c) 2006-2007, Ext JS, LLC.
37312  *
37313  * Originally Released Under LGPL - original licence link has changed is not relivant.
37314  *
37315  * Fork - LGPL
37316  * <script type="text/javascript">
37317  */
37318  
37319
37320
37321 /**
37322  * @class Roo.SplitLayoutRegion
37323  * @extends Roo.LayoutRegion
37324  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
37325  */
37326 Roo.bootstrap.layout.Split = function(config){
37327     this.cursor = config.cursor;
37328     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
37329 };
37330
37331 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
37332 {
37333     splitTip : "Drag to resize.",
37334     collapsibleSplitTip : "Drag to resize. Double click to hide.",
37335     useSplitTips : false,
37336
37337     applyConfig : function(config){
37338         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
37339     },
37340     
37341     onRender : function(ctr,pos) {
37342         
37343         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
37344         if(!this.config.split){
37345             return;
37346         }
37347         if(!this.split){
37348             
37349             var splitEl = Roo.DomHelper.append(ctr.dom,  {
37350                             tag: "div",
37351                             id: this.el.id + "-split",
37352                             cls: "roo-layout-split roo-layout-split-"+this.position,
37353                             html: "&#160;"
37354             });
37355             /** The SplitBar for this region 
37356             * @type Roo.SplitBar */
37357             // does not exist yet...
37358             Roo.log([this.position, this.orientation]);
37359             
37360             this.split = new Roo.bootstrap.SplitBar({
37361                 dragElement : splitEl,
37362                 resizingElement: this.el,
37363                 orientation : this.orientation
37364             });
37365             
37366             this.split.on("moved", this.onSplitMove, this);
37367             this.split.useShim = this.config.useShim === true;
37368             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
37369             if(this.useSplitTips){
37370                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
37371             }
37372             //if(config.collapsible){
37373             //    this.split.el.on("dblclick", this.collapse,  this);
37374             //}
37375         }
37376         if(typeof this.config.minSize != "undefined"){
37377             this.split.minSize = this.config.minSize;
37378         }
37379         if(typeof this.config.maxSize != "undefined"){
37380             this.split.maxSize = this.config.maxSize;
37381         }
37382         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
37383             this.hideSplitter();
37384         }
37385         
37386     },
37387
37388     getHMaxSize : function(){
37389          var cmax = this.config.maxSize || 10000;
37390          var center = this.mgr.getRegion("center");
37391          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
37392     },
37393
37394     getVMaxSize : function(){
37395          var cmax = this.config.maxSize || 10000;
37396          var center = this.mgr.getRegion("center");
37397          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
37398     },
37399
37400     onSplitMove : function(split, newSize){
37401         this.fireEvent("resized", this, newSize);
37402     },
37403     
37404     /** 
37405      * Returns the {@link Roo.SplitBar} for this region.
37406      * @return {Roo.SplitBar}
37407      */
37408     getSplitBar : function(){
37409         return this.split;
37410     },
37411     
37412     hide : function(){
37413         this.hideSplitter();
37414         Roo.bootstrap.layout.Split.superclass.hide.call(this);
37415     },
37416
37417     hideSplitter : function(){
37418         if(this.split){
37419             this.split.el.setLocation(-2000,-2000);
37420             this.split.el.hide();
37421         }
37422     },
37423
37424     show : function(){
37425         if(this.split){
37426             this.split.el.show();
37427         }
37428         Roo.bootstrap.layout.Split.superclass.show.call(this);
37429     },
37430     
37431     beforeSlide: function(){
37432         if(Roo.isGecko){// firefox overflow auto bug workaround
37433             this.bodyEl.clip();
37434             if(this.tabs) {
37435                 this.tabs.bodyEl.clip();
37436             }
37437             if(this.activePanel){
37438                 this.activePanel.getEl().clip();
37439                 
37440                 if(this.activePanel.beforeSlide){
37441                     this.activePanel.beforeSlide();
37442                 }
37443             }
37444         }
37445     },
37446     
37447     afterSlide : function(){
37448         if(Roo.isGecko){// firefox overflow auto bug workaround
37449             this.bodyEl.unclip();
37450             if(this.tabs) {
37451                 this.tabs.bodyEl.unclip();
37452             }
37453             if(this.activePanel){
37454                 this.activePanel.getEl().unclip();
37455                 if(this.activePanel.afterSlide){
37456                     this.activePanel.afterSlide();
37457                 }
37458             }
37459         }
37460     },
37461
37462     initAutoHide : function(){
37463         if(this.autoHide !== false){
37464             if(!this.autoHideHd){
37465                 var st = new Roo.util.DelayedTask(this.slideIn, this);
37466                 this.autoHideHd = {
37467                     "mouseout": function(e){
37468                         if(!e.within(this.el, true)){
37469                             st.delay(500);
37470                         }
37471                     },
37472                     "mouseover" : function(e){
37473                         st.cancel();
37474                     },
37475                     scope : this
37476                 };
37477             }
37478             this.el.on(this.autoHideHd);
37479         }
37480     },
37481
37482     clearAutoHide : function(){
37483         if(this.autoHide !== false){
37484             this.el.un("mouseout", this.autoHideHd.mouseout);
37485             this.el.un("mouseover", this.autoHideHd.mouseover);
37486         }
37487     },
37488
37489     clearMonitor : function(){
37490         Roo.get(document).un("click", this.slideInIf, this);
37491     },
37492
37493     // these names are backwards but not changed for compat
37494     slideOut : function(){
37495         if(this.isSlid || this.el.hasActiveFx()){
37496             return;
37497         }
37498         this.isSlid = true;
37499         if(this.collapseBtn){
37500             this.collapseBtn.hide();
37501         }
37502         this.closeBtnState = this.closeBtn.getStyle('display');
37503         this.closeBtn.hide();
37504         if(this.stickBtn){
37505             this.stickBtn.show();
37506         }
37507         this.el.show();
37508         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
37509         this.beforeSlide();
37510         this.el.setStyle("z-index", 10001);
37511         this.el.slideIn(this.getSlideAnchor(), {
37512             callback: function(){
37513                 this.afterSlide();
37514                 this.initAutoHide();
37515                 Roo.get(document).on("click", this.slideInIf, this);
37516                 this.fireEvent("slideshow", this);
37517             },
37518             scope: this,
37519             block: true
37520         });
37521     },
37522
37523     afterSlideIn : function(){
37524         this.clearAutoHide();
37525         this.isSlid = false;
37526         this.clearMonitor();
37527         this.el.setStyle("z-index", "");
37528         if(this.collapseBtn){
37529             this.collapseBtn.show();
37530         }
37531         this.closeBtn.setStyle('display', this.closeBtnState);
37532         if(this.stickBtn){
37533             this.stickBtn.hide();
37534         }
37535         this.fireEvent("slidehide", this);
37536     },
37537
37538     slideIn : function(cb){
37539         if(!this.isSlid || this.el.hasActiveFx()){
37540             Roo.callback(cb);
37541             return;
37542         }
37543         this.isSlid = false;
37544         this.beforeSlide();
37545         this.el.slideOut(this.getSlideAnchor(), {
37546             callback: function(){
37547                 this.el.setLeftTop(-10000, -10000);
37548                 this.afterSlide();
37549                 this.afterSlideIn();
37550                 Roo.callback(cb);
37551             },
37552             scope: this,
37553             block: true
37554         });
37555     },
37556     
37557     slideInIf : function(e){
37558         if(!e.within(this.el)){
37559             this.slideIn();
37560         }
37561     },
37562
37563     animateCollapse : function(){
37564         this.beforeSlide();
37565         this.el.setStyle("z-index", 20000);
37566         var anchor = this.getSlideAnchor();
37567         this.el.slideOut(anchor, {
37568             callback : function(){
37569                 this.el.setStyle("z-index", "");
37570                 this.collapsedEl.slideIn(anchor, {duration:.3});
37571                 this.afterSlide();
37572                 this.el.setLocation(-10000,-10000);
37573                 this.el.hide();
37574                 this.fireEvent("collapsed", this);
37575             },
37576             scope: this,
37577             block: true
37578         });
37579     },
37580
37581     animateExpand : function(){
37582         this.beforeSlide();
37583         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37584         this.el.setStyle("z-index", 20000);
37585         this.collapsedEl.hide({
37586             duration:.1
37587         });
37588         this.el.slideIn(this.getSlideAnchor(), {
37589             callback : function(){
37590                 this.el.setStyle("z-index", "");
37591                 this.afterSlide();
37592                 if(this.split){
37593                     this.split.el.show();
37594                 }
37595                 this.fireEvent("invalidated", this);
37596                 this.fireEvent("expanded", this);
37597             },
37598             scope: this,
37599             block: true
37600         });
37601     },
37602
37603     anchors : {
37604         "west" : "left",
37605         "east" : "right",
37606         "north" : "top",
37607         "south" : "bottom"
37608     },
37609
37610     sanchors : {
37611         "west" : "l",
37612         "east" : "r",
37613         "north" : "t",
37614         "south" : "b"
37615     },
37616
37617     canchors : {
37618         "west" : "tl-tr",
37619         "east" : "tr-tl",
37620         "north" : "tl-bl",
37621         "south" : "bl-tl"
37622     },
37623
37624     getAnchor : function(){
37625         return this.anchors[this.position];
37626     },
37627
37628     getCollapseAnchor : function(){
37629         return this.canchors[this.position];
37630     },
37631
37632     getSlideAnchor : function(){
37633         return this.sanchors[this.position];
37634     },
37635
37636     getAlignAdj : function(){
37637         var cm = this.cmargins;
37638         switch(this.position){
37639             case "west":
37640                 return [0, 0];
37641             break;
37642             case "east":
37643                 return [0, 0];
37644             break;
37645             case "north":
37646                 return [0, 0];
37647             break;
37648             case "south":
37649                 return [0, 0];
37650             break;
37651         }
37652     },
37653
37654     getExpandAdj : function(){
37655         var c = this.collapsedEl, cm = this.cmargins;
37656         switch(this.position){
37657             case "west":
37658                 return [-(cm.right+c.getWidth()+cm.left), 0];
37659             break;
37660             case "east":
37661                 return [cm.right+c.getWidth()+cm.left, 0];
37662             break;
37663             case "north":
37664                 return [0, -(cm.top+cm.bottom+c.getHeight())];
37665             break;
37666             case "south":
37667                 return [0, cm.top+cm.bottom+c.getHeight()];
37668             break;
37669         }
37670     }
37671 });/*
37672  * Based on:
37673  * Ext JS Library 1.1.1
37674  * Copyright(c) 2006-2007, Ext JS, LLC.
37675  *
37676  * Originally Released Under LGPL - original licence link has changed is not relivant.
37677  *
37678  * Fork - LGPL
37679  * <script type="text/javascript">
37680  */
37681 /*
37682  * These classes are private internal classes
37683  */
37684 Roo.bootstrap.layout.Center = function(config){
37685     config.region = "center";
37686     Roo.bootstrap.layout.Region.call(this, config);
37687     this.visible = true;
37688     this.minWidth = config.minWidth || 20;
37689     this.minHeight = config.minHeight || 20;
37690 };
37691
37692 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37693     hide : function(){
37694         // center panel can't be hidden
37695     },
37696     
37697     show : function(){
37698         // center panel can't be hidden
37699     },
37700     
37701     getMinWidth: function(){
37702         return this.minWidth;
37703     },
37704     
37705     getMinHeight: function(){
37706         return this.minHeight;
37707     }
37708 });
37709
37710
37711
37712
37713  
37714
37715
37716
37717
37718
37719
37720 Roo.bootstrap.layout.North = function(config)
37721 {
37722     config.region = 'north';
37723     config.cursor = 'n-resize';
37724     
37725     Roo.bootstrap.layout.Split.call(this, config);
37726     
37727     
37728     if(this.split){
37729         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37730         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37731         this.split.el.addClass("roo-layout-split-v");
37732     }
37733     var size = config.initialSize || config.height;
37734     if(typeof size != "undefined"){
37735         this.el.setHeight(size);
37736     }
37737 };
37738 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37739 {
37740     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37741     
37742     
37743     
37744     getBox : function(){
37745         if(this.collapsed){
37746             return this.collapsedEl.getBox();
37747         }
37748         var box = this.el.getBox();
37749         if(this.split){
37750             box.height += this.split.el.getHeight();
37751         }
37752         return box;
37753     },
37754     
37755     updateBox : function(box){
37756         if(this.split && !this.collapsed){
37757             box.height -= this.split.el.getHeight();
37758             this.split.el.setLeft(box.x);
37759             this.split.el.setTop(box.y+box.height);
37760             this.split.el.setWidth(box.width);
37761         }
37762         if(this.collapsed){
37763             this.updateBody(box.width, null);
37764         }
37765         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37766     }
37767 });
37768
37769
37770
37771
37772
37773 Roo.bootstrap.layout.South = function(config){
37774     config.region = 'south';
37775     config.cursor = 's-resize';
37776     Roo.bootstrap.layout.Split.call(this, config);
37777     if(this.split){
37778         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37779         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37780         this.split.el.addClass("roo-layout-split-v");
37781     }
37782     var size = config.initialSize || config.height;
37783     if(typeof size != "undefined"){
37784         this.el.setHeight(size);
37785     }
37786 };
37787
37788 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37789     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37790     getBox : function(){
37791         if(this.collapsed){
37792             return this.collapsedEl.getBox();
37793         }
37794         var box = this.el.getBox();
37795         if(this.split){
37796             var sh = this.split.el.getHeight();
37797             box.height += sh;
37798             box.y -= sh;
37799         }
37800         return box;
37801     },
37802     
37803     updateBox : function(box){
37804         if(this.split && !this.collapsed){
37805             var sh = this.split.el.getHeight();
37806             box.height -= sh;
37807             box.y += sh;
37808             this.split.el.setLeft(box.x);
37809             this.split.el.setTop(box.y-sh);
37810             this.split.el.setWidth(box.width);
37811         }
37812         if(this.collapsed){
37813             this.updateBody(box.width, null);
37814         }
37815         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37816     }
37817 });
37818
37819 Roo.bootstrap.layout.East = function(config){
37820     config.region = "east";
37821     config.cursor = "e-resize";
37822     Roo.bootstrap.layout.Split.call(this, config);
37823     if(this.split){
37824         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37825         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37826         this.split.el.addClass("roo-layout-split-h");
37827     }
37828     var size = config.initialSize || config.width;
37829     if(typeof size != "undefined"){
37830         this.el.setWidth(size);
37831     }
37832 };
37833 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37834     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37835     getBox : function(){
37836         if(this.collapsed){
37837             return this.collapsedEl.getBox();
37838         }
37839         var box = this.el.getBox();
37840         if(this.split){
37841             var sw = this.split.el.getWidth();
37842             box.width += sw;
37843             box.x -= sw;
37844         }
37845         return box;
37846     },
37847
37848     updateBox : function(box){
37849         if(this.split && !this.collapsed){
37850             var sw = this.split.el.getWidth();
37851             box.width -= sw;
37852             this.split.el.setLeft(box.x);
37853             this.split.el.setTop(box.y);
37854             this.split.el.setHeight(box.height);
37855             box.x += sw;
37856         }
37857         if(this.collapsed){
37858             this.updateBody(null, box.height);
37859         }
37860         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37861     }
37862 });
37863
37864 Roo.bootstrap.layout.West = function(config){
37865     config.region = "west";
37866     config.cursor = "w-resize";
37867     
37868     Roo.bootstrap.layout.Split.call(this, config);
37869     if(this.split){
37870         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37871         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37872         this.split.el.addClass("roo-layout-split-h");
37873     }
37874     
37875 };
37876 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37877     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37878     
37879     onRender: function(ctr, pos)
37880     {
37881         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37882         var size = this.config.initialSize || this.config.width;
37883         if(typeof size != "undefined"){
37884             this.el.setWidth(size);
37885         }
37886     },
37887     
37888     getBox : function(){
37889         if(this.collapsed){
37890             return this.collapsedEl.getBox();
37891         }
37892         var box = this.el.getBox();
37893         if(this.split){
37894             box.width += this.split.el.getWidth();
37895         }
37896         return box;
37897     },
37898     
37899     updateBox : function(box){
37900         if(this.split && !this.collapsed){
37901             var sw = this.split.el.getWidth();
37902             box.width -= sw;
37903             this.split.el.setLeft(box.x+box.width);
37904             this.split.el.setTop(box.y);
37905             this.split.el.setHeight(box.height);
37906         }
37907         if(this.collapsed){
37908             this.updateBody(null, box.height);
37909         }
37910         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37911     }
37912 });Roo.namespace("Roo.bootstrap.panel");/*
37913  * Based on:
37914  * Ext JS Library 1.1.1
37915  * Copyright(c) 2006-2007, Ext JS, LLC.
37916  *
37917  * Originally Released Under LGPL - original licence link has changed is not relivant.
37918  *
37919  * Fork - LGPL
37920  * <script type="text/javascript">
37921  */
37922 /**
37923  * @class Roo.ContentPanel
37924  * @extends Roo.util.Observable
37925  * A basic ContentPanel element.
37926  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37927  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37928  * @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
37929  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37930  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37931  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37932  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37933  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37934  * @cfg {String} title          The title for this panel
37935  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37936  * @cfg {String} url            Calls {@link #setUrl} with this value
37937  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37938  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37939  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37940  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37941  * @cfg {Boolean} badges render the badges
37942
37943  * @constructor
37944  * Create a new ContentPanel.
37945  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37946  * @param {String/Object} config A string to set only the title or a config object
37947  * @param {String} content (optional) Set the HTML content for this panel
37948  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37949  */
37950 Roo.bootstrap.panel.Content = function( config){
37951     
37952     this.tpl = config.tpl || false;
37953     
37954     var el = config.el;
37955     var content = config.content;
37956
37957     if(config.autoCreate){ // xtype is available if this is called from factory
37958         el = Roo.id();
37959     }
37960     this.el = Roo.get(el);
37961     if(!this.el && config && config.autoCreate){
37962         if(typeof config.autoCreate == "object"){
37963             if(!config.autoCreate.id){
37964                 config.autoCreate.id = config.id||el;
37965             }
37966             this.el = Roo.DomHelper.append(document.body,
37967                         config.autoCreate, true);
37968         }else{
37969             var elcfg =  {   tag: "div",
37970                             cls: "roo-layout-inactive-content",
37971                             id: config.id||el
37972                             };
37973             if (config.html) {
37974                 elcfg.html = config.html;
37975                 
37976             }
37977                         
37978             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37979         }
37980     } 
37981     this.closable = false;
37982     this.loaded = false;
37983     this.active = false;
37984    
37985       
37986     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37987         
37988         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37989         
37990         this.wrapEl = this.el; //this.el.wrap();
37991         var ti = [];
37992         if (config.toolbar.items) {
37993             ti = config.toolbar.items ;
37994             delete config.toolbar.items ;
37995         }
37996         
37997         var nitems = [];
37998         this.toolbar.render(this.wrapEl, 'before');
37999         for(var i =0;i < ti.length;i++) {
38000           //  Roo.log(['add child', items[i]]);
38001             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38002         }
38003         this.toolbar.items = nitems;
38004         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
38005         delete config.toolbar;
38006         
38007     }
38008     /*
38009     // xtype created footer. - not sure if will work as we normally have to render first..
38010     if (this.footer && !this.footer.el && this.footer.xtype) {
38011         if (!this.wrapEl) {
38012             this.wrapEl = this.el.wrap();
38013         }
38014     
38015         this.footer.container = this.wrapEl.createChild();
38016          
38017         this.footer = Roo.factory(this.footer, Roo);
38018         
38019     }
38020     */
38021     
38022      if(typeof config == "string"){
38023         this.title = config;
38024     }else{
38025         Roo.apply(this, config);
38026     }
38027     
38028     if(this.resizeEl){
38029         this.resizeEl = Roo.get(this.resizeEl, true);
38030     }else{
38031         this.resizeEl = this.el;
38032     }
38033     // handle view.xtype
38034     
38035  
38036     
38037     
38038     this.addEvents({
38039         /**
38040          * @event activate
38041          * Fires when this panel is activated. 
38042          * @param {Roo.ContentPanel} this
38043          */
38044         "activate" : true,
38045         /**
38046          * @event deactivate
38047          * Fires when this panel is activated. 
38048          * @param {Roo.ContentPanel} this
38049          */
38050         "deactivate" : true,
38051
38052         /**
38053          * @event resize
38054          * Fires when this panel is resized if fitToFrame is true.
38055          * @param {Roo.ContentPanel} this
38056          * @param {Number} width The width after any component adjustments
38057          * @param {Number} height The height after any component adjustments
38058          */
38059         "resize" : true,
38060         
38061          /**
38062          * @event render
38063          * Fires when this tab is created
38064          * @param {Roo.ContentPanel} this
38065          */
38066         "render" : true
38067         
38068         
38069         
38070     });
38071     
38072
38073     
38074     
38075     if(this.autoScroll){
38076         this.resizeEl.setStyle("overflow", "auto");
38077     } else {
38078         // fix randome scrolling
38079         //this.el.on('scroll', function() {
38080         //    Roo.log('fix random scolling');
38081         //    this.scrollTo('top',0); 
38082         //});
38083     }
38084     content = content || this.content;
38085     if(content){
38086         this.setContent(content);
38087     }
38088     if(config && config.url){
38089         this.setUrl(this.url, this.params, this.loadOnce);
38090     }
38091     
38092     
38093     
38094     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
38095     
38096     if (this.view && typeof(this.view.xtype) != 'undefined') {
38097         this.view.el = this.el.appendChild(document.createElement("div"));
38098         this.view = Roo.factory(this.view); 
38099         this.view.render  &&  this.view.render(false, '');  
38100     }
38101     
38102     
38103     this.fireEvent('render', this);
38104 };
38105
38106 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
38107     
38108     tabTip : '',
38109     
38110     setRegion : function(region){
38111         this.region = region;
38112         this.setActiveClass(region && !this.background);
38113     },
38114     
38115     
38116     setActiveClass: function(state)
38117     {
38118         if(state){
38119            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
38120            this.el.setStyle('position','relative');
38121         }else{
38122            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
38123            this.el.setStyle('position', 'absolute');
38124         } 
38125     },
38126     
38127     /**
38128      * Returns the toolbar for this Panel if one was configured. 
38129      * @return {Roo.Toolbar} 
38130      */
38131     getToolbar : function(){
38132         return this.toolbar;
38133     },
38134     
38135     setActiveState : function(active)
38136     {
38137         this.active = active;
38138         this.setActiveClass(active);
38139         if(!active){
38140             if(this.fireEvent("deactivate", this) === false){
38141                 return false;
38142             }
38143             return true;
38144         }
38145         this.fireEvent("activate", this);
38146         return true;
38147     },
38148     /**
38149      * Updates this panel's element
38150      * @param {String} content The new content
38151      * @param {Boolean} loadScripts (optional) true to look for and process scripts
38152     */
38153     setContent : function(content, loadScripts){
38154         this.el.update(content, loadScripts);
38155     },
38156
38157     ignoreResize : function(w, h){
38158         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
38159             return true;
38160         }else{
38161             this.lastSize = {width: w, height: h};
38162             return false;
38163         }
38164     },
38165     /**
38166      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
38167      * @return {Roo.UpdateManager} The UpdateManager
38168      */
38169     getUpdateManager : function(){
38170         return this.el.getUpdateManager();
38171     },
38172      /**
38173      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
38174      * @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:
38175 <pre><code>
38176 panel.load({
38177     url: "your-url.php",
38178     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
38179     callback: yourFunction,
38180     scope: yourObject, //(optional scope)
38181     discardUrl: false,
38182     nocache: false,
38183     text: "Loading...",
38184     timeout: 30,
38185     scripts: false
38186 });
38187 </code></pre>
38188      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
38189      * 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.
38190      * @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}
38191      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
38192      * @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.
38193      * @return {Roo.ContentPanel} this
38194      */
38195     load : function(){
38196         var um = this.el.getUpdateManager();
38197         um.update.apply(um, arguments);
38198         return this;
38199     },
38200
38201
38202     /**
38203      * 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.
38204      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
38205      * @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)
38206      * @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)
38207      * @return {Roo.UpdateManager} The UpdateManager
38208      */
38209     setUrl : function(url, params, loadOnce){
38210         if(this.refreshDelegate){
38211             this.removeListener("activate", this.refreshDelegate);
38212         }
38213         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38214         this.on("activate", this.refreshDelegate);
38215         return this.el.getUpdateManager();
38216     },
38217     
38218     _handleRefresh : function(url, params, loadOnce){
38219         if(!loadOnce || !this.loaded){
38220             var updater = this.el.getUpdateManager();
38221             updater.update(url, params, this._setLoaded.createDelegate(this));
38222         }
38223     },
38224     
38225     _setLoaded : function(){
38226         this.loaded = true;
38227     }, 
38228     
38229     /**
38230      * Returns this panel's id
38231      * @return {String} 
38232      */
38233     getId : function(){
38234         return this.el.id;
38235     },
38236     
38237     /** 
38238      * Returns this panel's element - used by regiosn to add.
38239      * @return {Roo.Element} 
38240      */
38241     getEl : function(){
38242         return this.wrapEl || this.el;
38243     },
38244     
38245    
38246     
38247     adjustForComponents : function(width, height)
38248     {
38249         //Roo.log('adjustForComponents ');
38250         if(this.resizeEl != this.el){
38251             width -= this.el.getFrameWidth('lr');
38252             height -= this.el.getFrameWidth('tb');
38253         }
38254         if(this.toolbar){
38255             var te = this.toolbar.getEl();
38256             te.setWidth(width);
38257             height -= te.getHeight();
38258         }
38259         if(this.footer){
38260             var te = this.footer.getEl();
38261             te.setWidth(width);
38262             height -= te.getHeight();
38263         }
38264         
38265         
38266         if(this.adjustments){
38267             width += this.adjustments[0];
38268             height += this.adjustments[1];
38269         }
38270         return {"width": width, "height": height};
38271     },
38272     
38273     setSize : function(width, height){
38274         if(this.fitToFrame && !this.ignoreResize(width, height)){
38275             if(this.fitContainer && this.resizeEl != this.el){
38276                 this.el.setSize(width, height);
38277             }
38278             var size = this.adjustForComponents(width, height);
38279             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
38280             this.fireEvent('resize', this, size.width, size.height);
38281         }
38282     },
38283     
38284     /**
38285      * Returns this panel's title
38286      * @return {String} 
38287      */
38288     getTitle : function(){
38289         
38290         if (typeof(this.title) != 'object') {
38291             return this.title;
38292         }
38293         
38294         var t = '';
38295         for (var k in this.title) {
38296             if (!this.title.hasOwnProperty(k)) {
38297                 continue;
38298             }
38299             
38300             if (k.indexOf('-') >= 0) {
38301                 var s = k.split('-');
38302                 for (var i = 0; i<s.length; i++) {
38303                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
38304                 }
38305             } else {
38306                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
38307             }
38308         }
38309         return t;
38310     },
38311     
38312     /**
38313      * Set this panel's title
38314      * @param {String} title
38315      */
38316     setTitle : function(title){
38317         this.title = title;
38318         if(this.region){
38319             this.region.updatePanelTitle(this, title);
38320         }
38321     },
38322     
38323     /**
38324      * Returns true is this panel was configured to be closable
38325      * @return {Boolean} 
38326      */
38327     isClosable : function(){
38328         return this.closable;
38329     },
38330     
38331     beforeSlide : function(){
38332         this.el.clip();
38333         this.resizeEl.clip();
38334     },
38335     
38336     afterSlide : function(){
38337         this.el.unclip();
38338         this.resizeEl.unclip();
38339     },
38340     
38341     /**
38342      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
38343      *   Will fail silently if the {@link #setUrl} method has not been called.
38344      *   This does not activate the panel, just updates its content.
38345      */
38346     refresh : function(){
38347         if(this.refreshDelegate){
38348            this.loaded = false;
38349            this.refreshDelegate();
38350         }
38351     },
38352     
38353     /**
38354      * Destroys this panel
38355      */
38356     destroy : function(){
38357         this.el.removeAllListeners();
38358         var tempEl = document.createElement("span");
38359         tempEl.appendChild(this.el.dom);
38360         tempEl.innerHTML = "";
38361         this.el.remove();
38362         this.el = null;
38363     },
38364     
38365     /**
38366      * form - if the content panel contains a form - this is a reference to it.
38367      * @type {Roo.form.Form}
38368      */
38369     form : false,
38370     /**
38371      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
38372      *    This contains a reference to it.
38373      * @type {Roo.View}
38374      */
38375     view : false,
38376     
38377       /**
38378      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
38379      * <pre><code>
38380
38381 layout.addxtype({
38382        xtype : 'Form',
38383        items: [ .... ]
38384    }
38385 );
38386
38387 </code></pre>
38388      * @param {Object} cfg Xtype definition of item to add.
38389      */
38390     
38391     
38392     getChildContainer: function () {
38393         return this.getEl();
38394     }
38395     
38396     
38397     /*
38398         var  ret = new Roo.factory(cfg);
38399         return ret;
38400         
38401         
38402         // add form..
38403         if (cfg.xtype.match(/^Form$/)) {
38404             
38405             var el;
38406             //if (this.footer) {
38407             //    el = this.footer.container.insertSibling(false, 'before');
38408             //} else {
38409                 el = this.el.createChild();
38410             //}
38411
38412             this.form = new  Roo.form.Form(cfg);
38413             
38414             
38415             if ( this.form.allItems.length) {
38416                 this.form.render(el.dom);
38417             }
38418             return this.form;
38419         }
38420         // should only have one of theses..
38421         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
38422             // views.. should not be just added - used named prop 'view''
38423             
38424             cfg.el = this.el.appendChild(document.createElement("div"));
38425             // factory?
38426             
38427             var ret = new Roo.factory(cfg);
38428              
38429              ret.render && ret.render(false, ''); // render blank..
38430             this.view = ret;
38431             return ret;
38432         }
38433         return false;
38434     }
38435     \*/
38436 });
38437  
38438 /**
38439  * @class Roo.bootstrap.panel.Grid
38440  * @extends Roo.bootstrap.panel.Content
38441  * @constructor
38442  * Create a new GridPanel.
38443  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
38444  * @param {Object} config A the config object
38445   
38446  */
38447
38448
38449
38450 Roo.bootstrap.panel.Grid = function(config)
38451 {
38452     
38453       
38454     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
38455         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
38456
38457     config.el = this.wrapper;
38458     //this.el = this.wrapper;
38459     
38460       if (config.container) {
38461         // ctor'ed from a Border/panel.grid
38462         
38463         
38464         this.wrapper.setStyle("overflow", "hidden");
38465         this.wrapper.addClass('roo-grid-container');
38466
38467     }
38468     
38469     
38470     if(config.toolbar){
38471         var tool_el = this.wrapper.createChild();    
38472         this.toolbar = Roo.factory(config.toolbar);
38473         var ti = [];
38474         if (config.toolbar.items) {
38475             ti = config.toolbar.items ;
38476             delete config.toolbar.items ;
38477         }
38478         
38479         var nitems = [];
38480         this.toolbar.render(tool_el);
38481         for(var i =0;i < ti.length;i++) {
38482           //  Roo.log(['add child', items[i]]);
38483             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38484         }
38485         this.toolbar.items = nitems;
38486         
38487         delete config.toolbar;
38488     }
38489     
38490     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
38491     config.grid.scrollBody = true;;
38492     config.grid.monitorWindowResize = false; // turn off autosizing
38493     config.grid.autoHeight = false;
38494     config.grid.autoWidth = false;
38495     
38496     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
38497     
38498     if (config.background) {
38499         // render grid on panel activation (if panel background)
38500         this.on('activate', function(gp) {
38501             if (!gp.grid.rendered) {
38502                 gp.grid.render(this.wrapper);
38503                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
38504             }
38505         });
38506             
38507     } else {
38508         this.grid.render(this.wrapper);
38509         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
38510
38511     }
38512     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
38513     // ??? needed ??? config.el = this.wrapper;
38514     
38515     
38516     
38517   
38518     // xtype created footer. - not sure if will work as we normally have to render first..
38519     if (this.footer && !this.footer.el && this.footer.xtype) {
38520         
38521         var ctr = this.grid.getView().getFooterPanel(true);
38522         this.footer.dataSource = this.grid.dataSource;
38523         this.footer = Roo.factory(this.footer, Roo);
38524         this.footer.render(ctr);
38525         
38526     }
38527     
38528     
38529     
38530     
38531      
38532 };
38533
38534 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38535     getId : function(){
38536         return this.grid.id;
38537     },
38538     
38539     /**
38540      * Returns the grid for this panel
38541      * @return {Roo.bootstrap.Table} 
38542      */
38543     getGrid : function(){
38544         return this.grid;    
38545     },
38546     
38547     setSize : function(width, height){
38548         if(!this.ignoreResize(width, height)){
38549             var grid = this.grid;
38550             var size = this.adjustForComponents(width, height);
38551             var gridel = grid.getGridEl();
38552             gridel.setSize(size.width, size.height);
38553             /*
38554             var thd = grid.getGridEl().select('thead',true).first();
38555             var tbd = grid.getGridEl().select('tbody', true).first();
38556             if (tbd) {
38557                 tbd.setSize(width, height - thd.getHeight());
38558             }
38559             */
38560             grid.autoSize();
38561         }
38562     },
38563      
38564     
38565     
38566     beforeSlide : function(){
38567         this.grid.getView().scroller.clip();
38568     },
38569     
38570     afterSlide : function(){
38571         this.grid.getView().scroller.unclip();
38572     },
38573     
38574     destroy : function(){
38575         this.grid.destroy();
38576         delete this.grid;
38577         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
38578     }
38579 });
38580
38581 /**
38582  * @class Roo.bootstrap.panel.Nest
38583  * @extends Roo.bootstrap.panel.Content
38584  * @constructor
38585  * Create a new Panel, that can contain a layout.Border.
38586  * 
38587  * 
38588  * @param {Roo.BorderLayout} layout The layout for this panel
38589  * @param {String/Object} config A string to set only the title or a config object
38590  */
38591 Roo.bootstrap.panel.Nest = function(config)
38592 {
38593     // construct with only one argument..
38594     /* FIXME - implement nicer consturctors
38595     if (layout.layout) {
38596         config = layout;
38597         layout = config.layout;
38598         delete config.layout;
38599     }
38600     if (layout.xtype && !layout.getEl) {
38601         // then layout needs constructing..
38602         layout = Roo.factory(layout, Roo);
38603     }
38604     */
38605     
38606     config.el =  config.layout.getEl();
38607     
38608     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
38609     
38610     config.layout.monitorWindowResize = false; // turn off autosizing
38611     this.layout = config.layout;
38612     this.layout.getEl().addClass("roo-layout-nested-layout");
38613     this.layout.parent = this;
38614     
38615     
38616     
38617     
38618 };
38619
38620 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38621
38622     setSize : function(width, height){
38623         if(!this.ignoreResize(width, height)){
38624             var size = this.adjustForComponents(width, height);
38625             var el = this.layout.getEl();
38626             if (size.height < 1) {
38627                 el.setWidth(size.width);   
38628             } else {
38629                 el.setSize(size.width, size.height);
38630             }
38631             var touch = el.dom.offsetWidth;
38632             this.layout.layout();
38633             // ie requires a double layout on the first pass
38634             if(Roo.isIE && !this.initialized){
38635                 this.initialized = true;
38636                 this.layout.layout();
38637             }
38638         }
38639     },
38640     
38641     // activate all subpanels if not currently active..
38642     
38643     setActiveState : function(active){
38644         this.active = active;
38645         this.setActiveClass(active);
38646         
38647         if(!active){
38648             this.fireEvent("deactivate", this);
38649             return;
38650         }
38651         
38652         this.fireEvent("activate", this);
38653         // not sure if this should happen before or after..
38654         if (!this.layout) {
38655             return; // should not happen..
38656         }
38657         var reg = false;
38658         for (var r in this.layout.regions) {
38659             reg = this.layout.getRegion(r);
38660             if (reg.getActivePanel()) {
38661                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
38662                 reg.setActivePanel(reg.getActivePanel());
38663                 continue;
38664             }
38665             if (!reg.panels.length) {
38666                 continue;
38667             }
38668             reg.showPanel(reg.getPanel(0));
38669         }
38670         
38671         
38672         
38673         
38674     },
38675     
38676     /**
38677      * Returns the nested BorderLayout for this panel
38678      * @return {Roo.BorderLayout} 
38679      */
38680     getLayout : function(){
38681         return this.layout;
38682     },
38683     
38684      /**
38685      * Adds a xtype elements to the layout of the nested panel
38686      * <pre><code>
38687
38688 panel.addxtype({
38689        xtype : 'ContentPanel',
38690        region: 'west',
38691        items: [ .... ]
38692    }
38693 );
38694
38695 panel.addxtype({
38696         xtype : 'NestedLayoutPanel',
38697         region: 'west',
38698         layout: {
38699            center: { },
38700            west: { }   
38701         },
38702         items : [ ... list of content panels or nested layout panels.. ]
38703    }
38704 );
38705 </code></pre>
38706      * @param {Object} cfg Xtype definition of item to add.
38707      */
38708     addxtype : function(cfg) {
38709         return this.layout.addxtype(cfg);
38710     
38711     }
38712 });/*
38713  * Based on:
38714  * Ext JS Library 1.1.1
38715  * Copyright(c) 2006-2007, Ext JS, LLC.
38716  *
38717  * Originally Released Under LGPL - original licence link has changed is not relivant.
38718  *
38719  * Fork - LGPL
38720  * <script type="text/javascript">
38721  */
38722 /**
38723  * @class Roo.TabPanel
38724  * @extends Roo.util.Observable
38725  * A lightweight tab container.
38726  * <br><br>
38727  * Usage:
38728  * <pre><code>
38729 // basic tabs 1, built from existing content
38730 var tabs = new Roo.TabPanel("tabs1");
38731 tabs.addTab("script", "View Script");
38732 tabs.addTab("markup", "View Markup");
38733 tabs.activate("script");
38734
38735 // more advanced tabs, built from javascript
38736 var jtabs = new Roo.TabPanel("jtabs");
38737 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38738
38739 // set up the UpdateManager
38740 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38741 var updater = tab2.getUpdateManager();
38742 updater.setDefaultUrl("ajax1.htm");
38743 tab2.on('activate', updater.refresh, updater, true);
38744
38745 // Use setUrl for Ajax loading
38746 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38747 tab3.setUrl("ajax2.htm", null, true);
38748
38749 // Disabled tab
38750 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38751 tab4.disable();
38752
38753 jtabs.activate("jtabs-1");
38754  * </code></pre>
38755  * @constructor
38756  * Create a new TabPanel.
38757  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38758  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38759  */
38760 Roo.bootstrap.panel.Tabs = function(config){
38761     /**
38762     * The container element for this TabPanel.
38763     * @type Roo.Element
38764     */
38765     this.el = Roo.get(config.el);
38766     delete config.el;
38767     if(config){
38768         if(typeof config == "boolean"){
38769             this.tabPosition = config ? "bottom" : "top";
38770         }else{
38771             Roo.apply(this, config);
38772         }
38773     }
38774     
38775     if(this.tabPosition == "bottom"){
38776         // if tabs are at the bottom = create the body first.
38777         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38778         this.el.addClass("roo-tabs-bottom");
38779     }
38780     // next create the tabs holders
38781     
38782     if (this.tabPosition == "west"){
38783         
38784         var reg = this.region; // fake it..
38785         while (reg) {
38786             if (!reg.mgr.parent) {
38787                 break;
38788             }
38789             reg = reg.mgr.parent.region;
38790         }
38791         Roo.log("got nest?");
38792         Roo.log(reg);
38793         if (reg.mgr.getRegion('west')) {
38794             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38795             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38796             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38797             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38798             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38799         
38800             
38801         }
38802         
38803         
38804     } else {
38805      
38806         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38807         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38808         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38809         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38810     }
38811     
38812     
38813     if(Roo.isIE){
38814         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38815     }
38816     
38817     // finally - if tabs are at the top, then create the body last..
38818     if(this.tabPosition != "bottom"){
38819         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38820          * @type Roo.Element
38821          */
38822         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38823         this.el.addClass("roo-tabs-top");
38824     }
38825     this.items = [];
38826
38827     this.bodyEl.setStyle("position", "relative");
38828
38829     this.active = null;
38830     this.activateDelegate = this.activate.createDelegate(this);
38831
38832     this.addEvents({
38833         /**
38834          * @event tabchange
38835          * Fires when the active tab changes
38836          * @param {Roo.TabPanel} this
38837          * @param {Roo.TabPanelItem} activePanel The new active tab
38838          */
38839         "tabchange": true,
38840         /**
38841          * @event beforetabchange
38842          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38843          * @param {Roo.TabPanel} this
38844          * @param {Object} e Set cancel to true on this object to cancel the tab change
38845          * @param {Roo.TabPanelItem} tab The tab being changed to
38846          */
38847         "beforetabchange" : true
38848     });
38849
38850     Roo.EventManager.onWindowResize(this.onResize, this);
38851     this.cpad = this.el.getPadding("lr");
38852     this.hiddenCount = 0;
38853
38854
38855     // toolbar on the tabbar support...
38856     if (this.toolbar) {
38857         alert("no toolbar support yet");
38858         this.toolbar  = false;
38859         /*
38860         var tcfg = this.toolbar;
38861         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38862         this.toolbar = new Roo.Toolbar(tcfg);
38863         if (Roo.isSafari) {
38864             var tbl = tcfg.container.child('table', true);
38865             tbl.setAttribute('width', '100%');
38866         }
38867         */
38868         
38869     }
38870    
38871
38872
38873     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38874 };
38875
38876 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38877     /*
38878      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38879      */
38880     tabPosition : "top",
38881     /*
38882      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38883      */
38884     currentTabWidth : 0,
38885     /*
38886      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38887      */
38888     minTabWidth : 40,
38889     /*
38890      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38891      */
38892     maxTabWidth : 250,
38893     /*
38894      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38895      */
38896     preferredTabWidth : 175,
38897     /*
38898      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38899      */
38900     resizeTabs : false,
38901     /*
38902      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38903      */
38904     monitorResize : true,
38905     /*
38906      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38907      */
38908     toolbar : false,  // set by caller..
38909     
38910     region : false, /// set by caller
38911     
38912     disableTooltips : true, // not used yet...
38913
38914     /**
38915      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38916      * @param {String} id The id of the div to use <b>or create</b>
38917      * @param {String} text The text for the tab
38918      * @param {String} content (optional) Content to put in the TabPanelItem body
38919      * @param {Boolean} closable (optional) True to create a close icon on the tab
38920      * @return {Roo.TabPanelItem} The created TabPanelItem
38921      */
38922     addTab : function(id, text, content, closable, tpl)
38923     {
38924         var item = new Roo.bootstrap.panel.TabItem({
38925             panel: this,
38926             id : id,
38927             text : text,
38928             closable : closable,
38929             tpl : tpl
38930         });
38931         this.addTabItem(item);
38932         if(content){
38933             item.setContent(content);
38934         }
38935         return item;
38936     },
38937
38938     /**
38939      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38940      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38941      * @return {Roo.TabPanelItem}
38942      */
38943     getTab : function(id){
38944         return this.items[id];
38945     },
38946
38947     /**
38948      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38949      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38950      */
38951     hideTab : function(id){
38952         var t = this.items[id];
38953         if(!t.isHidden()){
38954            t.setHidden(true);
38955            this.hiddenCount++;
38956            this.autoSizeTabs();
38957         }
38958     },
38959
38960     /**
38961      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38962      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38963      */
38964     unhideTab : function(id){
38965         var t = this.items[id];
38966         if(t.isHidden()){
38967            t.setHidden(false);
38968            this.hiddenCount--;
38969            this.autoSizeTabs();
38970         }
38971     },
38972
38973     /**
38974      * Adds an existing {@link Roo.TabPanelItem}.
38975      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38976      */
38977     addTabItem : function(item)
38978     {
38979         this.items[item.id] = item;
38980         this.items.push(item);
38981         this.autoSizeTabs();
38982       //  if(this.resizeTabs){
38983     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38984   //         this.autoSizeTabs();
38985 //        }else{
38986 //            item.autoSize();
38987        // }
38988     },
38989
38990     /**
38991      * Removes a {@link Roo.TabPanelItem}.
38992      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38993      */
38994     removeTab : function(id){
38995         var items = this.items;
38996         var tab = items[id];
38997         if(!tab) { return; }
38998         var index = items.indexOf(tab);
38999         if(this.active == tab && items.length > 1){
39000             var newTab = this.getNextAvailable(index);
39001             if(newTab) {
39002                 newTab.activate();
39003             }
39004         }
39005         this.stripEl.dom.removeChild(tab.pnode.dom);
39006         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
39007             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
39008         }
39009         items.splice(index, 1);
39010         delete this.items[tab.id];
39011         tab.fireEvent("close", tab);
39012         tab.purgeListeners();
39013         this.autoSizeTabs();
39014     },
39015
39016     getNextAvailable : function(start){
39017         var items = this.items;
39018         var index = start;
39019         // look for a next tab that will slide over to
39020         // replace the one being removed
39021         while(index < items.length){
39022             var item = items[++index];
39023             if(item && !item.isHidden()){
39024                 return item;
39025             }
39026         }
39027         // if one isn't found select the previous tab (on the left)
39028         index = start;
39029         while(index >= 0){
39030             var item = items[--index];
39031             if(item && !item.isHidden()){
39032                 return item;
39033             }
39034         }
39035         return null;
39036     },
39037
39038     /**
39039      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
39040      * @param {String/Number} id The id or index of the TabPanelItem to disable.
39041      */
39042     disableTab : function(id){
39043         var tab = this.items[id];
39044         if(tab && this.active != tab){
39045             tab.disable();
39046         }
39047     },
39048
39049     /**
39050      * Enables a {@link Roo.TabPanelItem} that is disabled.
39051      * @param {String/Number} id The id or index of the TabPanelItem to enable.
39052      */
39053     enableTab : function(id){
39054         var tab = this.items[id];
39055         tab.enable();
39056     },
39057
39058     /**
39059      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
39060      * @param {String/Number} id The id or index of the TabPanelItem to activate.
39061      * @return {Roo.TabPanelItem} The TabPanelItem.
39062      */
39063     activate : function(id)
39064     {
39065         //Roo.log('activite:'  + id);
39066         
39067         var tab = this.items[id];
39068         if(!tab){
39069             return null;
39070         }
39071         if(tab == this.active || tab.disabled){
39072             return tab;
39073         }
39074         var e = {};
39075         this.fireEvent("beforetabchange", this, e, tab);
39076         if(e.cancel !== true && !tab.disabled){
39077             if(this.active){
39078                 this.active.hide();
39079             }
39080             this.active = this.items[id];
39081             this.active.show();
39082             this.fireEvent("tabchange", this, this.active);
39083         }
39084         return tab;
39085     },
39086
39087     /**
39088      * Gets the active {@link Roo.TabPanelItem}.
39089      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
39090      */
39091     getActiveTab : function(){
39092         return this.active;
39093     },
39094
39095     /**
39096      * Updates the tab body element to fit the height of the container element
39097      * for overflow scrolling
39098      * @param {Number} targetHeight (optional) Override the starting height from the elements height
39099      */
39100     syncHeight : function(targetHeight){
39101         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
39102         var bm = this.bodyEl.getMargins();
39103         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
39104         this.bodyEl.setHeight(newHeight);
39105         return newHeight;
39106     },
39107
39108     onResize : function(){
39109         if(this.monitorResize){
39110             this.autoSizeTabs();
39111         }
39112     },
39113
39114     /**
39115      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
39116      */
39117     beginUpdate : function(){
39118         this.updating = true;
39119     },
39120
39121     /**
39122      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
39123      */
39124     endUpdate : function(){
39125         this.updating = false;
39126         this.autoSizeTabs();
39127     },
39128
39129     /**
39130      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
39131      */
39132     autoSizeTabs : function()
39133     {
39134         var count = this.items.length;
39135         var vcount = count - this.hiddenCount;
39136         
39137         if (vcount < 2) {
39138             this.stripEl.hide();
39139         } else {
39140             this.stripEl.show();
39141         }
39142         
39143         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
39144             return;
39145         }
39146         
39147         
39148         var w = Math.max(this.el.getWidth() - this.cpad, 10);
39149         var availWidth = Math.floor(w / vcount);
39150         var b = this.stripBody;
39151         if(b.getWidth() > w){
39152             var tabs = this.items;
39153             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
39154             if(availWidth < this.minTabWidth){
39155                 /*if(!this.sleft){    // incomplete scrolling code
39156                     this.createScrollButtons();
39157                 }
39158                 this.showScroll();
39159                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
39160             }
39161         }else{
39162             if(this.currentTabWidth < this.preferredTabWidth){
39163                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
39164             }
39165         }
39166     },
39167
39168     /**
39169      * Returns the number of tabs in this TabPanel.
39170      * @return {Number}
39171      */
39172      getCount : function(){
39173          return this.items.length;
39174      },
39175
39176     /**
39177      * Resizes all the tabs to the passed width
39178      * @param {Number} The new width
39179      */
39180     setTabWidth : function(width){
39181         this.currentTabWidth = width;
39182         for(var i = 0, len = this.items.length; i < len; i++) {
39183                 if(!this.items[i].isHidden()) {
39184                 this.items[i].setWidth(width);
39185             }
39186         }
39187     },
39188
39189     /**
39190      * Destroys this TabPanel
39191      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
39192      */
39193     destroy : function(removeEl){
39194         Roo.EventManager.removeResizeListener(this.onResize, this);
39195         for(var i = 0, len = this.items.length; i < len; i++){
39196             this.items[i].purgeListeners();
39197         }
39198         if(removeEl === true){
39199             this.el.update("");
39200             this.el.remove();
39201         }
39202     },
39203     
39204     createStrip : function(container)
39205     {
39206         var strip = document.createElement("nav");
39207         strip.className = Roo.bootstrap.version == 4 ?
39208             "navbar-light bg-light" : 
39209             "navbar navbar-default"; //"x-tabs-wrap";
39210         container.appendChild(strip);
39211         return strip;
39212     },
39213     
39214     createStripList : function(strip)
39215     {
39216         // div wrapper for retard IE
39217         // returns the "tr" element.
39218         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
39219         //'<div class="x-tabs-strip-wrap">'+
39220           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
39221           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
39222         return strip.firstChild; //.firstChild.firstChild.firstChild;
39223     },
39224     createBody : function(container)
39225     {
39226         var body = document.createElement("div");
39227         Roo.id(body, "tab-body");
39228         //Roo.fly(body).addClass("x-tabs-body");
39229         Roo.fly(body).addClass("tab-content");
39230         container.appendChild(body);
39231         return body;
39232     },
39233     createItemBody :function(bodyEl, id){
39234         var body = Roo.getDom(id);
39235         if(!body){
39236             body = document.createElement("div");
39237             body.id = id;
39238         }
39239         //Roo.fly(body).addClass("x-tabs-item-body");
39240         Roo.fly(body).addClass("tab-pane");
39241          bodyEl.insertBefore(body, bodyEl.firstChild);
39242         return body;
39243     },
39244     /** @private */
39245     createStripElements :  function(stripEl, text, closable, tpl)
39246     {
39247         var td = document.createElement("li"); // was td..
39248         td.className = 'nav-item';
39249         
39250         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
39251         
39252         
39253         stripEl.appendChild(td);
39254         /*if(closable){
39255             td.className = "x-tabs-closable";
39256             if(!this.closeTpl){
39257                 this.closeTpl = new Roo.Template(
39258                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39259                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
39260                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
39261                 );
39262             }
39263             var el = this.closeTpl.overwrite(td, {"text": text});
39264             var close = el.getElementsByTagName("div")[0];
39265             var inner = el.getElementsByTagName("em")[0];
39266             return {"el": el, "close": close, "inner": inner};
39267         } else {
39268         */
39269         // not sure what this is..
39270 //            if(!this.tabTpl){
39271                 //this.tabTpl = new Roo.Template(
39272                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39273                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
39274                 //);
39275 //                this.tabTpl = new Roo.Template(
39276 //                   '<a href="#">' +
39277 //                   '<span unselectable="on"' +
39278 //                            (this.disableTooltips ? '' : ' title="{text}"') +
39279 //                            ' >{text}</span></a>'
39280 //                );
39281 //                
39282 //            }
39283
39284
39285             var template = tpl || this.tabTpl || false;
39286             
39287             if(!template){
39288                 template =  new Roo.Template(
39289                         Roo.bootstrap.version == 4 ? 
39290                             (
39291                                 '<a class="nav-link" href="#" unselectable="on"' +
39292                                      (this.disableTooltips ? '' : ' title="{text}"') +
39293                                      ' >{text}</a>'
39294                             ) : (
39295                                 '<a class="nav-link" href="#">' +
39296                                 '<span unselectable="on"' +
39297                                          (this.disableTooltips ? '' : ' title="{text}"') +
39298                                     ' >{text}</span></a>'
39299                             )
39300                 );
39301             }
39302             
39303             switch (typeof(template)) {
39304                 case 'object' :
39305                     break;
39306                 case 'string' :
39307                     template = new Roo.Template(template);
39308                     break;
39309                 default :
39310                     break;
39311             }
39312             
39313             var el = template.overwrite(td, {"text": text});
39314             
39315             var inner = el.getElementsByTagName("span")[0];
39316             
39317             return {"el": el, "inner": inner};
39318             
39319     }
39320         
39321     
39322 });
39323
39324 /**
39325  * @class Roo.TabPanelItem
39326  * @extends Roo.util.Observable
39327  * Represents an individual item (tab plus body) in a TabPanel.
39328  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
39329  * @param {String} id The id of this TabPanelItem
39330  * @param {String} text The text for the tab of this TabPanelItem
39331  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
39332  */
39333 Roo.bootstrap.panel.TabItem = function(config){
39334     /**
39335      * The {@link Roo.TabPanel} this TabPanelItem belongs to
39336      * @type Roo.TabPanel
39337      */
39338     this.tabPanel = config.panel;
39339     /**
39340      * The id for this TabPanelItem
39341      * @type String
39342      */
39343     this.id = config.id;
39344     /** @private */
39345     this.disabled = false;
39346     /** @private */
39347     this.text = config.text;
39348     /** @private */
39349     this.loaded = false;
39350     this.closable = config.closable;
39351
39352     /**
39353      * The body element for this TabPanelItem.
39354      * @type Roo.Element
39355      */
39356     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
39357     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
39358     this.bodyEl.setStyle("display", "block");
39359     this.bodyEl.setStyle("zoom", "1");
39360     //this.hideAction();
39361
39362     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
39363     /** @private */
39364     this.el = Roo.get(els.el);
39365     this.inner = Roo.get(els.inner, true);
39366      this.textEl = Roo.bootstrap.version == 4 ?
39367         this.el : Roo.get(this.el.dom.firstChild, true);
39368
39369     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
39370     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
39371
39372     
39373 //    this.el.on("mousedown", this.onTabMouseDown, this);
39374     this.el.on("click", this.onTabClick, this);
39375     /** @private */
39376     if(config.closable){
39377         var c = Roo.get(els.close, true);
39378         c.dom.title = this.closeText;
39379         c.addClassOnOver("close-over");
39380         c.on("click", this.closeClick, this);
39381      }
39382
39383     this.addEvents({
39384          /**
39385          * @event activate
39386          * Fires when this tab becomes the active tab.
39387          * @param {Roo.TabPanel} tabPanel The parent TabPanel
39388          * @param {Roo.TabPanelItem} this
39389          */
39390         "activate": true,
39391         /**
39392          * @event beforeclose
39393          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
39394          * @param {Roo.TabPanelItem} this
39395          * @param {Object} e Set cancel to true on this object to cancel the close.
39396          */
39397         "beforeclose": true,
39398         /**
39399          * @event close
39400          * Fires when this tab is closed.
39401          * @param {Roo.TabPanelItem} this
39402          */
39403          "close": true,
39404         /**
39405          * @event deactivate
39406          * Fires when this tab is no longer the active tab.
39407          * @param {Roo.TabPanel} tabPanel The parent TabPanel
39408          * @param {Roo.TabPanelItem} this
39409          */
39410          "deactivate" : true
39411     });
39412     this.hidden = false;
39413
39414     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
39415 };
39416
39417 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
39418            {
39419     purgeListeners : function(){
39420        Roo.util.Observable.prototype.purgeListeners.call(this);
39421        this.el.removeAllListeners();
39422     },
39423     /**
39424      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
39425      */
39426     show : function(){
39427         this.status_node.addClass("active");
39428         this.showAction();
39429         if(Roo.isOpera){
39430             this.tabPanel.stripWrap.repaint();
39431         }
39432         this.fireEvent("activate", this.tabPanel, this);
39433     },
39434
39435     /**
39436      * Returns true if this tab is the active tab.
39437      * @return {Boolean}
39438      */
39439     isActive : function(){
39440         return this.tabPanel.getActiveTab() == this;
39441     },
39442
39443     /**
39444      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
39445      */
39446     hide : function(){
39447         this.status_node.removeClass("active");
39448         this.hideAction();
39449         this.fireEvent("deactivate", this.tabPanel, this);
39450     },
39451
39452     hideAction : function(){
39453         this.bodyEl.hide();
39454         this.bodyEl.setStyle("position", "absolute");
39455         this.bodyEl.setLeft("-20000px");
39456         this.bodyEl.setTop("-20000px");
39457     },
39458
39459     showAction : function(){
39460         this.bodyEl.setStyle("position", "relative");
39461         this.bodyEl.setTop("");
39462         this.bodyEl.setLeft("");
39463         this.bodyEl.show();
39464     },
39465
39466     /**
39467      * Set the tooltip for the tab.
39468      * @param {String} tooltip The tab's tooltip
39469      */
39470     setTooltip : function(text){
39471         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
39472             this.textEl.dom.qtip = text;
39473             this.textEl.dom.removeAttribute('title');
39474         }else{
39475             this.textEl.dom.title = text;
39476         }
39477     },
39478
39479     onTabClick : function(e){
39480         e.preventDefault();
39481         this.tabPanel.activate(this.id);
39482     },
39483
39484     onTabMouseDown : function(e){
39485         e.preventDefault();
39486         this.tabPanel.activate(this.id);
39487     },
39488 /*
39489     getWidth : function(){
39490         return this.inner.getWidth();
39491     },
39492
39493     setWidth : function(width){
39494         var iwidth = width - this.linode.getPadding("lr");
39495         this.inner.setWidth(iwidth);
39496         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
39497         this.linode.setWidth(width);
39498     },
39499 */
39500     /**
39501      * Show or hide the tab
39502      * @param {Boolean} hidden True to hide or false to show.
39503      */
39504     setHidden : function(hidden){
39505         this.hidden = hidden;
39506         this.linode.setStyle("display", hidden ? "none" : "");
39507     },
39508
39509     /**
39510      * Returns true if this tab is "hidden"
39511      * @return {Boolean}
39512      */
39513     isHidden : function(){
39514         return this.hidden;
39515     },
39516
39517     /**
39518      * Returns the text for this tab
39519      * @return {String}
39520      */
39521     getText : function(){
39522         return this.text;
39523     },
39524     /*
39525     autoSize : function(){
39526         //this.el.beginMeasure();
39527         this.textEl.setWidth(1);
39528         /*
39529          *  #2804 [new] Tabs in Roojs
39530          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
39531          */
39532         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
39533         //this.el.endMeasure();
39534     //},
39535
39536     /**
39537      * Sets the text for the tab (Note: this also sets the tooltip text)
39538      * @param {String} text The tab's text and tooltip
39539      */
39540     setText : function(text){
39541         this.text = text;
39542         this.textEl.update(text);
39543         this.setTooltip(text);
39544         //if(!this.tabPanel.resizeTabs){
39545         //    this.autoSize();
39546         //}
39547     },
39548     /**
39549      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39550      */
39551     activate : function(){
39552         this.tabPanel.activate(this.id);
39553     },
39554
39555     /**
39556      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39557      */
39558     disable : function(){
39559         if(this.tabPanel.active != this){
39560             this.disabled = true;
39561             this.status_node.addClass("disabled");
39562         }
39563     },
39564
39565     /**
39566      * Enables this TabPanelItem if it was previously disabled.
39567      */
39568     enable : function(){
39569         this.disabled = false;
39570         this.status_node.removeClass("disabled");
39571     },
39572
39573     /**
39574      * Sets the content for this TabPanelItem.
39575      * @param {String} content The content
39576      * @param {Boolean} loadScripts true to look for and load scripts
39577      */
39578     setContent : function(content, loadScripts){
39579         this.bodyEl.update(content, loadScripts);
39580     },
39581
39582     /**
39583      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
39584      * @return {Roo.UpdateManager} The UpdateManager
39585      */
39586     getUpdateManager : function(){
39587         return this.bodyEl.getUpdateManager();
39588     },
39589
39590     /**
39591      * Set a URL to be used to load the content for this TabPanelItem.
39592      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39593      * @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)
39594      * @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)
39595      * @return {Roo.UpdateManager} The UpdateManager
39596      */
39597     setUrl : function(url, params, loadOnce){
39598         if(this.refreshDelegate){
39599             this.un('activate', this.refreshDelegate);
39600         }
39601         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39602         this.on("activate", this.refreshDelegate);
39603         return this.bodyEl.getUpdateManager();
39604     },
39605
39606     /** @private */
39607     _handleRefresh : function(url, params, loadOnce){
39608         if(!loadOnce || !this.loaded){
39609             var updater = this.bodyEl.getUpdateManager();
39610             updater.update(url, params, this._setLoaded.createDelegate(this));
39611         }
39612     },
39613
39614     /**
39615      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
39616      *   Will fail silently if the setUrl method has not been called.
39617      *   This does not activate the panel, just updates its content.
39618      */
39619     refresh : function(){
39620         if(this.refreshDelegate){
39621            this.loaded = false;
39622            this.refreshDelegate();
39623         }
39624     },
39625
39626     /** @private */
39627     _setLoaded : function(){
39628         this.loaded = true;
39629     },
39630
39631     /** @private */
39632     closeClick : function(e){
39633         var o = {};
39634         e.stopEvent();
39635         this.fireEvent("beforeclose", this, o);
39636         if(o.cancel !== true){
39637             this.tabPanel.removeTab(this.id);
39638         }
39639     },
39640     /**
39641      * The text displayed in the tooltip for the close icon.
39642      * @type String
39643      */
39644     closeText : "Close this tab"
39645 });
39646 /**
39647 *    This script refer to:
39648 *    Title: International Telephone Input
39649 *    Author: Jack O'Connor
39650 *    Code version:  v12.1.12
39651 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39652 **/
39653
39654 Roo.bootstrap.PhoneInputData = function() {
39655     var d = [
39656       [
39657         "Afghanistan (‫افغانستان‬‎)",
39658         "af",
39659         "93"
39660       ],
39661       [
39662         "Albania (Shqipëri)",
39663         "al",
39664         "355"
39665       ],
39666       [
39667         "Algeria (‫الجزائر‬‎)",
39668         "dz",
39669         "213"
39670       ],
39671       [
39672         "American Samoa",
39673         "as",
39674         "1684"
39675       ],
39676       [
39677         "Andorra",
39678         "ad",
39679         "376"
39680       ],
39681       [
39682         "Angola",
39683         "ao",
39684         "244"
39685       ],
39686       [
39687         "Anguilla",
39688         "ai",
39689         "1264"
39690       ],
39691       [
39692         "Antigua and Barbuda",
39693         "ag",
39694         "1268"
39695       ],
39696       [
39697         "Argentina",
39698         "ar",
39699         "54"
39700       ],
39701       [
39702         "Armenia (Հայաստան)",
39703         "am",
39704         "374"
39705       ],
39706       [
39707         "Aruba",
39708         "aw",
39709         "297"
39710       ],
39711       [
39712         "Australia",
39713         "au",
39714         "61",
39715         0
39716       ],
39717       [
39718         "Austria (Österreich)",
39719         "at",
39720         "43"
39721       ],
39722       [
39723         "Azerbaijan (Azərbaycan)",
39724         "az",
39725         "994"
39726       ],
39727       [
39728         "Bahamas",
39729         "bs",
39730         "1242"
39731       ],
39732       [
39733         "Bahrain (‫البحرين‬‎)",
39734         "bh",
39735         "973"
39736       ],
39737       [
39738         "Bangladesh (বাংলাদেশ)",
39739         "bd",
39740         "880"
39741       ],
39742       [
39743         "Barbados",
39744         "bb",
39745         "1246"
39746       ],
39747       [
39748         "Belarus (Беларусь)",
39749         "by",
39750         "375"
39751       ],
39752       [
39753         "Belgium (België)",
39754         "be",
39755         "32"
39756       ],
39757       [
39758         "Belize",
39759         "bz",
39760         "501"
39761       ],
39762       [
39763         "Benin (Bénin)",
39764         "bj",
39765         "229"
39766       ],
39767       [
39768         "Bermuda",
39769         "bm",
39770         "1441"
39771       ],
39772       [
39773         "Bhutan (འབྲུག)",
39774         "bt",
39775         "975"
39776       ],
39777       [
39778         "Bolivia",
39779         "bo",
39780         "591"
39781       ],
39782       [
39783         "Bosnia and Herzegovina (Босна и Херцеговина)",
39784         "ba",
39785         "387"
39786       ],
39787       [
39788         "Botswana",
39789         "bw",
39790         "267"
39791       ],
39792       [
39793         "Brazil (Brasil)",
39794         "br",
39795         "55"
39796       ],
39797       [
39798         "British Indian Ocean Territory",
39799         "io",
39800         "246"
39801       ],
39802       [
39803         "British Virgin Islands",
39804         "vg",
39805         "1284"
39806       ],
39807       [
39808         "Brunei",
39809         "bn",
39810         "673"
39811       ],
39812       [
39813         "Bulgaria (България)",
39814         "bg",
39815         "359"
39816       ],
39817       [
39818         "Burkina Faso",
39819         "bf",
39820         "226"
39821       ],
39822       [
39823         "Burundi (Uburundi)",
39824         "bi",
39825         "257"
39826       ],
39827       [
39828         "Cambodia (កម្ពុជា)",
39829         "kh",
39830         "855"
39831       ],
39832       [
39833         "Cameroon (Cameroun)",
39834         "cm",
39835         "237"
39836       ],
39837       [
39838         "Canada",
39839         "ca",
39840         "1",
39841         1,
39842         ["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"]
39843       ],
39844       [
39845         "Cape Verde (Kabu Verdi)",
39846         "cv",
39847         "238"
39848       ],
39849       [
39850         "Caribbean Netherlands",
39851         "bq",
39852         "599",
39853         1
39854       ],
39855       [
39856         "Cayman Islands",
39857         "ky",
39858         "1345"
39859       ],
39860       [
39861         "Central African Republic (République centrafricaine)",
39862         "cf",
39863         "236"
39864       ],
39865       [
39866         "Chad (Tchad)",
39867         "td",
39868         "235"
39869       ],
39870       [
39871         "Chile",
39872         "cl",
39873         "56"
39874       ],
39875       [
39876         "China (中国)",
39877         "cn",
39878         "86"
39879       ],
39880       [
39881         "Christmas Island",
39882         "cx",
39883         "61",
39884         2
39885       ],
39886       [
39887         "Cocos (Keeling) Islands",
39888         "cc",
39889         "61",
39890         1
39891       ],
39892       [
39893         "Colombia",
39894         "co",
39895         "57"
39896       ],
39897       [
39898         "Comoros (‫جزر القمر‬‎)",
39899         "km",
39900         "269"
39901       ],
39902       [
39903         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39904         "cd",
39905         "243"
39906       ],
39907       [
39908         "Congo (Republic) (Congo-Brazzaville)",
39909         "cg",
39910         "242"
39911       ],
39912       [
39913         "Cook Islands",
39914         "ck",
39915         "682"
39916       ],
39917       [
39918         "Costa Rica",
39919         "cr",
39920         "506"
39921       ],
39922       [
39923         "Côte d’Ivoire",
39924         "ci",
39925         "225"
39926       ],
39927       [
39928         "Croatia (Hrvatska)",
39929         "hr",
39930         "385"
39931       ],
39932       [
39933         "Cuba",
39934         "cu",
39935         "53"
39936       ],
39937       [
39938         "Curaçao",
39939         "cw",
39940         "599",
39941         0
39942       ],
39943       [
39944         "Cyprus (Κύπρος)",
39945         "cy",
39946         "357"
39947       ],
39948       [
39949         "Czech Republic (Česká republika)",
39950         "cz",
39951         "420"
39952       ],
39953       [
39954         "Denmark (Danmark)",
39955         "dk",
39956         "45"
39957       ],
39958       [
39959         "Djibouti",
39960         "dj",
39961         "253"
39962       ],
39963       [
39964         "Dominica",
39965         "dm",
39966         "1767"
39967       ],
39968       [
39969         "Dominican Republic (República Dominicana)",
39970         "do",
39971         "1",
39972         2,
39973         ["809", "829", "849"]
39974       ],
39975       [
39976         "Ecuador",
39977         "ec",
39978         "593"
39979       ],
39980       [
39981         "Egypt (‫مصر‬‎)",
39982         "eg",
39983         "20"
39984       ],
39985       [
39986         "El Salvador",
39987         "sv",
39988         "503"
39989       ],
39990       [
39991         "Equatorial Guinea (Guinea Ecuatorial)",
39992         "gq",
39993         "240"
39994       ],
39995       [
39996         "Eritrea",
39997         "er",
39998         "291"
39999       ],
40000       [
40001         "Estonia (Eesti)",
40002         "ee",
40003         "372"
40004       ],
40005       [
40006         "Ethiopia",
40007         "et",
40008         "251"
40009       ],
40010       [
40011         "Falkland Islands (Islas Malvinas)",
40012         "fk",
40013         "500"
40014       ],
40015       [
40016         "Faroe Islands (Føroyar)",
40017         "fo",
40018         "298"
40019       ],
40020       [
40021         "Fiji",
40022         "fj",
40023         "679"
40024       ],
40025       [
40026         "Finland (Suomi)",
40027         "fi",
40028         "358",
40029         0
40030       ],
40031       [
40032         "France",
40033         "fr",
40034         "33"
40035       ],
40036       [
40037         "French Guiana (Guyane française)",
40038         "gf",
40039         "594"
40040       ],
40041       [
40042         "French Polynesia (Polynésie française)",
40043         "pf",
40044         "689"
40045       ],
40046       [
40047         "Gabon",
40048         "ga",
40049         "241"
40050       ],
40051       [
40052         "Gambia",
40053         "gm",
40054         "220"
40055       ],
40056       [
40057         "Georgia (საქართველო)",
40058         "ge",
40059         "995"
40060       ],
40061       [
40062         "Germany (Deutschland)",
40063         "de",
40064         "49"
40065       ],
40066       [
40067         "Ghana (Gaana)",
40068         "gh",
40069         "233"
40070       ],
40071       [
40072         "Gibraltar",
40073         "gi",
40074         "350"
40075       ],
40076       [
40077         "Greece (Ελλάδα)",
40078         "gr",
40079         "30"
40080       ],
40081       [
40082         "Greenland (Kalaallit Nunaat)",
40083         "gl",
40084         "299"
40085       ],
40086       [
40087         "Grenada",
40088         "gd",
40089         "1473"
40090       ],
40091       [
40092         "Guadeloupe",
40093         "gp",
40094         "590",
40095         0
40096       ],
40097       [
40098         "Guam",
40099         "gu",
40100         "1671"
40101       ],
40102       [
40103         "Guatemala",
40104         "gt",
40105         "502"
40106       ],
40107       [
40108         "Guernsey",
40109         "gg",
40110         "44",
40111         1
40112       ],
40113       [
40114         "Guinea (Guinée)",
40115         "gn",
40116         "224"
40117       ],
40118       [
40119         "Guinea-Bissau (Guiné Bissau)",
40120         "gw",
40121         "245"
40122       ],
40123       [
40124         "Guyana",
40125         "gy",
40126         "592"
40127       ],
40128       [
40129         "Haiti",
40130         "ht",
40131         "509"
40132       ],
40133       [
40134         "Honduras",
40135         "hn",
40136         "504"
40137       ],
40138       [
40139         "Hong Kong (香港)",
40140         "hk",
40141         "852"
40142       ],
40143       [
40144         "Hungary (Magyarország)",
40145         "hu",
40146         "36"
40147       ],
40148       [
40149         "Iceland (Ísland)",
40150         "is",
40151         "354"
40152       ],
40153       [
40154         "India (भारत)",
40155         "in",
40156         "91"
40157       ],
40158       [
40159         "Indonesia",
40160         "id",
40161         "62"
40162       ],
40163       [
40164         "Iran (‫ایران‬‎)",
40165         "ir",
40166         "98"
40167       ],
40168       [
40169         "Iraq (‫العراق‬‎)",
40170         "iq",
40171         "964"
40172       ],
40173       [
40174         "Ireland",
40175         "ie",
40176         "353"
40177       ],
40178       [
40179         "Isle of Man",
40180         "im",
40181         "44",
40182         2
40183       ],
40184       [
40185         "Israel (‫ישראל‬‎)",
40186         "il",
40187         "972"
40188       ],
40189       [
40190         "Italy (Italia)",
40191         "it",
40192         "39",
40193         0
40194       ],
40195       [
40196         "Jamaica",
40197         "jm",
40198         "1876"
40199       ],
40200       [
40201         "Japan (日本)",
40202         "jp",
40203         "81"
40204       ],
40205       [
40206         "Jersey",
40207         "je",
40208         "44",
40209         3
40210       ],
40211       [
40212         "Jordan (‫الأردن‬‎)",
40213         "jo",
40214         "962"
40215       ],
40216       [
40217         "Kazakhstan (Казахстан)",
40218         "kz",
40219         "7",
40220         1
40221       ],
40222       [
40223         "Kenya",
40224         "ke",
40225         "254"
40226       ],
40227       [
40228         "Kiribati",
40229         "ki",
40230         "686"
40231       ],
40232       [
40233         "Kosovo",
40234         "xk",
40235         "383"
40236       ],
40237       [
40238         "Kuwait (‫الكويت‬‎)",
40239         "kw",
40240         "965"
40241       ],
40242       [
40243         "Kyrgyzstan (Кыргызстан)",
40244         "kg",
40245         "996"
40246       ],
40247       [
40248         "Laos (ລາວ)",
40249         "la",
40250         "856"
40251       ],
40252       [
40253         "Latvia (Latvija)",
40254         "lv",
40255         "371"
40256       ],
40257       [
40258         "Lebanon (‫لبنان‬‎)",
40259         "lb",
40260         "961"
40261       ],
40262       [
40263         "Lesotho",
40264         "ls",
40265         "266"
40266       ],
40267       [
40268         "Liberia",
40269         "lr",
40270         "231"
40271       ],
40272       [
40273         "Libya (‫ليبيا‬‎)",
40274         "ly",
40275         "218"
40276       ],
40277       [
40278         "Liechtenstein",
40279         "li",
40280         "423"
40281       ],
40282       [
40283         "Lithuania (Lietuva)",
40284         "lt",
40285         "370"
40286       ],
40287       [
40288         "Luxembourg",
40289         "lu",
40290         "352"
40291       ],
40292       [
40293         "Macau (澳門)",
40294         "mo",
40295         "853"
40296       ],
40297       [
40298         "Macedonia (FYROM) (Македонија)",
40299         "mk",
40300         "389"
40301       ],
40302       [
40303         "Madagascar (Madagasikara)",
40304         "mg",
40305         "261"
40306       ],
40307       [
40308         "Malawi",
40309         "mw",
40310         "265"
40311       ],
40312       [
40313         "Malaysia",
40314         "my",
40315         "60"
40316       ],
40317       [
40318         "Maldives",
40319         "mv",
40320         "960"
40321       ],
40322       [
40323         "Mali",
40324         "ml",
40325         "223"
40326       ],
40327       [
40328         "Malta",
40329         "mt",
40330         "356"
40331       ],
40332       [
40333         "Marshall Islands",
40334         "mh",
40335         "692"
40336       ],
40337       [
40338         "Martinique",
40339         "mq",
40340         "596"
40341       ],
40342       [
40343         "Mauritania (‫موريتانيا‬‎)",
40344         "mr",
40345         "222"
40346       ],
40347       [
40348         "Mauritius (Moris)",
40349         "mu",
40350         "230"
40351       ],
40352       [
40353         "Mayotte",
40354         "yt",
40355         "262",
40356         1
40357       ],
40358       [
40359         "Mexico (México)",
40360         "mx",
40361         "52"
40362       ],
40363       [
40364         "Micronesia",
40365         "fm",
40366         "691"
40367       ],
40368       [
40369         "Moldova (Republica Moldova)",
40370         "md",
40371         "373"
40372       ],
40373       [
40374         "Monaco",
40375         "mc",
40376         "377"
40377       ],
40378       [
40379         "Mongolia (Монгол)",
40380         "mn",
40381         "976"
40382       ],
40383       [
40384         "Montenegro (Crna Gora)",
40385         "me",
40386         "382"
40387       ],
40388       [
40389         "Montserrat",
40390         "ms",
40391         "1664"
40392       ],
40393       [
40394         "Morocco (‫المغرب‬‎)",
40395         "ma",
40396         "212",
40397         0
40398       ],
40399       [
40400         "Mozambique (Moçambique)",
40401         "mz",
40402         "258"
40403       ],
40404       [
40405         "Myanmar (Burma) (မြန်မာ)",
40406         "mm",
40407         "95"
40408       ],
40409       [
40410         "Namibia (Namibië)",
40411         "na",
40412         "264"
40413       ],
40414       [
40415         "Nauru",
40416         "nr",
40417         "674"
40418       ],
40419       [
40420         "Nepal (नेपाल)",
40421         "np",
40422         "977"
40423       ],
40424       [
40425         "Netherlands (Nederland)",
40426         "nl",
40427         "31"
40428       ],
40429       [
40430         "New Caledonia (Nouvelle-Calédonie)",
40431         "nc",
40432         "687"
40433       ],
40434       [
40435         "New Zealand",
40436         "nz",
40437         "64"
40438       ],
40439       [
40440         "Nicaragua",
40441         "ni",
40442         "505"
40443       ],
40444       [
40445         "Niger (Nijar)",
40446         "ne",
40447         "227"
40448       ],
40449       [
40450         "Nigeria",
40451         "ng",
40452         "234"
40453       ],
40454       [
40455         "Niue",
40456         "nu",
40457         "683"
40458       ],
40459       [
40460         "Norfolk Island",
40461         "nf",
40462         "672"
40463       ],
40464       [
40465         "North Korea (조선 민주주의 인민 공화국)",
40466         "kp",
40467         "850"
40468       ],
40469       [
40470         "Northern Mariana Islands",
40471         "mp",
40472         "1670"
40473       ],
40474       [
40475         "Norway (Norge)",
40476         "no",
40477         "47",
40478         0
40479       ],
40480       [
40481         "Oman (‫عُمان‬‎)",
40482         "om",
40483         "968"
40484       ],
40485       [
40486         "Pakistan (‫پاکستان‬‎)",
40487         "pk",
40488         "92"
40489       ],
40490       [
40491         "Palau",
40492         "pw",
40493         "680"
40494       ],
40495       [
40496         "Palestine (‫فلسطين‬‎)",
40497         "ps",
40498         "970"
40499       ],
40500       [
40501         "Panama (Panamá)",
40502         "pa",
40503         "507"
40504       ],
40505       [
40506         "Papua New Guinea",
40507         "pg",
40508         "675"
40509       ],
40510       [
40511         "Paraguay",
40512         "py",
40513         "595"
40514       ],
40515       [
40516         "Peru (Perú)",
40517         "pe",
40518         "51"
40519       ],
40520       [
40521         "Philippines",
40522         "ph",
40523         "63"
40524       ],
40525       [
40526         "Poland (Polska)",
40527         "pl",
40528         "48"
40529       ],
40530       [
40531         "Portugal",
40532         "pt",
40533         "351"
40534       ],
40535       [
40536         "Puerto Rico",
40537         "pr",
40538         "1",
40539         3,
40540         ["787", "939"]
40541       ],
40542       [
40543         "Qatar (‫قطر‬‎)",
40544         "qa",
40545         "974"
40546       ],
40547       [
40548         "Réunion (La Réunion)",
40549         "re",
40550         "262",
40551         0
40552       ],
40553       [
40554         "Romania (România)",
40555         "ro",
40556         "40"
40557       ],
40558       [
40559         "Russia (Россия)",
40560         "ru",
40561         "7",
40562         0
40563       ],
40564       [
40565         "Rwanda",
40566         "rw",
40567         "250"
40568       ],
40569       [
40570         "Saint Barthélemy",
40571         "bl",
40572         "590",
40573         1
40574       ],
40575       [
40576         "Saint Helena",
40577         "sh",
40578         "290"
40579       ],
40580       [
40581         "Saint Kitts and Nevis",
40582         "kn",
40583         "1869"
40584       ],
40585       [
40586         "Saint Lucia",
40587         "lc",
40588         "1758"
40589       ],
40590       [
40591         "Saint Martin (Saint-Martin (partie française))",
40592         "mf",
40593         "590",
40594         2
40595       ],
40596       [
40597         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
40598         "pm",
40599         "508"
40600       ],
40601       [
40602         "Saint Vincent and the Grenadines",
40603         "vc",
40604         "1784"
40605       ],
40606       [
40607         "Samoa",
40608         "ws",
40609         "685"
40610       ],
40611       [
40612         "San Marino",
40613         "sm",
40614         "378"
40615       ],
40616       [
40617         "São Tomé and Príncipe (São Tomé e Príncipe)",
40618         "st",
40619         "239"
40620       ],
40621       [
40622         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
40623         "sa",
40624         "966"
40625       ],
40626       [
40627         "Senegal (Sénégal)",
40628         "sn",
40629         "221"
40630       ],
40631       [
40632         "Serbia (Србија)",
40633         "rs",
40634         "381"
40635       ],
40636       [
40637         "Seychelles",
40638         "sc",
40639         "248"
40640       ],
40641       [
40642         "Sierra Leone",
40643         "sl",
40644         "232"
40645       ],
40646       [
40647         "Singapore",
40648         "sg",
40649         "65"
40650       ],
40651       [
40652         "Sint Maarten",
40653         "sx",
40654         "1721"
40655       ],
40656       [
40657         "Slovakia (Slovensko)",
40658         "sk",
40659         "421"
40660       ],
40661       [
40662         "Slovenia (Slovenija)",
40663         "si",
40664         "386"
40665       ],
40666       [
40667         "Solomon Islands",
40668         "sb",
40669         "677"
40670       ],
40671       [
40672         "Somalia (Soomaaliya)",
40673         "so",
40674         "252"
40675       ],
40676       [
40677         "South Africa",
40678         "za",
40679         "27"
40680       ],
40681       [
40682         "South Korea (대한민국)",
40683         "kr",
40684         "82"
40685       ],
40686       [
40687         "South Sudan (‫جنوب السودان‬‎)",
40688         "ss",
40689         "211"
40690       ],
40691       [
40692         "Spain (España)",
40693         "es",
40694         "34"
40695       ],
40696       [
40697         "Sri Lanka (ශ්‍රී ලංකාව)",
40698         "lk",
40699         "94"
40700       ],
40701       [
40702         "Sudan (‫السودان‬‎)",
40703         "sd",
40704         "249"
40705       ],
40706       [
40707         "Suriname",
40708         "sr",
40709         "597"
40710       ],
40711       [
40712         "Svalbard and Jan Mayen",
40713         "sj",
40714         "47",
40715         1
40716       ],
40717       [
40718         "Swaziland",
40719         "sz",
40720         "268"
40721       ],
40722       [
40723         "Sweden (Sverige)",
40724         "se",
40725         "46"
40726       ],
40727       [
40728         "Switzerland (Schweiz)",
40729         "ch",
40730         "41"
40731       ],
40732       [
40733         "Syria (‫سوريا‬‎)",
40734         "sy",
40735         "963"
40736       ],
40737       [
40738         "Taiwan (台灣)",
40739         "tw",
40740         "886"
40741       ],
40742       [
40743         "Tajikistan",
40744         "tj",
40745         "992"
40746       ],
40747       [
40748         "Tanzania",
40749         "tz",
40750         "255"
40751       ],
40752       [
40753         "Thailand (ไทย)",
40754         "th",
40755         "66"
40756       ],
40757       [
40758         "Timor-Leste",
40759         "tl",
40760         "670"
40761       ],
40762       [
40763         "Togo",
40764         "tg",
40765         "228"
40766       ],
40767       [
40768         "Tokelau",
40769         "tk",
40770         "690"
40771       ],
40772       [
40773         "Tonga",
40774         "to",
40775         "676"
40776       ],
40777       [
40778         "Trinidad and Tobago",
40779         "tt",
40780         "1868"
40781       ],
40782       [
40783         "Tunisia (‫تونس‬‎)",
40784         "tn",
40785         "216"
40786       ],
40787       [
40788         "Turkey (Türkiye)",
40789         "tr",
40790         "90"
40791       ],
40792       [
40793         "Turkmenistan",
40794         "tm",
40795         "993"
40796       ],
40797       [
40798         "Turks and Caicos Islands",
40799         "tc",
40800         "1649"
40801       ],
40802       [
40803         "Tuvalu",
40804         "tv",
40805         "688"
40806       ],
40807       [
40808         "U.S. Virgin Islands",
40809         "vi",
40810         "1340"
40811       ],
40812       [
40813         "Uganda",
40814         "ug",
40815         "256"
40816       ],
40817       [
40818         "Ukraine (Україна)",
40819         "ua",
40820         "380"
40821       ],
40822       [
40823         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40824         "ae",
40825         "971"
40826       ],
40827       [
40828         "United Kingdom",
40829         "gb",
40830         "44",
40831         0
40832       ],
40833       [
40834         "United States",
40835         "us",
40836         "1",
40837         0
40838       ],
40839       [
40840         "Uruguay",
40841         "uy",
40842         "598"
40843       ],
40844       [
40845         "Uzbekistan (Oʻzbekiston)",
40846         "uz",
40847         "998"
40848       ],
40849       [
40850         "Vanuatu",
40851         "vu",
40852         "678"
40853       ],
40854       [
40855         "Vatican City (Città del Vaticano)",
40856         "va",
40857         "39",
40858         1
40859       ],
40860       [
40861         "Venezuela",
40862         "ve",
40863         "58"
40864       ],
40865       [
40866         "Vietnam (Việt Nam)",
40867         "vn",
40868         "84"
40869       ],
40870       [
40871         "Wallis and Futuna (Wallis-et-Futuna)",
40872         "wf",
40873         "681"
40874       ],
40875       [
40876         "Western Sahara (‫الصحراء الغربية‬‎)",
40877         "eh",
40878         "212",
40879         1
40880       ],
40881       [
40882         "Yemen (‫اليمن‬‎)",
40883         "ye",
40884         "967"
40885       ],
40886       [
40887         "Zambia",
40888         "zm",
40889         "260"
40890       ],
40891       [
40892         "Zimbabwe",
40893         "zw",
40894         "263"
40895       ],
40896       [
40897         "Åland Islands",
40898         "ax",
40899         "358",
40900         1
40901       ]
40902   ];
40903   
40904   return d;
40905 }/**
40906 *    This script refer to:
40907 *    Title: International Telephone Input
40908 *    Author: Jack O'Connor
40909 *    Code version:  v12.1.12
40910 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40911 **/
40912
40913 /**
40914  * @class Roo.bootstrap.PhoneInput
40915  * @extends Roo.bootstrap.TriggerField
40916  * An input with International dial-code selection
40917  
40918  * @cfg {String} defaultDialCode default '+852'
40919  * @cfg {Array} preferedCountries default []
40920   
40921  * @constructor
40922  * Create a new PhoneInput.
40923  * @param {Object} config Configuration options
40924  */
40925
40926 Roo.bootstrap.PhoneInput = function(config) {
40927     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40928 };
40929
40930 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40931         
40932         listWidth: undefined,
40933         
40934         selectedClass: 'active',
40935         
40936         invalidClass : "has-warning",
40937         
40938         validClass: 'has-success',
40939         
40940         allowed: '0123456789',
40941         
40942         max_length: 15,
40943         
40944         /**
40945          * @cfg {String} defaultDialCode The default dial code when initializing the input
40946          */
40947         defaultDialCode: '+852',
40948         
40949         /**
40950          * @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
40951          */
40952         preferedCountries: false,
40953         
40954         getAutoCreate : function()
40955         {
40956             var data = Roo.bootstrap.PhoneInputData();
40957             var align = this.labelAlign || this.parentLabelAlign();
40958             var id = Roo.id();
40959             
40960             this.allCountries = [];
40961             this.dialCodeMapping = [];
40962             
40963             for (var i = 0; i < data.length; i++) {
40964               var c = data[i];
40965               this.allCountries[i] = {
40966                 name: c[0],
40967                 iso2: c[1],
40968                 dialCode: c[2],
40969                 priority: c[3] || 0,
40970                 areaCodes: c[4] || null
40971               };
40972               this.dialCodeMapping[c[2]] = {
40973                   name: c[0],
40974                   iso2: c[1],
40975                   priority: c[3] || 0,
40976                   areaCodes: c[4] || null
40977               };
40978             }
40979             
40980             var cfg = {
40981                 cls: 'form-group',
40982                 cn: []
40983             };
40984             
40985             var input =  {
40986                 tag: 'input',
40987                 id : id,
40988                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40989                 maxlength: this.max_length,
40990                 cls : 'form-control tel-input',
40991                 autocomplete: 'new-password'
40992             };
40993             
40994             var hiddenInput = {
40995                 tag: 'input',
40996                 type: 'hidden',
40997                 cls: 'hidden-tel-input'
40998             };
40999             
41000             if (this.name) {
41001                 hiddenInput.name = this.name;
41002             }
41003             
41004             if (this.disabled) {
41005                 input.disabled = true;
41006             }
41007             
41008             var flag_container = {
41009                 tag: 'div',
41010                 cls: 'flag-box',
41011                 cn: [
41012                     {
41013                         tag: 'div',
41014                         cls: 'flag'
41015                     },
41016                     {
41017                         tag: 'div',
41018                         cls: 'caret'
41019                     }
41020                 ]
41021             };
41022             
41023             var box = {
41024                 tag: 'div',
41025                 cls: this.hasFeedback ? 'has-feedback' : '',
41026                 cn: [
41027                     hiddenInput,
41028                     input,
41029                     {
41030                         tag: 'input',
41031                         cls: 'dial-code-holder',
41032                         disabled: true
41033                     }
41034                 ]
41035             };
41036             
41037             var container = {
41038                 cls: 'roo-select2-container input-group',
41039                 cn: [
41040                     flag_container,
41041                     box
41042                 ]
41043             };
41044             
41045             if (this.fieldLabel.length) {
41046                 var indicator = {
41047                     tag: 'i',
41048                     tooltip: 'This field is required'
41049                 };
41050                 
41051                 var label = {
41052                     tag: 'label',
41053                     'for':  id,
41054                     cls: 'control-label',
41055                     cn: []
41056                 };
41057                 
41058                 var label_text = {
41059                     tag: 'span',
41060                     html: this.fieldLabel
41061                 };
41062                 
41063                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41064                 label.cn = [
41065                     indicator,
41066                     label_text
41067                 ];
41068                 
41069                 if(this.indicatorpos == 'right') {
41070                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41071                     label.cn = [
41072                         label_text,
41073                         indicator
41074                     ];
41075                 }
41076                 
41077                 if(align == 'left') {
41078                     container = {
41079                         tag: 'div',
41080                         cn: [
41081                             container
41082                         ]
41083                     };
41084                     
41085                     if(this.labelWidth > 12){
41086                         label.style = "width: " + this.labelWidth + 'px';
41087                     }
41088                     if(this.labelWidth < 13 && this.labelmd == 0){
41089                         this.labelmd = this.labelWidth;
41090                     }
41091                     if(this.labellg > 0){
41092                         label.cls += ' col-lg-' + this.labellg;
41093                         input.cls += ' col-lg-' + (12 - this.labellg);
41094                     }
41095                     if(this.labelmd > 0){
41096                         label.cls += ' col-md-' + this.labelmd;
41097                         container.cls += ' col-md-' + (12 - this.labelmd);
41098                     }
41099                     if(this.labelsm > 0){
41100                         label.cls += ' col-sm-' + this.labelsm;
41101                         container.cls += ' col-sm-' + (12 - this.labelsm);
41102                     }
41103                     if(this.labelxs > 0){
41104                         label.cls += ' col-xs-' + this.labelxs;
41105                         container.cls += ' col-xs-' + (12 - this.labelxs);
41106                     }
41107                 }
41108             }
41109             
41110             cfg.cn = [
41111                 label,
41112                 container
41113             ];
41114             
41115             var settings = this;
41116             
41117             ['xs','sm','md','lg'].map(function(size){
41118                 if (settings[size]) {
41119                     cfg.cls += ' col-' + size + '-' + settings[size];
41120                 }
41121             });
41122             
41123             this.store = new Roo.data.Store({
41124                 proxy : new Roo.data.MemoryProxy({}),
41125                 reader : new Roo.data.JsonReader({
41126                     fields : [
41127                         {
41128                             'name' : 'name',
41129                             'type' : 'string'
41130                         },
41131                         {
41132                             'name' : 'iso2',
41133                             'type' : 'string'
41134                         },
41135                         {
41136                             'name' : 'dialCode',
41137                             'type' : 'string'
41138                         },
41139                         {
41140                             'name' : 'priority',
41141                             'type' : 'string'
41142                         },
41143                         {
41144                             'name' : 'areaCodes',
41145                             'type' : 'string'
41146                         }
41147                     ]
41148                 })
41149             });
41150             
41151             if(!this.preferedCountries) {
41152                 this.preferedCountries = [
41153                     'hk',
41154                     'gb',
41155                     'us'
41156                 ];
41157             }
41158             
41159             var p = this.preferedCountries.reverse();
41160             
41161             if(p) {
41162                 for (var i = 0; i < p.length; i++) {
41163                     for (var j = 0; j < this.allCountries.length; j++) {
41164                         if(this.allCountries[j].iso2 == p[i]) {
41165                             var t = this.allCountries[j];
41166                             this.allCountries.splice(j,1);
41167                             this.allCountries.unshift(t);
41168                         }
41169                     } 
41170                 }
41171             }
41172             
41173             this.store.proxy.data = {
41174                 success: true,
41175                 data: this.allCountries
41176             };
41177             
41178             return cfg;
41179         },
41180         
41181         initEvents : function()
41182         {
41183             this.createList();
41184             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
41185             
41186             this.indicator = this.indicatorEl();
41187             this.flag = this.flagEl();
41188             this.dialCodeHolder = this.dialCodeHolderEl();
41189             
41190             this.trigger = this.el.select('div.flag-box',true).first();
41191             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41192             
41193             var _this = this;
41194             
41195             (function(){
41196                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41197                 _this.list.setWidth(lw);
41198             }).defer(100);
41199             
41200             this.list.on('mouseover', this.onViewOver, this);
41201             this.list.on('mousemove', this.onViewMove, this);
41202             this.inputEl().on("keyup", this.onKeyUp, this);
41203             this.inputEl().on("keypress", this.onKeyPress, this);
41204             
41205             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
41206
41207             this.view = new Roo.View(this.list, this.tpl, {
41208                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41209             });
41210             
41211             this.view.on('click', this.onViewClick, this);
41212             this.setValue(this.defaultDialCode);
41213         },
41214         
41215         onTriggerClick : function(e)
41216         {
41217             Roo.log('trigger click');
41218             if(this.disabled){
41219                 return;
41220             }
41221             
41222             if(this.isExpanded()){
41223                 this.collapse();
41224                 this.hasFocus = false;
41225             }else {
41226                 this.store.load({});
41227                 this.hasFocus = true;
41228                 this.expand();
41229             }
41230         },
41231         
41232         isExpanded : function()
41233         {
41234             return this.list.isVisible();
41235         },
41236         
41237         collapse : function()
41238         {
41239             if(!this.isExpanded()){
41240                 return;
41241             }
41242             this.list.hide();
41243             Roo.get(document).un('mousedown', this.collapseIf, this);
41244             Roo.get(document).un('mousewheel', this.collapseIf, this);
41245             this.fireEvent('collapse', this);
41246             this.validate();
41247         },
41248         
41249         expand : function()
41250         {
41251             Roo.log('expand');
41252
41253             if(this.isExpanded() || !this.hasFocus){
41254                 return;
41255             }
41256             
41257             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
41258             this.list.setWidth(lw);
41259             
41260             this.list.show();
41261             this.restrictHeight();
41262             
41263             Roo.get(document).on('mousedown', this.collapseIf, this);
41264             Roo.get(document).on('mousewheel', this.collapseIf, this);
41265             
41266             this.fireEvent('expand', this);
41267         },
41268         
41269         restrictHeight : function()
41270         {
41271             this.list.alignTo(this.inputEl(), this.listAlign);
41272             this.list.alignTo(this.inputEl(), this.listAlign);
41273         },
41274         
41275         onViewOver : function(e, t)
41276         {
41277             if(this.inKeyMode){
41278                 return;
41279             }
41280             var item = this.view.findItemFromChild(t);
41281             
41282             if(item){
41283                 var index = this.view.indexOf(item);
41284                 this.select(index, false);
41285             }
41286         },
41287
41288         // private
41289         onViewClick : function(view, doFocus, el, e)
41290         {
41291             var index = this.view.getSelectedIndexes()[0];
41292             
41293             var r = this.store.getAt(index);
41294             
41295             if(r){
41296                 this.onSelect(r, index);
41297             }
41298             if(doFocus !== false && !this.blockFocus){
41299                 this.inputEl().focus();
41300             }
41301         },
41302         
41303         onViewMove : function(e, t)
41304         {
41305             this.inKeyMode = false;
41306         },
41307         
41308         select : function(index, scrollIntoView)
41309         {
41310             this.selectedIndex = index;
41311             this.view.select(index);
41312             if(scrollIntoView !== false){
41313                 var el = this.view.getNode(index);
41314                 if(el){
41315                     this.list.scrollChildIntoView(el, false);
41316                 }
41317             }
41318         },
41319         
41320         createList : function()
41321         {
41322             this.list = Roo.get(document.body).createChild({
41323                 tag: 'ul',
41324                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
41325                 style: 'display:none'
41326             });
41327             
41328             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
41329         },
41330         
41331         collapseIf : function(e)
41332         {
41333             var in_combo  = e.within(this.el);
41334             var in_list =  e.within(this.list);
41335             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
41336             
41337             if (in_combo || in_list || is_list) {
41338                 return;
41339             }
41340             this.collapse();
41341         },
41342         
41343         onSelect : function(record, index)
41344         {
41345             if(this.fireEvent('beforeselect', this, record, index) !== false){
41346                 
41347                 this.setFlagClass(record.data.iso2);
41348                 this.setDialCode(record.data.dialCode);
41349                 this.hasFocus = false;
41350                 this.collapse();
41351                 this.fireEvent('select', this, record, index);
41352             }
41353         },
41354         
41355         flagEl : function()
41356         {
41357             var flag = this.el.select('div.flag',true).first();
41358             if(!flag){
41359                 return false;
41360             }
41361             return flag;
41362         },
41363         
41364         dialCodeHolderEl : function()
41365         {
41366             var d = this.el.select('input.dial-code-holder',true).first();
41367             if(!d){
41368                 return false;
41369             }
41370             return d;
41371         },
41372         
41373         setDialCode : function(v)
41374         {
41375             this.dialCodeHolder.dom.value = '+'+v;
41376         },
41377         
41378         setFlagClass : function(n)
41379         {
41380             this.flag.dom.className = 'flag '+n;
41381         },
41382         
41383         getValue : function()
41384         {
41385             var v = this.inputEl().getValue();
41386             if(this.dialCodeHolder) {
41387                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
41388             }
41389             return v;
41390         },
41391         
41392         setValue : function(v)
41393         {
41394             var d = this.getDialCode(v);
41395             
41396             //invalid dial code
41397             if(v.length == 0 || !d || d.length == 0) {
41398                 if(this.rendered){
41399                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41400                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41401                 }
41402                 return;
41403             }
41404             
41405             //valid dial code
41406             this.setFlagClass(this.dialCodeMapping[d].iso2);
41407             this.setDialCode(d);
41408             this.inputEl().dom.value = v.replace('+'+d,'');
41409             this.hiddenEl().dom.value = this.getValue();
41410             
41411             this.validate();
41412         },
41413         
41414         getDialCode : function(v)
41415         {
41416             v = v ||  '';
41417             
41418             if (v.length == 0) {
41419                 return this.dialCodeHolder.dom.value;
41420             }
41421             
41422             var dialCode = "";
41423             if (v.charAt(0) != "+") {
41424                 return false;
41425             }
41426             var numericChars = "";
41427             for (var i = 1; i < v.length; i++) {
41428               var c = v.charAt(i);
41429               if (!isNaN(c)) {
41430                 numericChars += c;
41431                 if (this.dialCodeMapping[numericChars]) {
41432                   dialCode = v.substr(1, i);
41433                 }
41434                 if (numericChars.length == 4) {
41435                   break;
41436                 }
41437               }
41438             }
41439             return dialCode;
41440         },
41441         
41442         reset : function()
41443         {
41444             this.setValue(this.defaultDialCode);
41445             this.validate();
41446         },
41447         
41448         hiddenEl : function()
41449         {
41450             return this.el.select('input.hidden-tel-input',true).first();
41451         },
41452         
41453         // after setting val
41454         onKeyUp : function(e){
41455             this.setValue(this.getValue());
41456         },
41457         
41458         onKeyPress : function(e){
41459             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
41460                 e.stopEvent();
41461             }
41462         }
41463         
41464 });
41465 /**
41466  * @class Roo.bootstrap.MoneyField
41467  * @extends Roo.bootstrap.ComboBox
41468  * Bootstrap MoneyField class
41469  * 
41470  * @constructor
41471  * Create a new MoneyField.
41472  * @param {Object} config Configuration options
41473  */
41474
41475 Roo.bootstrap.MoneyField = function(config) {
41476     
41477     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
41478     
41479 };
41480
41481 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
41482     
41483     /**
41484      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41485      */
41486     allowDecimals : true,
41487     /**
41488      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41489      */
41490     decimalSeparator : ".",
41491     /**
41492      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41493      */
41494     decimalPrecision : 0,
41495     /**
41496      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41497      */
41498     allowNegative : true,
41499     /**
41500      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41501      */
41502     allowZero: true,
41503     /**
41504      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41505      */
41506     minValue : Number.NEGATIVE_INFINITY,
41507     /**
41508      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41509      */
41510     maxValue : Number.MAX_VALUE,
41511     /**
41512      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41513      */
41514     minText : "The minimum value for this field is {0}",
41515     /**
41516      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41517      */
41518     maxText : "The maximum value for this field is {0}",
41519     /**
41520      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41521      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41522      */
41523     nanText : "{0} is not a valid number",
41524     /**
41525      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
41526      */
41527     castInt : true,
41528     /**
41529      * @cfg {String} defaults currency of the MoneyField
41530      * value should be in lkey
41531      */
41532     defaultCurrency : false,
41533     /**
41534      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41535      */
41536     thousandsDelimiter : false,
41537     /**
41538      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41539      */
41540     max_length: false,
41541     
41542     inputlg : 9,
41543     inputmd : 9,
41544     inputsm : 9,
41545     inputxs : 6,
41546     
41547     store : false,
41548     
41549     getAutoCreate : function()
41550     {
41551         var align = this.labelAlign || this.parentLabelAlign();
41552         
41553         var id = Roo.id();
41554
41555         var cfg = {
41556             cls: 'form-group',
41557             cn: []
41558         };
41559
41560         var input =  {
41561             tag: 'input',
41562             id : id,
41563             cls : 'form-control roo-money-amount-input',
41564             autocomplete: 'new-password'
41565         };
41566         
41567         var hiddenInput = {
41568             tag: 'input',
41569             type: 'hidden',
41570             id: Roo.id(),
41571             cls: 'hidden-number-input'
41572         };
41573         
41574         if(this.max_length) {
41575             input.maxlength = this.max_length; 
41576         }
41577         
41578         if (this.name) {
41579             hiddenInput.name = this.name;
41580         }
41581
41582         if (this.disabled) {
41583             input.disabled = true;
41584         }
41585
41586         var clg = 12 - this.inputlg;
41587         var cmd = 12 - this.inputmd;
41588         var csm = 12 - this.inputsm;
41589         var cxs = 12 - this.inputxs;
41590         
41591         var container = {
41592             tag : 'div',
41593             cls : 'row roo-money-field',
41594             cn : [
41595                 {
41596                     tag : 'div',
41597                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
41598                     cn : [
41599                         {
41600                             tag : 'div',
41601                             cls: 'roo-select2-container input-group',
41602                             cn: [
41603                                 {
41604                                     tag : 'input',
41605                                     cls : 'form-control roo-money-currency-input',
41606                                     autocomplete: 'new-password',
41607                                     readOnly : 1,
41608                                     name : this.currencyName
41609                                 },
41610                                 {
41611                                     tag :'span',
41612                                     cls : 'input-group-addon',
41613                                     cn : [
41614                                         {
41615                                             tag: 'span',
41616                                             cls: 'caret'
41617                                         }
41618                                     ]
41619                                 }
41620                             ]
41621                         }
41622                     ]
41623                 },
41624                 {
41625                     tag : 'div',
41626                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41627                     cn : [
41628                         {
41629                             tag: 'div',
41630                             cls: this.hasFeedback ? 'has-feedback' : '',
41631                             cn: [
41632                                 input
41633                             ]
41634                         }
41635                     ]
41636                 }
41637             ]
41638             
41639         };
41640         
41641         if (this.fieldLabel.length) {
41642             var indicator = {
41643                 tag: 'i',
41644                 tooltip: 'This field is required'
41645             };
41646
41647             var label = {
41648                 tag: 'label',
41649                 'for':  id,
41650                 cls: 'control-label',
41651                 cn: []
41652             };
41653
41654             var label_text = {
41655                 tag: 'span',
41656                 html: this.fieldLabel
41657             };
41658
41659             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41660             label.cn = [
41661                 indicator,
41662                 label_text
41663             ];
41664
41665             if(this.indicatorpos == 'right') {
41666                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41667                 label.cn = [
41668                     label_text,
41669                     indicator
41670                 ];
41671             }
41672
41673             if(align == 'left') {
41674                 container = {
41675                     tag: 'div',
41676                     cn: [
41677                         container
41678                     ]
41679                 };
41680
41681                 if(this.labelWidth > 12){
41682                     label.style = "width: " + this.labelWidth + 'px';
41683                 }
41684                 if(this.labelWidth < 13 && this.labelmd == 0){
41685                     this.labelmd = this.labelWidth;
41686                 }
41687                 if(this.labellg > 0){
41688                     label.cls += ' col-lg-' + this.labellg;
41689                     input.cls += ' col-lg-' + (12 - this.labellg);
41690                 }
41691                 if(this.labelmd > 0){
41692                     label.cls += ' col-md-' + this.labelmd;
41693                     container.cls += ' col-md-' + (12 - this.labelmd);
41694                 }
41695                 if(this.labelsm > 0){
41696                     label.cls += ' col-sm-' + this.labelsm;
41697                     container.cls += ' col-sm-' + (12 - this.labelsm);
41698                 }
41699                 if(this.labelxs > 0){
41700                     label.cls += ' col-xs-' + this.labelxs;
41701                     container.cls += ' col-xs-' + (12 - this.labelxs);
41702                 }
41703             }
41704         }
41705
41706         cfg.cn = [
41707             label,
41708             container,
41709             hiddenInput
41710         ];
41711         
41712         var settings = this;
41713
41714         ['xs','sm','md','lg'].map(function(size){
41715             if (settings[size]) {
41716                 cfg.cls += ' col-' + size + '-' + settings[size];
41717             }
41718         });
41719         
41720         return cfg;
41721     },
41722     
41723     initEvents : function()
41724     {
41725         this.indicator = this.indicatorEl();
41726         
41727         this.initCurrencyEvent();
41728         
41729         this.initNumberEvent();
41730     },
41731     
41732     initCurrencyEvent : function()
41733     {
41734         if (!this.store) {
41735             throw "can not find store for combo";
41736         }
41737         
41738         this.store = Roo.factory(this.store, Roo.data);
41739         this.store.parent = this;
41740         
41741         this.createList();
41742         
41743         this.triggerEl = this.el.select('.input-group-addon', true).first();
41744         
41745         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41746         
41747         var _this = this;
41748         
41749         (function(){
41750             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41751             _this.list.setWidth(lw);
41752         }).defer(100);
41753         
41754         this.list.on('mouseover', this.onViewOver, this);
41755         this.list.on('mousemove', this.onViewMove, this);
41756         this.list.on('scroll', this.onViewScroll, this);
41757         
41758         if(!this.tpl){
41759             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41760         }
41761         
41762         this.view = new Roo.View(this.list, this.tpl, {
41763             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41764         });
41765         
41766         this.view.on('click', this.onViewClick, this);
41767         
41768         this.store.on('beforeload', this.onBeforeLoad, this);
41769         this.store.on('load', this.onLoad, this);
41770         this.store.on('loadexception', this.onLoadException, this);
41771         
41772         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41773             "up" : function(e){
41774                 this.inKeyMode = true;
41775                 this.selectPrev();
41776             },
41777
41778             "down" : function(e){
41779                 if(!this.isExpanded()){
41780                     this.onTriggerClick();
41781                 }else{
41782                     this.inKeyMode = true;
41783                     this.selectNext();
41784                 }
41785             },
41786
41787             "enter" : function(e){
41788                 this.collapse();
41789                 
41790                 if(this.fireEvent("specialkey", this, e)){
41791                     this.onViewClick(false);
41792                 }
41793                 
41794                 return true;
41795             },
41796
41797             "esc" : function(e){
41798                 this.collapse();
41799             },
41800
41801             "tab" : function(e){
41802                 this.collapse();
41803                 
41804                 if(this.fireEvent("specialkey", this, e)){
41805                     this.onViewClick(false);
41806                 }
41807                 
41808                 return true;
41809             },
41810
41811             scope : this,
41812
41813             doRelay : function(foo, bar, hname){
41814                 if(hname == 'down' || this.scope.isExpanded()){
41815                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41816                 }
41817                 return true;
41818             },
41819
41820             forceKeyDown: true
41821         });
41822         
41823         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41824         
41825     },
41826     
41827     initNumberEvent : function(e)
41828     {
41829         this.inputEl().on("keydown" , this.fireKey,  this);
41830         this.inputEl().on("focus", this.onFocus,  this);
41831         this.inputEl().on("blur", this.onBlur,  this);
41832         
41833         this.inputEl().relayEvent('keyup', this);
41834         
41835         if(this.indicator){
41836             this.indicator.addClass('invisible');
41837         }
41838  
41839         this.originalValue = this.getValue();
41840         
41841         if(this.validationEvent == 'keyup'){
41842             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41843             this.inputEl().on('keyup', this.filterValidation, this);
41844         }
41845         else if(this.validationEvent !== false){
41846             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41847         }
41848         
41849         if(this.selectOnFocus){
41850             this.on("focus", this.preFocus, this);
41851             
41852         }
41853         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41854             this.inputEl().on("keypress", this.filterKeys, this);
41855         } else {
41856             this.inputEl().relayEvent('keypress', this);
41857         }
41858         
41859         var allowed = "0123456789";
41860         
41861         if(this.allowDecimals){
41862             allowed += this.decimalSeparator;
41863         }
41864         
41865         if(this.allowNegative){
41866             allowed += "-";
41867         }
41868         
41869         if(this.thousandsDelimiter) {
41870             allowed += ",";
41871         }
41872         
41873         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41874         
41875         var keyPress = function(e){
41876             
41877             var k = e.getKey();
41878             
41879             var c = e.getCharCode();
41880             
41881             if(
41882                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41883                     allowed.indexOf(String.fromCharCode(c)) === -1
41884             ){
41885                 e.stopEvent();
41886                 return;
41887             }
41888             
41889             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41890                 return;
41891             }
41892             
41893             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41894                 e.stopEvent();
41895             }
41896         };
41897         
41898         this.inputEl().on("keypress", keyPress, this);
41899         
41900     },
41901     
41902     onTriggerClick : function(e)
41903     {   
41904         if(this.disabled){
41905             return;
41906         }
41907         
41908         this.page = 0;
41909         this.loadNext = false;
41910         
41911         if(this.isExpanded()){
41912             this.collapse();
41913             return;
41914         }
41915         
41916         this.hasFocus = true;
41917         
41918         if(this.triggerAction == 'all') {
41919             this.doQuery(this.allQuery, true);
41920             return;
41921         }
41922         
41923         this.doQuery(this.getRawValue());
41924     },
41925     
41926     getCurrency : function()
41927     {   
41928         var v = this.currencyEl().getValue();
41929         
41930         return v;
41931     },
41932     
41933     restrictHeight : function()
41934     {
41935         this.list.alignTo(this.currencyEl(), this.listAlign);
41936         this.list.alignTo(this.currencyEl(), this.listAlign);
41937     },
41938     
41939     onViewClick : function(view, doFocus, el, e)
41940     {
41941         var index = this.view.getSelectedIndexes()[0];
41942         
41943         var r = this.store.getAt(index);
41944         
41945         if(r){
41946             this.onSelect(r, index);
41947         }
41948     },
41949     
41950     onSelect : function(record, index){
41951         
41952         if(this.fireEvent('beforeselect', this, record, index) !== false){
41953         
41954             this.setFromCurrencyData(index > -1 ? record.data : false);
41955             
41956             this.collapse();
41957             
41958             this.fireEvent('select', this, record, index);
41959         }
41960     },
41961     
41962     setFromCurrencyData : function(o)
41963     {
41964         var currency = '';
41965         
41966         this.lastCurrency = o;
41967         
41968         if (this.currencyField) {
41969             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41970         } else {
41971             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41972         }
41973         
41974         this.lastSelectionText = currency;
41975         
41976         //setting default currency
41977         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41978             this.setCurrency(this.defaultCurrency);
41979             return;
41980         }
41981         
41982         this.setCurrency(currency);
41983     },
41984     
41985     setFromData : function(o)
41986     {
41987         var c = {};
41988         
41989         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41990         
41991         this.setFromCurrencyData(c);
41992         
41993         var value = '';
41994         
41995         if (this.name) {
41996             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41997         } else {
41998             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41999         }
42000         
42001         this.setValue(value);
42002         
42003     },
42004     
42005     setCurrency : function(v)
42006     {   
42007         this.currencyValue = v;
42008         
42009         if(this.rendered){
42010             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
42011             this.validate();
42012         }
42013     },
42014     
42015     setValue : function(v)
42016     {
42017         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
42018         
42019         this.value = v;
42020         
42021         if(this.rendered){
42022             
42023             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42024             
42025             this.inputEl().dom.value = (v == '') ? '' :
42026                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
42027             
42028             if(!this.allowZero && v === '0') {
42029                 this.hiddenEl().dom.value = '';
42030                 this.inputEl().dom.value = '';
42031             }
42032             
42033             this.validate();
42034         }
42035     },
42036     
42037     getRawValue : function()
42038     {
42039         var v = this.inputEl().getValue();
42040         
42041         return v;
42042     },
42043     
42044     getValue : function()
42045     {
42046         return this.fixPrecision(this.parseValue(this.getRawValue()));
42047     },
42048     
42049     parseValue : function(value)
42050     {
42051         if(this.thousandsDelimiter) {
42052             value += "";
42053             r = new RegExp(",", "g");
42054             value = value.replace(r, "");
42055         }
42056         
42057         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
42058         return isNaN(value) ? '' : value;
42059         
42060     },
42061     
42062     fixPrecision : function(value)
42063     {
42064         if(this.thousandsDelimiter) {
42065             value += "";
42066             r = new RegExp(",", "g");
42067             value = value.replace(r, "");
42068         }
42069         
42070         var nan = isNaN(value);
42071         
42072         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
42073             return nan ? '' : value;
42074         }
42075         return parseFloat(value).toFixed(this.decimalPrecision);
42076     },
42077     
42078     decimalPrecisionFcn : function(v)
42079     {
42080         return Math.floor(v);
42081     },
42082     
42083     validateValue : function(value)
42084     {
42085         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
42086             return false;
42087         }
42088         
42089         var num = this.parseValue(value);
42090         
42091         if(isNaN(num)){
42092             this.markInvalid(String.format(this.nanText, value));
42093             return false;
42094         }
42095         
42096         if(num < this.minValue){
42097             this.markInvalid(String.format(this.minText, this.minValue));
42098             return false;
42099         }
42100         
42101         if(num > this.maxValue){
42102             this.markInvalid(String.format(this.maxText, this.maxValue));
42103             return false;
42104         }
42105         
42106         return true;
42107     },
42108     
42109     validate : function()
42110     {
42111         if(this.disabled || this.allowBlank){
42112             this.markValid();
42113             return true;
42114         }
42115         
42116         var currency = this.getCurrency();
42117         
42118         if(this.validateValue(this.getRawValue()) && currency.length){
42119             this.markValid();
42120             return true;
42121         }
42122         
42123         this.markInvalid();
42124         return false;
42125     },
42126     
42127     getName: function()
42128     {
42129         return this.name;
42130     },
42131     
42132     beforeBlur : function()
42133     {
42134         if(!this.castInt){
42135             return;
42136         }
42137         
42138         var v = this.parseValue(this.getRawValue());
42139         
42140         if(v || v == 0){
42141             this.setValue(v);
42142         }
42143     },
42144     
42145     onBlur : function()
42146     {
42147         this.beforeBlur();
42148         
42149         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
42150             //this.el.removeClass(this.focusClass);
42151         }
42152         
42153         this.hasFocus = false;
42154         
42155         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
42156             this.validate();
42157         }
42158         
42159         var v = this.getValue();
42160         
42161         if(String(v) !== String(this.startValue)){
42162             this.fireEvent('change', this, v, this.startValue);
42163         }
42164         
42165         this.fireEvent("blur", this);
42166     },
42167     
42168     inputEl : function()
42169     {
42170         return this.el.select('.roo-money-amount-input', true).first();
42171     },
42172     
42173     currencyEl : function()
42174     {
42175         return this.el.select('.roo-money-currency-input', true).first();
42176     },
42177     
42178     hiddenEl : function()
42179     {
42180         return this.el.select('input.hidden-number-input',true).first();
42181     }
42182     
42183 });/**
42184  * @class Roo.bootstrap.BezierSignature
42185  * @extends Roo.bootstrap.Component
42186  * Bootstrap BezierSignature class
42187  * This script refer to:
42188  *    Title: Signature Pad
42189  *    Author: szimek
42190  *    Availability: https://github.com/szimek/signature_pad
42191  *
42192  * @constructor
42193  * Create a new BezierSignature
42194  * @param {Object} config The config object
42195  */
42196
42197 Roo.bootstrap.BezierSignature = function(config){
42198     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
42199     this.addEvents({
42200         "resize" : true
42201     });
42202 };
42203
42204 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
42205 {
42206      
42207     curve_data: [],
42208     
42209     is_empty: true,
42210     
42211     mouse_btn_down: true,
42212     
42213     /**
42214      * @cfg {int} canvas height
42215      */
42216     canvas_height: '200px',
42217     
42218     /**
42219      * @cfg {float|function} Radius of a single dot.
42220      */ 
42221     dot_size: false,
42222     
42223     /**
42224      * @cfg {float} Minimum width of a line. Defaults to 0.5.
42225      */
42226     min_width: 0.5,
42227     
42228     /**
42229      * @cfg {float} Maximum width of a line. Defaults to 2.5.
42230      */
42231     max_width: 2.5,
42232     
42233     /**
42234      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
42235      */
42236     throttle: 16,
42237     
42238     /**
42239      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
42240      */
42241     min_distance: 5,
42242     
42243     /**
42244      * @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.
42245      */
42246     bg_color: 'rgba(0, 0, 0, 0)',
42247     
42248     /**
42249      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
42250      */
42251     dot_color: 'black',
42252     
42253     /**
42254      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
42255      */ 
42256     velocity_filter_weight: 0.7,
42257     
42258     /**
42259      * @cfg {function} Callback when stroke begin. 
42260      */
42261     onBegin: false,
42262     
42263     /**
42264      * @cfg {function} Callback when stroke end.
42265      */
42266     onEnd: false,
42267     
42268     getAutoCreate : function()
42269     {
42270         var cls = 'roo-signature column';
42271         
42272         if(this.cls){
42273             cls += ' ' + this.cls;
42274         }
42275         
42276         var col_sizes = [
42277             'lg',
42278             'md',
42279             'sm',
42280             'xs'
42281         ];
42282         
42283         for(var i = 0; i < col_sizes.length; i++) {
42284             if(this[col_sizes[i]]) {
42285                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
42286             }
42287         }
42288         
42289         var cfg = {
42290             tag: 'div',
42291             cls: cls,
42292             cn: [
42293                 {
42294                     tag: 'div',
42295                     cls: 'roo-signature-body',
42296                     cn: [
42297                         {
42298                             tag: 'canvas',
42299                             cls: 'roo-signature-body-canvas',
42300                             height: this.canvas_height,
42301                             width: this.canvas_width
42302                         }
42303                     ]
42304                 },
42305                 {
42306                     tag: 'input',
42307                     type: 'file',
42308                     style: 'display: none'
42309                 }
42310             ]
42311         };
42312         
42313         return cfg;
42314     },
42315     
42316     initEvents: function() 
42317     {
42318         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
42319         
42320         var canvas = this.canvasEl();
42321         
42322         // mouse && touch event swapping...
42323         canvas.dom.style.touchAction = 'none';
42324         canvas.dom.style.msTouchAction = 'none';
42325         
42326         this.mouse_btn_down = false;
42327         canvas.on('mousedown', this._handleMouseDown, this);
42328         canvas.on('mousemove', this._handleMouseMove, this);
42329         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
42330         
42331         if (window.PointerEvent) {
42332             canvas.on('pointerdown', this._handleMouseDown, this);
42333             canvas.on('pointermove', this._handleMouseMove, this);
42334             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
42335         }
42336         
42337         if ('ontouchstart' in window) {
42338             canvas.on('touchstart', this._handleTouchStart, this);
42339             canvas.on('touchmove', this._handleTouchMove, this);
42340             canvas.on('touchend', this._handleTouchEnd, this);
42341         }
42342         
42343         Roo.EventManager.onWindowResize(this.resize, this, true);
42344         
42345         // file input event
42346         this.fileEl().on('change', this.uploadImage, this);
42347         
42348         this.clear();
42349         
42350         this.resize();
42351     },
42352     
42353     resize: function(){
42354         
42355         var canvas = this.canvasEl().dom;
42356         var ctx = this.canvasElCtx();
42357         var img_data = false;
42358         
42359         if(canvas.width > 0) {
42360             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
42361         }
42362         // setting canvas width will clean img data
42363         canvas.width = 0;
42364         
42365         var style = window.getComputedStyle ? 
42366             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
42367             
42368         var padding_left = parseInt(style.paddingLeft) || 0;
42369         var padding_right = parseInt(style.paddingRight) || 0;
42370         
42371         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
42372         
42373         if(img_data) {
42374             ctx.putImageData(img_data, 0, 0);
42375         }
42376     },
42377     
42378     _handleMouseDown: function(e)
42379     {
42380         if (e.browserEvent.which === 1) {
42381             this.mouse_btn_down = true;
42382             this.strokeBegin(e);
42383         }
42384     },
42385     
42386     _handleMouseMove: function (e)
42387     {
42388         if (this.mouse_btn_down) {
42389             this.strokeMoveUpdate(e);
42390         }
42391     },
42392     
42393     _handleMouseUp: function (e)
42394     {
42395         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
42396             this.mouse_btn_down = false;
42397             this.strokeEnd(e);
42398         }
42399     },
42400     
42401     _handleTouchStart: function (e) {
42402         
42403         e.preventDefault();
42404         if (e.browserEvent.targetTouches.length === 1) {
42405             // var touch = e.browserEvent.changedTouches[0];
42406             // this.strokeBegin(touch);
42407             
42408              this.strokeBegin(e); // assume e catching the correct xy...
42409         }
42410     },
42411     
42412     _handleTouchMove: function (e) {
42413         e.preventDefault();
42414         // var touch = event.targetTouches[0];
42415         // _this._strokeMoveUpdate(touch);
42416         this.strokeMoveUpdate(e);
42417     },
42418     
42419     _handleTouchEnd: function (e) {
42420         var wasCanvasTouched = e.target === this.canvasEl().dom;
42421         if (wasCanvasTouched) {
42422             e.preventDefault();
42423             // var touch = event.changedTouches[0];
42424             // _this._strokeEnd(touch);
42425             this.strokeEnd(e);
42426         }
42427     },
42428     
42429     reset: function () {
42430         this._lastPoints = [];
42431         this._lastVelocity = 0;
42432         this._lastWidth = (this.min_width + this.max_width) / 2;
42433         this.canvasElCtx().fillStyle = this.dot_color;
42434     },
42435     
42436     strokeMoveUpdate: function(e)
42437     {
42438         this.strokeUpdate(e);
42439         
42440         if (this.throttle) {
42441             this.throttleStroke(this.strokeUpdate, this.throttle);
42442         }
42443         else {
42444             this.strokeUpdate(e);
42445         }
42446     },
42447     
42448     strokeBegin: function(e)
42449     {
42450         var newPointGroup = {
42451             color: this.dot_color,
42452             points: []
42453         };
42454         
42455         if (typeof this.onBegin === 'function') {
42456             this.onBegin(e);
42457         }
42458         
42459         this.curve_data.push(newPointGroup);
42460         this.reset();
42461         this.strokeUpdate(e);
42462     },
42463     
42464     strokeUpdate: function(e)
42465     {
42466         var rect = this.canvasEl().dom.getBoundingClientRect();
42467         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
42468         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
42469         var lastPoints = lastPointGroup.points;
42470         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
42471         var isLastPointTooClose = lastPoint
42472             ? point.distanceTo(lastPoint) <= this.min_distance
42473             : false;
42474         var color = lastPointGroup.color;
42475         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
42476             var curve = this.addPoint(point);
42477             if (!lastPoint) {
42478                 this.drawDot({color: color, point: point});
42479             }
42480             else if (curve) {
42481                 this.drawCurve({color: color, curve: curve});
42482             }
42483             lastPoints.push({
42484                 time: point.time,
42485                 x: point.x,
42486                 y: point.y
42487             });
42488         }
42489     },
42490     
42491     strokeEnd: function(e)
42492     {
42493         this.strokeUpdate(e);
42494         if (typeof this.onEnd === 'function') {
42495             this.onEnd(e);
42496         }
42497     },
42498     
42499     addPoint:  function (point) {
42500         var _lastPoints = this._lastPoints;
42501         _lastPoints.push(point);
42502         if (_lastPoints.length > 2) {
42503             if (_lastPoints.length === 3) {
42504                 _lastPoints.unshift(_lastPoints[0]);
42505             }
42506             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
42507             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
42508             _lastPoints.shift();
42509             return curve;
42510         }
42511         return null;
42512     },
42513     
42514     calculateCurveWidths: function (startPoint, endPoint) {
42515         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
42516             (1 - this.velocity_filter_weight) * this._lastVelocity;
42517
42518         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
42519         var widths = {
42520             end: newWidth,
42521             start: this._lastWidth
42522         };
42523         
42524         this._lastVelocity = velocity;
42525         this._lastWidth = newWidth;
42526         return widths;
42527     },
42528     
42529     drawDot: function (_a) {
42530         var color = _a.color, point = _a.point;
42531         var ctx = this.canvasElCtx();
42532         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
42533         ctx.beginPath();
42534         this.drawCurveSegment(point.x, point.y, width);
42535         ctx.closePath();
42536         ctx.fillStyle = color;
42537         ctx.fill();
42538     },
42539     
42540     drawCurve: function (_a) {
42541         var color = _a.color, curve = _a.curve;
42542         var ctx = this.canvasElCtx();
42543         var widthDelta = curve.endWidth - curve.startWidth;
42544         var drawSteps = Math.floor(curve.length()) * 2;
42545         ctx.beginPath();
42546         ctx.fillStyle = color;
42547         for (var i = 0; i < drawSteps; i += 1) {
42548         var t = i / drawSteps;
42549         var tt = t * t;
42550         var ttt = tt * t;
42551         var u = 1 - t;
42552         var uu = u * u;
42553         var uuu = uu * u;
42554         var x = uuu * curve.startPoint.x;
42555         x += 3 * uu * t * curve.control1.x;
42556         x += 3 * u * tt * curve.control2.x;
42557         x += ttt * curve.endPoint.x;
42558         var y = uuu * curve.startPoint.y;
42559         y += 3 * uu * t * curve.control1.y;
42560         y += 3 * u * tt * curve.control2.y;
42561         y += ttt * curve.endPoint.y;
42562         var width = curve.startWidth + ttt * widthDelta;
42563         this.drawCurveSegment(x, y, width);
42564         }
42565         ctx.closePath();
42566         ctx.fill();
42567     },
42568     
42569     drawCurveSegment: function (x, y, width) {
42570         var ctx = this.canvasElCtx();
42571         ctx.moveTo(x, y);
42572         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
42573         this.is_empty = false;
42574     },
42575     
42576     clear: function()
42577     {
42578         var ctx = this.canvasElCtx();
42579         var canvas = this.canvasEl().dom;
42580         ctx.fillStyle = this.bg_color;
42581         ctx.clearRect(0, 0, canvas.width, canvas.height);
42582         ctx.fillRect(0, 0, canvas.width, canvas.height);
42583         this.curve_data = [];
42584         this.reset();
42585         this.is_empty = true;
42586     },
42587     
42588     fileEl: function()
42589     {
42590         return  this.el.select('input',true).first();
42591     },
42592     
42593     canvasEl: function()
42594     {
42595         return this.el.select('canvas',true).first();
42596     },
42597     
42598     canvasElCtx: function()
42599     {
42600         return this.el.select('canvas',true).first().dom.getContext('2d');
42601     },
42602     
42603     getImage: function(type)
42604     {
42605         if(this.is_empty) {
42606             return false;
42607         }
42608         
42609         // encryption ?
42610         return this.canvasEl().dom.toDataURL('image/'+type, 1);
42611     },
42612     
42613     drawFromImage: function(img_src)
42614     {
42615         var img = new Image();
42616         
42617         img.onload = function(){
42618             this.canvasElCtx().drawImage(img, 0, 0);
42619         }.bind(this);
42620         
42621         img.src = img_src;
42622         
42623         this.is_empty = false;
42624     },
42625     
42626     selectImage: function()
42627     {
42628         this.fileEl().dom.click();
42629     },
42630     
42631     uploadImage: function(e)
42632     {
42633         var reader = new FileReader();
42634         
42635         reader.onload = function(e){
42636             var img = new Image();
42637             img.onload = function(){
42638                 this.reset();
42639                 this.canvasElCtx().drawImage(img, 0, 0);
42640             }.bind(this);
42641             img.src = e.target.result;
42642         }.bind(this);
42643         
42644         reader.readAsDataURL(e.target.files[0]);
42645     },
42646     
42647     // Bezier Point Constructor
42648     Point: (function () {
42649         function Point(x, y, time) {
42650             this.x = x;
42651             this.y = y;
42652             this.time = time || Date.now();
42653         }
42654         Point.prototype.distanceTo = function (start) {
42655             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42656         };
42657         Point.prototype.equals = function (other) {
42658             return this.x === other.x && this.y === other.y && this.time === other.time;
42659         };
42660         Point.prototype.velocityFrom = function (start) {
42661             return this.time !== start.time
42662             ? this.distanceTo(start) / (this.time - start.time)
42663             : 0;
42664         };
42665         return Point;
42666     }()),
42667     
42668     
42669     // Bezier Constructor
42670     Bezier: (function () {
42671         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42672             this.startPoint = startPoint;
42673             this.control2 = control2;
42674             this.control1 = control1;
42675             this.endPoint = endPoint;
42676             this.startWidth = startWidth;
42677             this.endWidth = endWidth;
42678         }
42679         Bezier.fromPoints = function (points, widths, scope) {
42680             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42681             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42682             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42683         };
42684         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42685             var dx1 = s1.x - s2.x;
42686             var dy1 = s1.y - s2.y;
42687             var dx2 = s2.x - s3.x;
42688             var dy2 = s2.y - s3.y;
42689             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42690             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42691             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42692             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42693             var dxm = m1.x - m2.x;
42694             var dym = m1.y - m2.y;
42695             var k = l2 / (l1 + l2);
42696             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42697             var tx = s2.x - cm.x;
42698             var ty = s2.y - cm.y;
42699             return {
42700                 c1: new scope.Point(m1.x + tx, m1.y + ty),
42701                 c2: new scope.Point(m2.x + tx, m2.y + ty)
42702             };
42703         };
42704         Bezier.prototype.length = function () {
42705             var steps = 10;
42706             var length = 0;
42707             var px;
42708             var py;
42709             for (var i = 0; i <= steps; i += 1) {
42710                 var t = i / steps;
42711                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42712                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42713                 if (i > 0) {
42714                     var xdiff = cx - px;
42715                     var ydiff = cy - py;
42716                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42717                 }
42718                 px = cx;
42719                 py = cy;
42720             }
42721             return length;
42722         };
42723         Bezier.prototype.point = function (t, start, c1, c2, end) {
42724             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42725             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42726             + (3.0 * c2 * (1.0 - t) * t * t)
42727             + (end * t * t * t);
42728         };
42729         return Bezier;
42730     }()),
42731     
42732     throttleStroke: function(fn, wait) {
42733       if (wait === void 0) { wait = 250; }
42734       var previous = 0;
42735       var timeout = null;
42736       var result;
42737       var storedContext;
42738       var storedArgs;
42739       var later = function () {
42740           previous = Date.now();
42741           timeout = null;
42742           result = fn.apply(storedContext, storedArgs);
42743           if (!timeout) {
42744               storedContext = null;
42745               storedArgs = [];
42746           }
42747       };
42748       return function wrapper() {
42749           var args = [];
42750           for (var _i = 0; _i < arguments.length; _i++) {
42751               args[_i] = arguments[_i];
42752           }
42753           var now = Date.now();
42754           var remaining = wait - (now - previous);
42755           storedContext = this;
42756           storedArgs = args;
42757           if (remaining <= 0 || remaining > wait) {
42758               if (timeout) {
42759                   clearTimeout(timeout);
42760                   timeout = null;
42761               }
42762               previous = now;
42763               result = fn.apply(storedContext, storedArgs);
42764               if (!timeout) {
42765                   storedContext = null;
42766                   storedArgs = [];
42767               }
42768           }
42769           else if (!timeout) {
42770               timeout = window.setTimeout(later, remaining);
42771           }
42772           return result;
42773       };
42774   }
42775   
42776 });
42777
42778  
42779
42780