ce4ffe5ce9254ffafa205044465195b1a6024f42
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets, function(s) {
10                     if ( s.href  && s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })(); /*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass(['hidden','d-none']);
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass(['hidden','d-none']);
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * element
442  * 
443  */
444
445 /**
446  * @class Roo.bootstrap.Element
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Element class
449  * @cfg {String} html contents of the element
450  * @cfg {String} tag tag of the element
451  * @cfg {String} cls class of the element
452  * @cfg {Boolean} preventDefault (true|false) default false
453  * @cfg {Boolean} clickable (true|false) default false
454  * 
455  * @constructor
456  * Create a new Element
457  * @param {Object} config The config object
458  */
459
460 Roo.bootstrap.Element = function(config){
461     Roo.bootstrap.Element.superclass.constructor.call(this, config);
462     
463     this.addEvents({
464         // raw events
465         /**
466          * @event click
467          * When a element is chick
468          * @param {Roo.bootstrap.Element} this
469          * @param {Roo.EventObject} e
470          */
471         "click" : true
472     });
473 };
474
475 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
476     
477     tag: 'div',
478     cls: '',
479     html: '',
480     preventDefault: false, 
481     clickable: false,
482     
483     getAutoCreate : function(){
484         
485         var cfg = {
486             tag: this.tag,
487             // cls: this.cls, double assign in parent class Component.js :: onRender
488             html: this.html
489         };
490         
491         return cfg;
492     },
493     
494     initEvents: function() 
495     {
496         Roo.bootstrap.Element.superclass.initEvents.call(this);
497         
498         if(this.clickable){
499             this.el.on('click', this.onClick, this);
500         }
501         
502     },
503     
504     onClick : function(e)
505     {
506         if(this.preventDefault){
507             e.preventDefault();
508         }
509         
510         this.fireEvent('click', this, e);
511     },
512     
513     getValue : function()
514     {
515         return this.el.dom.innerHTML;
516     },
517     
518     setValue : function(value)
519     {
520         this.el.dom.innerHTML = value;
521     }
522    
523 });
524
525  
526
527  /*
528  * - LGPL
529  *
530  * Body
531  *
532  */
533
534 /**
535  * @class Roo.bootstrap.Body
536  * @extends Roo.bootstrap.Component
537  * Bootstrap Body class
538  *
539  * @constructor
540  * Create a new body
541  * @param {Object} config The config object
542  */
543
544 Roo.bootstrap.Body = function(config){
545
546     config = config || {};
547
548     Roo.bootstrap.Body.superclass.constructor.call(this, config);
549     this.el = Roo.get(config.el ? config.el : document.body );
550     if (this.cls && this.cls.length) {
551         Roo.get(document.body).addClass(this.cls);
552     }
553 };
554
555 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
556
557     is_body : true,// just to make sure it's constructed?
558
559         autoCreate : {
560         cls: 'container'
561     },
562     onRender : function(ct, position)
563     {
564        /* Roo.log("Roo.bootstrap.Body - onRender");
565         if (this.cls && this.cls.length) {
566             Roo.get(document.body).addClass(this.cls);
567         }
568         // style??? xttr???
569         */
570     }
571
572
573
574
575 });
576 /*
577  * - LGPL
578  *
579  * button group
580  * 
581  */
582
583
584 /**
585  * @class Roo.bootstrap.ButtonGroup
586  * @extends Roo.bootstrap.Component
587  * Bootstrap ButtonGroup class
588  * @cfg {String} size lg | sm | xs (default empty normal)
589  * @cfg {String} align vertical | justified  (default none)
590  * @cfg {String} direction up | down (default down)
591  * @cfg {Boolean} toolbar false | true
592  * @cfg {Boolean} btn true | false
593  * 
594  * 
595  * @constructor
596  * Create a new Input
597  * @param {Object} config The config object
598  */
599
600 Roo.bootstrap.ButtonGroup = function(config){
601     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
602 };
603
604 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
605     
606     size: '',
607     align: '',
608     direction: '',
609     toolbar: false,
610     btn: true,
611
612     getAutoCreate : function(){
613         var cfg = {
614             cls: 'btn-group',
615             html : null
616         };
617         
618         cfg.html = this.html || cfg.html;
619         
620         if (this.toolbar) {
621             cfg = {
622                 cls: 'btn-toolbar',
623                 html: null
624             };
625             
626             return cfg;
627         }
628         
629         if (['vertical','justified'].indexOf(this.align)!==-1) {
630             cfg.cls = 'btn-group-' + this.align;
631             
632             if (this.align == 'justified') {
633                 console.log(this.items);
634             }
635         }
636         
637         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
638             cfg.cls += ' btn-group-' + this.size;
639         }
640         
641         if (this.direction == 'up') {
642             cfg.cls += ' dropup' ;
643         }
644         
645         return cfg;
646     },
647     /**
648      * Add a button to the group (similar to NavItem API.)
649      */
650     addItem : function(cfg)
651     {
652         var cn = new Roo.bootstrap.Button(cfg);
653         //this.register(cn);
654         cn.parentId = this.id;
655         cn.onRender(this.el, null);
656         return cn;
657     }
658    
659 });
660
661  /*
662  * - LGPL
663  *
664  * button
665  * 
666  */
667
668 /**
669  * @class Roo.bootstrap.Button
670  * @extends Roo.bootstrap.Component
671  * Bootstrap Button class
672  * @cfg {String} html The button content
673  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
674  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
675  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
676  * @cfg {String} size ( lg | sm | xs)
677  * @cfg {String} tag ( a | input | submit)
678  * @cfg {String} href empty or href
679  * @cfg {Boolean} disabled default false;
680  * @cfg {Boolean} isClose default false;
681  * @cfg {String} glyphicon depricated - use fa
682  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
683  * @cfg {String} badge text for badge
684  * @cfg {String} theme (default|glow)  
685  * @cfg {Boolean} inverse dark themed version
686  * @cfg {Boolean} toggle is it a slidy toggle button
687  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
688  * @cfg {String} ontext text for on slidy toggle state
689  * @cfg {String} offtext text for off slidy toggle state
690  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
691  * @cfg {Boolean} removeClass remove the standard class..
692  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
693  * 
694  * @constructor
695  * Create a new button
696  * @param {Object} config The config object
697  */
698
699
700 Roo.bootstrap.Button = function(config){
701     Roo.bootstrap.Button.superclass.constructor.call(this, config);
702     this.weightClass = ["btn-default btn-outline-secondary", 
703                        "btn-primary", 
704                        "btn-success", 
705                        "btn-info", 
706                        "btn-warning",
707                        "btn-danger",
708                        "btn-link"
709                       ],  
710     this.addEvents({
711         // raw events
712         /**
713          * @event click
714          * When a butotn is pressed
715          * @param {Roo.bootstrap.Button} btn
716          * @param {Roo.EventObject} e
717          */
718         "click" : true,
719          /**
720          * @event toggle
721          * After the button has been toggles
722          * @param {Roo.bootstrap.Button} btn
723          * @param {Roo.EventObject} e
724          * @param {boolean} pressed (also available as button.pressed)
725          */
726         "toggle" : true
727     });
728 };
729
730 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
731     html: false,
732     active: false,
733     weight: '',
734     badge_weight: '',
735     outline : false,
736     size: '',
737     tag: 'button',
738     href: '',
739     disabled: false,
740     isClose: false,
741     glyphicon: '',
742     fa: '',
743     badge: '',
744     theme: 'default',
745     inverse: false,
746     
747     toggle: false,
748     ontext: 'ON',
749     offtext: 'OFF',
750     defaulton: true,
751     preventDefault: true,
752     removeClass: false,
753     name: false,
754     target: false,
755      
756     pressed : null,
757      
758     
759     getAutoCreate : function(){
760         
761         var cfg = {
762             tag : 'button',
763             cls : 'roo-button',
764             html: ''
765         };
766         
767         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
768             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
769             this.tag = 'button';
770         } else {
771             cfg.tag = this.tag;
772         }
773         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
774         
775         if (this.toggle == true) {
776             cfg={
777                 tag: 'div',
778                 cls: 'slider-frame roo-button',
779                 cn: [
780                     {
781                         tag: 'span',
782                         'data-on-text':'ON',
783                         'data-off-text':'OFF',
784                         cls: 'slider-button',
785                         html: this.offtext
786                     }
787                 ]
788             };
789             
790             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
791                 cfg.cls += ' '+this.weight;
792             }
793             
794             return cfg;
795         }
796         
797         if (this.isClose) {
798             cfg.cls += ' close';
799             
800             cfg["aria-hidden"] = true;
801             
802             cfg.html = "&times;";
803             
804             return cfg;
805         }
806         
807          
808         if (this.theme==='default') {
809             cfg.cls = 'btn roo-button';
810             
811             //if (this.parentType != 'Navbar') {
812             this.weight = this.weight.length ?  this.weight : 'default';
813             //}
814             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
815                 
816                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
817                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
818                 cfg.cls += ' btn-' + outline + weight;
819                 if (this.weight == 'default') {
820                     // BC
821                     cfg.cls += ' btn-' + this.weight;
822                 }
823             }
824         } else if (this.theme==='glow') {
825             
826             cfg.tag = 'a';
827             cfg.cls = 'btn-glow roo-button';
828             
829             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
830                 
831                 cfg.cls += ' ' + this.weight;
832             }
833         }
834    
835         
836         if (this.inverse) {
837             this.cls += ' inverse';
838         }
839         
840         
841         if (this.active || this.pressed === true) {
842             cfg.cls += ' active';
843         }
844         
845         if (this.disabled) {
846             cfg.disabled = 'disabled';
847         }
848         
849         if (this.items) {
850             Roo.log('changing to ul' );
851             cfg.tag = 'ul';
852             this.glyphicon = 'caret';
853             if (Roo.bootstrap.version == 4) {
854                 this.fa = 'caret-down';
855             }
856             
857         }
858         
859         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
860          
861         //gsRoo.log(this.parentType);
862         if (this.parentType === 'Navbar' && !this.parent().bar) {
863             Roo.log('changing to li?');
864             
865             cfg.tag = 'li';
866             
867             cfg.cls = '';
868             cfg.cn =  [{
869                 tag : 'a',
870                 cls : 'roo-button',
871                 html : this.html,
872                 href : this.href || '#'
873             }];
874             if (this.menu) {
875                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
876                 cfg.cls += ' dropdown';
877             }   
878             
879             delete cfg.html;
880             
881         }
882         
883        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
884         
885         if (this.glyphicon) {
886             cfg.html = ' ' + cfg.html;
887             
888             cfg.cn = [
889                 {
890                     tag: 'span',
891                     cls: 'glyphicon glyphicon-' + this.glyphicon
892                 }
893             ];
894         }
895         if (this.fa) {
896             cfg.html = ' ' + cfg.html;
897             
898             cfg.cn = [
899                 {
900                     tag: 'i',
901                     cls: 'fa fas fa-' + this.fa
902                 }
903             ];
904         }
905         
906         if (this.badge) {
907             cfg.html += ' ';
908             
909             cfg.tag = 'a';
910             
911 //            cfg.cls='btn roo-button';
912             
913             cfg.href=this.href;
914             
915             var value = cfg.html;
916             
917             if(this.glyphicon){
918                 value = {
919                     tag: 'span',
920                     cls: 'glyphicon glyphicon-' + this.glyphicon,
921                     html: this.html
922                 };
923             }
924             if(this.fa){
925                 value = {
926                     tag: 'i',
927                     cls: 'fa fas fa-' + this.fa,
928                     html: this.html
929                 };
930             }
931             
932             var bw = this.badge_weight.length ? this.badge_weight :
933                 (this.weight.length ? this.weight : 'secondary');
934             bw = bw == 'default' ? 'secondary' : bw;
935             
936             cfg.cn = [
937                 value,
938                 {
939                     tag: 'span',
940                     cls: 'badge badge-' + bw,
941                     html: this.badge
942                 }
943             ];
944             
945             cfg.html='';
946         }
947         
948         if (this.menu) {
949             cfg.cls += ' dropdown';
950             cfg.html = typeof(cfg.html) != 'undefined' ?
951                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
952         }
953         
954         if (cfg.tag !== 'a' && this.href !== '') {
955             throw "Tag must be a to set href.";
956         } else if (this.href.length > 0) {
957             cfg.href = this.href;
958         }
959         
960         if(this.removeClass){
961             cfg.cls = '';
962         }
963         
964         if(this.target){
965             cfg.target = this.target;
966         }
967         
968         return cfg;
969     },
970     initEvents: function() {
971        // Roo.log('init events?');
972 //        Roo.log(this.el.dom);
973         // add the menu...
974         
975         if (typeof (this.menu) != 'undefined') {
976             this.menu.parentType = this.xtype;
977             this.menu.triggerEl = this.el;
978             this.addxtype(Roo.apply({}, this.menu));
979         }
980
981
982        if (this.el.hasClass('roo-button')) {
983             this.el.on('click', this.onClick, this);
984        } else {
985             this.el.select('.roo-button').on('click', this.onClick, this);
986        }
987        
988        if(this.removeClass){
989            this.el.on('click', this.onClick, this);
990        }
991        
992        this.el.enableDisplayMode();
993         
994     },
995     onClick : function(e)
996     {
997         if (this.disabled) {
998             return;
999         }
1000         
1001         Roo.log('button on click ');
1002         if(this.preventDefault){
1003             e.preventDefault();
1004         }
1005         
1006         if (this.pressed === true || this.pressed === false) {
1007             this.toggleActive(e);
1008         }
1009         
1010         
1011         this.fireEvent('click', this, e);
1012     },
1013     
1014     /**
1015      * Enables this button
1016      */
1017     enable : function()
1018     {
1019         this.disabled = false;
1020         this.el.removeClass('disabled');
1021     },
1022     
1023     /**
1024      * Disable this button
1025      */
1026     disable : function()
1027     {
1028         this.disabled = true;
1029         this.el.addClass('disabled');
1030     },
1031      /**
1032      * sets the active state on/off, 
1033      * @param {Boolean} state (optional) Force a particular state
1034      */
1035     setActive : function(v) {
1036         
1037         this.el[v ? 'addClass' : 'removeClass']('active');
1038         this.pressed = v;
1039     },
1040      /**
1041      * toggles the current active state 
1042      */
1043     toggleActive : function(e)
1044     {
1045         this.setActive(!this.pressed);
1046         this.fireEvent('toggle', this, e, !this.pressed);
1047     },
1048      /**
1049      * get the current active state
1050      * @return {boolean} true if it's active
1051      */
1052     isActive : function()
1053     {
1054         return this.el.hasClass('active');
1055     },
1056     /**
1057      * set the text of the first selected button
1058      */
1059     setText : function(str)
1060     {
1061         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1062     },
1063     /**
1064      * get the text of the first selected button
1065      */
1066     getText : function()
1067     {
1068         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1069     },
1070     
1071     setWeight : function(str)
1072     {
1073         this.el.removeClass(this.weightClass);
1074         this.weight = str;
1075         var outline = this.outline ? 'outline-' : '';
1076         if (str == 'default') {
1077             this.el.addClass('btn-default btn-outline-secondary');        
1078             return;
1079         }
1080         this.el.addClass('btn-' + outline + str);        
1081     }
1082     
1083     
1084 });
1085
1086  /*
1087  * - LGPL
1088  *
1089  * column
1090  * 
1091  */
1092
1093 /**
1094  * @class Roo.bootstrap.Column
1095  * @extends Roo.bootstrap.Component
1096  * Bootstrap Column class
1097  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1098  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1099  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1100  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1101  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1102  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1103  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1104  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1105  *
1106  * 
1107  * @cfg {Boolean} hidden (true|false) hide the element
1108  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1109  * @cfg {String} fa (ban|check|...) font awesome icon
1110  * @cfg {Number} fasize (1|2|....) font awsome size
1111
1112  * @cfg {String} icon (info-sign|check|...) glyphicon name
1113
1114  * @cfg {String} html content of column.
1115  * 
1116  * @constructor
1117  * Create a new Column
1118  * @param {Object} config The config object
1119  */
1120
1121 Roo.bootstrap.Column = function(config){
1122     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1123 };
1124
1125 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1126     
1127     xs: false,
1128     sm: false,
1129     md: false,
1130     lg: false,
1131     xsoff: false,
1132     smoff: false,
1133     mdoff: false,
1134     lgoff: false,
1135     html: '',
1136     offset: 0,
1137     alert: false,
1138     fa: false,
1139     icon : false,
1140     hidden : false,
1141     fasize : 1,
1142     
1143     getAutoCreate : function(){
1144         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1145         
1146         cfg = {
1147             tag: 'div',
1148             cls: 'column'
1149         };
1150         
1151         var settings=this;
1152         var sizes =   ['xs','sm','md','lg'];
1153         sizes.map(function(size ,ix){
1154             //Roo.log( size + ':' + settings[size]);
1155             
1156             if (settings[size+'off'] !== false) {
1157                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1158             }
1159             
1160             if (settings[size] === false) {
1161                 return;
1162             }
1163             
1164             if (!settings[size]) { // 0 = hidden
1165                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1166                 // bootsrap4
1167                 for (var i = ix; i > -1; i--) {
1168                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1169                 }
1170                 
1171                 
1172                 return;
1173             }
1174             cfg.cls += ' col-' + size + '-' + settings[size] + (
1175                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1176             );
1177             
1178         });
1179         
1180         if (this.hidden) {
1181             cfg.cls += ' hidden';
1182         }
1183         
1184         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1185             cfg.cls +=' alert alert-' + this.alert;
1186         }
1187         
1188         
1189         if (this.html.length) {
1190             cfg.html = this.html;
1191         }
1192         if (this.fa) {
1193             var fasize = '';
1194             if (this.fasize > 1) {
1195                 fasize = ' fa-' + this.fasize + 'x';
1196             }
1197             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1198             
1199             
1200         }
1201         if (this.icon) {
1202             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1203         }
1204         
1205         return cfg;
1206     }
1207    
1208 });
1209
1210  
1211
1212  /*
1213  * - LGPL
1214  *
1215  * page container.
1216  * 
1217  */
1218
1219
1220 /**
1221  * @class Roo.bootstrap.Container
1222  * @extends Roo.bootstrap.Component
1223  * Bootstrap Container class
1224  * @cfg {Boolean} jumbotron is it a jumbotron element
1225  * @cfg {String} html content of element
1226  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1227  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1228  * @cfg {String} header content of header (for panel)
1229  * @cfg {String} footer content of footer (for panel)
1230  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1231  * @cfg {String} tag (header|aside|section) type of HTML tag.
1232  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1233  * @cfg {String} fa font awesome icon
1234  * @cfg {String} icon (info-sign|check|...) glyphicon name
1235  * @cfg {Boolean} hidden (true|false) hide the element
1236  * @cfg {Boolean} expandable (true|false) default false
1237  * @cfg {Boolean} expanded (true|false) default true
1238  * @cfg {String} rheader contet on the right of header
1239  * @cfg {Boolean} clickable (true|false) default false
1240
1241  *     
1242  * @constructor
1243  * Create a new Container
1244  * @param {Object} config The config object
1245  */
1246
1247 Roo.bootstrap.Container = function(config){
1248     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1249     
1250     this.addEvents({
1251         // raw events
1252          /**
1253          * @event expand
1254          * After the panel has been expand
1255          * 
1256          * @param {Roo.bootstrap.Container} this
1257          */
1258         "expand" : true,
1259         /**
1260          * @event collapse
1261          * After the panel has been collapsed
1262          * 
1263          * @param {Roo.bootstrap.Container} this
1264          */
1265         "collapse" : true,
1266         /**
1267          * @event click
1268          * When a element is chick
1269          * @param {Roo.bootstrap.Container} this
1270          * @param {Roo.EventObject} e
1271          */
1272         "click" : true
1273     });
1274 };
1275
1276 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1277     
1278     jumbotron : false,
1279     well: '',
1280     panel : '',
1281     header: '',
1282     footer : '',
1283     sticky: '',
1284     tag : false,
1285     alert : false,
1286     fa: false,
1287     icon : false,
1288     expandable : false,
1289     rheader : '',
1290     expanded : true,
1291     clickable: false,
1292   
1293      
1294     getChildContainer : function() {
1295         
1296         if(!this.el){
1297             return false;
1298         }
1299         
1300         if (this.panel.length) {
1301             return this.el.select('.panel-body',true).first();
1302         }
1303         
1304         return this.el;
1305     },
1306     
1307     
1308     getAutoCreate : function(){
1309         
1310         var cfg = {
1311             tag : this.tag || 'div',
1312             html : '',
1313             cls : ''
1314         };
1315         if (this.jumbotron) {
1316             cfg.cls = 'jumbotron';
1317         }
1318         
1319         
1320         
1321         // - this is applied by the parent..
1322         //if (this.cls) {
1323         //    cfg.cls = this.cls + '';
1324         //}
1325         
1326         if (this.sticky.length) {
1327             
1328             var bd = Roo.get(document.body);
1329             if (!bd.hasClass('bootstrap-sticky')) {
1330                 bd.addClass('bootstrap-sticky');
1331                 Roo.select('html',true).setStyle('height', '100%');
1332             }
1333              
1334             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1335         }
1336         
1337         
1338         if (this.well.length) {
1339             switch (this.well) {
1340                 case 'lg':
1341                 case 'sm':
1342                     cfg.cls +=' well well-' +this.well;
1343                     break;
1344                 default:
1345                     cfg.cls +=' well';
1346                     break;
1347             }
1348         }
1349         
1350         if (this.hidden) {
1351             cfg.cls += ' hidden';
1352         }
1353         
1354         
1355         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1356             cfg.cls +=' alert alert-' + this.alert;
1357         }
1358         
1359         var body = cfg;
1360         
1361         if (this.panel.length) {
1362             cfg.cls += ' panel panel-' + this.panel;
1363             cfg.cn = [];
1364             if (this.header.length) {
1365                 
1366                 var h = [];
1367                 
1368                 if(this.expandable){
1369                     
1370                     cfg.cls = cfg.cls + ' expandable';
1371                     
1372                     h.push({
1373                         tag: 'i',
1374                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1375                     });
1376                     
1377                 }
1378                 
1379                 h.push(
1380                     {
1381                         tag: 'span',
1382                         cls : 'panel-title',
1383                         html : (this.expandable ? '&nbsp;' : '') + this.header
1384                     },
1385                     {
1386                         tag: 'span',
1387                         cls: 'panel-header-right',
1388                         html: this.rheader
1389                     }
1390                 );
1391                 
1392                 cfg.cn.push({
1393                     cls : 'panel-heading',
1394                     style : this.expandable ? 'cursor: pointer' : '',
1395                     cn : h
1396                 });
1397                 
1398             }
1399             
1400             body = false;
1401             cfg.cn.push({
1402                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1403                 html : this.html
1404             });
1405             
1406             
1407             if (this.footer.length) {
1408                 cfg.cn.push({
1409                     cls : 'panel-footer',
1410                     html : this.footer
1411                     
1412                 });
1413             }
1414             
1415         }
1416         
1417         if (body) {
1418             body.html = this.html || cfg.html;
1419             // prefix with the icons..
1420             if (this.fa) {
1421                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1422             }
1423             if (this.icon) {
1424                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1425             }
1426             
1427             
1428         }
1429         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1430             cfg.cls =  'container';
1431         }
1432         
1433         return cfg;
1434     },
1435     
1436     initEvents: function() 
1437     {
1438         if(this.expandable){
1439             var headerEl = this.headerEl();
1440         
1441             if(headerEl){
1442                 headerEl.on('click', this.onToggleClick, this);
1443             }
1444         }
1445         
1446         if(this.clickable){
1447             this.el.on('click', this.onClick, this);
1448         }
1449         
1450     },
1451     
1452     onToggleClick : function()
1453     {
1454         var headerEl = this.headerEl();
1455         
1456         if(!headerEl){
1457             return;
1458         }
1459         
1460         if(this.expanded){
1461             this.collapse();
1462             return;
1463         }
1464         
1465         this.expand();
1466     },
1467     
1468     expand : function()
1469     {
1470         if(this.fireEvent('expand', this)) {
1471             
1472             this.expanded = true;
1473             
1474             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1475             
1476             this.el.select('.panel-body',true).first().removeClass('hide');
1477             
1478             var toggleEl = this.toggleEl();
1479
1480             if(!toggleEl){
1481                 return;
1482             }
1483
1484             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1485         }
1486         
1487     },
1488     
1489     collapse : function()
1490     {
1491         if(this.fireEvent('collapse', this)) {
1492             
1493             this.expanded = false;
1494             
1495             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1496             this.el.select('.panel-body',true).first().addClass('hide');
1497         
1498             var toggleEl = this.toggleEl();
1499
1500             if(!toggleEl){
1501                 return;
1502             }
1503
1504             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1505         }
1506     },
1507     
1508     toggleEl : function()
1509     {
1510         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1511             return;
1512         }
1513         
1514         return this.el.select('.panel-heading .fa',true).first();
1515     },
1516     
1517     headerEl : function()
1518     {
1519         if(!this.el || !this.panel.length || !this.header.length){
1520             return;
1521         }
1522         
1523         return this.el.select('.panel-heading',true).first()
1524     },
1525     
1526     bodyEl : function()
1527     {
1528         if(!this.el || !this.panel.length){
1529             return;
1530         }
1531         
1532         return this.el.select('.panel-body',true).first()
1533     },
1534     
1535     titleEl : function()
1536     {
1537         if(!this.el || !this.panel.length || !this.header.length){
1538             return;
1539         }
1540         
1541         return this.el.select('.panel-title',true).first();
1542     },
1543     
1544     setTitle : function(v)
1545     {
1546         var titleEl = this.titleEl();
1547         
1548         if(!titleEl){
1549             return;
1550         }
1551         
1552         titleEl.dom.innerHTML = v;
1553     },
1554     
1555     getTitle : function()
1556     {
1557         
1558         var titleEl = this.titleEl();
1559         
1560         if(!titleEl){
1561             return '';
1562         }
1563         
1564         return titleEl.dom.innerHTML;
1565     },
1566     
1567     setRightTitle : function(v)
1568     {
1569         var t = this.el.select('.panel-header-right',true).first();
1570         
1571         if(!t){
1572             return;
1573         }
1574         
1575         t.dom.innerHTML = v;
1576     },
1577     
1578     onClick : function(e)
1579     {
1580         e.preventDefault();
1581         
1582         this.fireEvent('click', this, e);
1583     }
1584 });
1585
1586  /*
1587  *  - LGPL
1588  *
1589  *  This is BS4's Card element.. - similar to our containers probably..
1590  * 
1591  */
1592 /**
1593  * @class Roo.bootstrap.Card
1594  * @extends Roo.bootstrap.Component
1595  * Bootstrap Card class
1596  *
1597  *
1598  * possible... may not be implemented..
1599  * @cfg {String} header_image  src url of image.
1600  * @cfg {String|Object} header
1601  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1602  * 
1603  * @cfg {String} title
1604  * @cfg {String} subtitle
1605  * @cfg {String} html -- html contents - or just use children..
1606  * @cfg {String} footer
1607  
1608  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1609  * 
1610  * @cfg {String} margin (0|1|2|3|4|5|auto)
1611  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1612  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1613  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1614  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1615  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1616  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1617  *
1618  * @cfg {String} padding (0|1|2|3|4|5)
1619  * @cfg {String} padding_top (0|1|2|3|4|5)
1620  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1621  * @cfg {String} padding_left (0|1|2|3|4|5)
1622  * @cfg {String} padding_right (0|1|2|3|4|5)
1623  * @cfg {String} padding_x (0|1|2|3|4|5)
1624  * @cfg {String} padding_y (0|1|2|3|4|5)
1625  *
1626  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1627  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1628  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1629  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1630  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1631  
1632  * @config {Boolean} dragable  if this card can be dragged.
1633  * @config {Boolean} drag_group  group for drag
1634  * 
1635  
1636  * @constructor
1637  * Create a new Container
1638  * @param {Object} config The config object
1639  */
1640
1641 Roo.bootstrap.Card = function(config){
1642     Roo.bootstrap.Card.superclass.constructor.call(this, config);
1643     
1644     this.addEvents({
1645         
1646     });
1647 };
1648
1649
1650 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
1651     
1652     
1653     weight : '',
1654     
1655     margin: '', /// may be better in component?
1656     margin_top: '', 
1657     margin_bottom: '', 
1658     margin_left: '',
1659     margin_right: '',
1660     margin_x: '',
1661     margin_y: '',
1662     
1663     padding : '',
1664     padding_top: '', 
1665     padding_bottom: '', 
1666     padding_left: '',
1667     padding_right: '',
1668     padding_x: '',
1669     padding_y: '',
1670     
1671     display: '', 
1672     display_xs: '', 
1673     display_sm: '', 
1674     display_lg: '',
1675     display_xl: '',
1676  
1677     header_image  : '',
1678     header : '',
1679     header_size : 0,
1680     title : '',
1681     subtitle : '',
1682     html : '',
1683     footer: '',
1684     
1685     dragable : false,
1686     drag_group : false,
1687     
1688     childContainer : false,
1689
1690     layoutCls : function()
1691     {
1692         var cls = '';
1693         var t = this;
1694         Roo.log(this.margin_bottom.length);
1695         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
1696             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
1697             
1698             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
1699                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
1700             }
1701             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
1702                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
1703             }
1704         });
1705         
1706         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
1707             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
1708                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
1709             }
1710         });
1711         
1712         // more generic support?
1713         if (this.hidden) {
1714             cls += ' d-none';
1715         }
1716         
1717         return cls;
1718     },
1719  
1720        // Roo.log("Call onRender: " + this.xtype);
1721         /*  We are looking at something like this.
1722 <div class="card">
1723     <img src="..." class="card-img-top" alt="...">
1724     <div class="card-body">
1725         <h5 class="card-title">Card title</h5>
1726          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
1727
1728         >> this bit is really the body...
1729         <div> << we will ad dthis in hopefully it will not break shit.
1730         
1731         ** card text does not actually have any styling...
1732         
1733             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
1734         
1735         </div> <<
1736           <a href="#" class="card-link">Card link</a>
1737           
1738     </div>
1739     <div class="card-footer">
1740         <small class="text-muted">Last updated 3 mins ago</small>
1741     </div>
1742 </div>
1743          */
1744     getAutoCreate : function(){
1745         
1746         var cfg = {
1747             tag : 'div',
1748             cls : 'card',
1749             cn : [ ]
1750         };
1751         
1752         if (this.weight.length && this.weight != 'light') {
1753             cfg.cls += ' text-white';
1754         } else {
1755             cfg.cls += ' text-dark'; // need as it's nested..
1756         }
1757         if (this.weight.length) {
1758             cfg.cls += ' bg-' + this.weight;
1759         }
1760         
1761         cfg.cls += this.layoutCls(); 
1762         
1763         if (this.header.length) {
1764             cfg.cn.push({
1765                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
1766                 cls : 'card-header',
1767                 html : this.header // escape?
1768             });
1769         } else {
1770             cfg.cn.push({
1771                 tag : 'div',
1772                 cls : 'card-header d-none'
1773             });
1774         }
1775         if (this.header_image.length) {
1776             cfg.cn.push({
1777                 tag : 'img',
1778                 cls : 'card-img-top',
1779                 src: this.header_image // escape?
1780             });
1781         }
1782         
1783         var body = {
1784             tag : 'div',
1785             cls : 'card-body',
1786             cn : []
1787         };
1788         cfg.cn.push(body);
1789         
1790         if (this.title.length) {
1791             body.cn.push({
1792                 tag : 'div',
1793                 cls : 'card-title',
1794                 src: this.title // escape?
1795             });
1796         }
1797         
1798         if (this.subtitle.length) {
1799             body.cn.push({
1800                 tag : 'div',
1801                 cls : 'card-title',
1802                 src: this.subtitle // escape?
1803             });
1804         }
1805         
1806         body.cn.push({
1807             tag : 'div',
1808             cls : 'roo-card-body-ctr'
1809         });
1810         
1811         if (this.html.length) {
1812             body.cn.push({
1813                 tag: 'div',
1814                 html : this.html
1815             });
1816         }
1817         // fixme ? handle objects?
1818         if (this.footer.length) {
1819             cfg.cn.push({
1820                 tag : 'div',
1821                 cls : 'card-footer',
1822                 html: this.footer // escape?
1823             });
1824         }
1825         // footer...
1826         
1827         return cfg;
1828     },
1829     
1830     
1831     getCardHeader : function()
1832     {
1833         var  ret = this.el.select('.card-header',true).first();
1834         if (ret.hasClass('d-none')) {
1835             ret.removeClass('d-none');
1836         }
1837         
1838         return ret;
1839     },
1840     
1841     getChildContainer : function()
1842     {
1843         
1844         if(!this.el){
1845             return false;
1846         }
1847         return this.el.select('.roo-card-body-ctr',true).first();    
1848     },
1849     
1850     initEvents: function() 
1851     {
1852         if(this.dragable){
1853              this.dragZone = new Roo.dd.DragZone(this.getEl(), {
1854                     containerScroll: true,
1855                     ddGroup: this.drag_group || 'default_card_drag_group'
1856             });
1857             this.dragZone.getDragData = this.getDragData.createDelegate(this);
1858         }
1859         
1860         
1861         
1862     },
1863     getDragData : function(e) {
1864         var target = this.getEl();
1865         if (target) {
1866             //this.handleSelection(e);
1867             
1868             var dragData = {
1869                 source: this,
1870                 copy: false,
1871                 nodes: this.getEl(),
1872                 records: []
1873             };
1874             
1875             
1876             dragData.ddel = target.dom ;        // the div element
1877             Roo.log(target.getWidth( ));
1878              dragData.ddel.style.width = target.getWidth() + 'px';
1879             
1880             return dragData;
1881         }
1882         return false;
1883     }
1884     
1885 });
1886
1887 /*
1888  * - LGPL
1889  *
1890  * Card header - holder for the card header elements.
1891  * 
1892  */
1893
1894 /**
1895  * @class Roo.bootstrap.CardHeader
1896  * @extends Roo.bootstrap.Element
1897  * Bootstrap Element class
1898  * @constructor
1899  * Create a new Element
1900  * @param {Object} config The config object
1901  */
1902
1903 Roo.bootstrap.CardHeader = function(config){
1904     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
1905 };
1906
1907 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
1908     
1909     tag: 'div',
1910     container_method : 'getCardHeader',
1911     
1912     getAutoCreate : function() {
1913         
1914         var cfg = {
1915             tag: 'div' 
1916         };
1917         
1918         return cfg;
1919     } 
1920     
1921     
1922    
1923 });
1924
1925  
1926
1927  /*
1928  * - LGPL
1929  *
1930  * image
1931  * 
1932  */
1933
1934
1935 /**
1936  * @class Roo.bootstrap.Img
1937  * @extends Roo.bootstrap.Component
1938  * Bootstrap Img class
1939  * @cfg {Boolean} imgResponsive false | true
1940  * @cfg {String} border rounded | circle | thumbnail
1941  * @cfg {String} src image source
1942  * @cfg {String} alt image alternative text
1943  * @cfg {String} href a tag href
1944  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1945  * @cfg {String} xsUrl xs image source
1946  * @cfg {String} smUrl sm image source
1947  * @cfg {String} mdUrl md image source
1948  * @cfg {String} lgUrl lg image source
1949  * 
1950  * @constructor
1951  * Create a new Input
1952  * @param {Object} config The config object
1953  */
1954
1955 Roo.bootstrap.Img = function(config){
1956     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1957     
1958     this.addEvents({
1959         // img events
1960         /**
1961          * @event click
1962          * The img click event for the img.
1963          * @param {Roo.EventObject} e
1964          */
1965         "click" : true
1966     });
1967 };
1968
1969 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1970     
1971     imgResponsive: true,
1972     border: '',
1973     src: 'about:blank',
1974     href: false,
1975     target: false,
1976     xsUrl: '',
1977     smUrl: '',
1978     mdUrl: '',
1979     lgUrl: '',
1980
1981     getAutoCreate : function()
1982     {   
1983         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1984             return this.createSingleImg();
1985         }
1986         
1987         var cfg = {
1988             tag: 'div',
1989             cls: 'roo-image-responsive-group',
1990             cn: []
1991         };
1992         var _this = this;
1993         
1994         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1995             
1996             if(!_this[size + 'Url']){
1997                 return;
1998             }
1999             
2000             var img = {
2001                 tag: 'img',
2002                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2003                 html: _this.html || cfg.html,
2004                 src: _this[size + 'Url']
2005             };
2006             
2007             img.cls += ' roo-image-responsive-' + size;
2008             
2009             var s = ['xs', 'sm', 'md', 'lg'];
2010             
2011             s.splice(s.indexOf(size), 1);
2012             
2013             Roo.each(s, function(ss){
2014                 img.cls += ' hidden-' + ss;
2015             });
2016             
2017             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2018                 cfg.cls += ' img-' + _this.border;
2019             }
2020             
2021             if(_this.alt){
2022                 cfg.alt = _this.alt;
2023             }
2024             
2025             if(_this.href){
2026                 var a = {
2027                     tag: 'a',
2028                     href: _this.href,
2029                     cn: [
2030                         img
2031                     ]
2032                 };
2033
2034                 if(this.target){
2035                     a.target = _this.target;
2036                 }
2037             }
2038             
2039             cfg.cn.push((_this.href) ? a : img);
2040             
2041         });
2042         
2043         return cfg;
2044     },
2045     
2046     createSingleImg : function()
2047     {
2048         var cfg = {
2049             tag: 'img',
2050             cls: (this.imgResponsive) ? 'img-responsive' : '',
2051             html : null,
2052             src : 'about:blank'  // just incase src get's set to undefined?!?
2053         };
2054         
2055         cfg.html = this.html || cfg.html;
2056         
2057         cfg.src = this.src || cfg.src;
2058         
2059         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2060             cfg.cls += ' img-' + this.border;
2061         }
2062         
2063         if(this.alt){
2064             cfg.alt = this.alt;
2065         }
2066         
2067         if(this.href){
2068             var a = {
2069                 tag: 'a',
2070                 href: this.href,
2071                 cn: [
2072                     cfg
2073                 ]
2074             };
2075             
2076             if(this.target){
2077                 a.target = this.target;
2078             }
2079             
2080         }
2081         
2082         return (this.href) ? a : cfg;
2083     },
2084     
2085     initEvents: function() 
2086     {
2087         if(!this.href){
2088             this.el.on('click', this.onClick, this);
2089         }
2090         
2091     },
2092     
2093     onClick : function(e)
2094     {
2095         Roo.log('img onclick');
2096         this.fireEvent('click', this, e);
2097     },
2098     /**
2099      * Sets the url of the image - used to update it
2100      * @param {String} url the url of the image
2101      */
2102     
2103     setSrc : function(url)
2104     {
2105         this.src =  url;
2106         
2107         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2108             this.el.dom.src =  url;
2109             return;
2110         }
2111         
2112         this.el.select('img', true).first().dom.src =  url;
2113     }
2114     
2115     
2116    
2117 });
2118
2119  /*
2120  * - LGPL
2121  *
2122  * image
2123  * 
2124  */
2125
2126
2127 /**
2128  * @class Roo.bootstrap.Link
2129  * @extends Roo.bootstrap.Component
2130  * Bootstrap Link Class
2131  * @cfg {String} alt image alternative text
2132  * @cfg {String} href a tag href
2133  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2134  * @cfg {String} html the content of the link.
2135  * @cfg {String} anchor name for the anchor link
2136  * @cfg {String} fa - favicon
2137
2138  * @cfg {Boolean} preventDefault (true | false) default false
2139
2140  * 
2141  * @constructor
2142  * Create a new Input
2143  * @param {Object} config The config object
2144  */
2145
2146 Roo.bootstrap.Link = function(config){
2147     Roo.bootstrap.Link.superclass.constructor.call(this, config);
2148     
2149     this.addEvents({
2150         // img events
2151         /**
2152          * @event click
2153          * The img click event for the img.
2154          * @param {Roo.EventObject} e
2155          */
2156         "click" : true
2157     });
2158 };
2159
2160 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
2161     
2162     href: false,
2163     target: false,
2164     preventDefault: false,
2165     anchor : false,
2166     alt : false,
2167     fa: false,
2168
2169
2170     getAutoCreate : function()
2171     {
2172         var html = this.html || '';
2173         
2174         if (this.fa !== false) {
2175             html = '<i class="fa fa-' + this.fa + '"></i>';
2176         }
2177         var cfg = {
2178             tag: 'a'
2179         };
2180         // anchor's do not require html/href...
2181         if (this.anchor === false) {
2182             cfg.html = html;
2183             cfg.href = this.href || '#';
2184         } else {
2185             cfg.name = this.anchor;
2186             if (this.html !== false || this.fa !== false) {
2187                 cfg.html = html;
2188             }
2189             if (this.href !== false) {
2190                 cfg.href = this.href;
2191             }
2192         }
2193         
2194         if(this.alt !== false){
2195             cfg.alt = this.alt;
2196         }
2197         
2198         
2199         if(this.target !== false) {
2200             cfg.target = this.target;
2201         }
2202         
2203         return cfg;
2204     },
2205     
2206     initEvents: function() {
2207         
2208         if(!this.href || this.preventDefault){
2209             this.el.on('click', this.onClick, this);
2210         }
2211     },
2212     
2213     onClick : function(e)
2214     {
2215         if(this.preventDefault){
2216             e.preventDefault();
2217         }
2218         //Roo.log('img onclick');
2219         this.fireEvent('click', this, e);
2220     }
2221    
2222 });
2223
2224  /*
2225  * - LGPL
2226  *
2227  * header
2228  * 
2229  */
2230
2231 /**
2232  * @class Roo.bootstrap.Header
2233  * @extends Roo.bootstrap.Component
2234  * Bootstrap Header class
2235  * @cfg {String} html content of header
2236  * @cfg {Number} level (1|2|3|4|5|6) default 1
2237  * 
2238  * @constructor
2239  * Create a new Header
2240  * @param {Object} config The config object
2241  */
2242
2243
2244 Roo.bootstrap.Header  = function(config){
2245     Roo.bootstrap.Header.superclass.constructor.call(this, config);
2246 };
2247
2248 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
2249     
2250     //href : false,
2251     html : false,
2252     level : 1,
2253     
2254     
2255     
2256     getAutoCreate : function(){
2257         
2258         
2259         
2260         var cfg = {
2261             tag: 'h' + (1 *this.level),
2262             html: this.html || ''
2263         } ;
2264         
2265         return cfg;
2266     }
2267    
2268 });
2269
2270  
2271
2272  /*
2273  * Based on:
2274  * Ext JS Library 1.1.1
2275  * Copyright(c) 2006-2007, Ext JS, LLC.
2276  *
2277  * Originally Released Under LGPL - original licence link has changed is not relivant.
2278  *
2279  * Fork - LGPL
2280  * <script type="text/javascript">
2281  */
2282  
2283 /**
2284  * @class Roo.bootstrap.MenuMgr
2285  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
2286  * @singleton
2287  */
2288 Roo.bootstrap.MenuMgr = function(){
2289    var menus, active, groups = {}, attached = false, lastShow = new Date();
2290
2291    // private - called when first menu is created
2292    function init(){
2293        menus = {};
2294        active = new Roo.util.MixedCollection();
2295        Roo.get(document).addKeyListener(27, function(){
2296            if(active.length > 0){
2297                hideAll();
2298            }
2299        });
2300    }
2301
2302    // private
2303    function hideAll(){
2304        if(active && active.length > 0){
2305            var c = active.clone();
2306            c.each(function(m){
2307                m.hide();
2308            });
2309        }
2310    }
2311
2312    // private
2313    function onHide(m){
2314        active.remove(m);
2315        if(active.length < 1){
2316            Roo.get(document).un("mouseup", onMouseDown);
2317             
2318            attached = false;
2319        }
2320    }
2321
2322    // private
2323    function onShow(m){
2324        var last = active.last();
2325        lastShow = new Date();
2326        active.add(m);
2327        if(!attached){
2328           Roo.get(document).on("mouseup", onMouseDown);
2329            
2330            attached = true;
2331        }
2332        if(m.parentMenu){
2333           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
2334           m.parentMenu.activeChild = m;
2335        }else if(last && last.isVisible()){
2336           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
2337        }
2338    }
2339
2340    // private
2341    function onBeforeHide(m){
2342        if(m.activeChild){
2343            m.activeChild.hide();
2344        }
2345        if(m.autoHideTimer){
2346            clearTimeout(m.autoHideTimer);
2347            delete m.autoHideTimer;
2348        }
2349    }
2350
2351    // private
2352    function onBeforeShow(m){
2353        var pm = m.parentMenu;
2354        if(!pm && !m.allowOtherMenus){
2355            hideAll();
2356        }else if(pm && pm.activeChild && active != m){
2357            pm.activeChild.hide();
2358        }
2359    }
2360
2361    // private this should really trigger on mouseup..
2362    function onMouseDown(e){
2363         Roo.log("on Mouse Up");
2364         
2365         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
2366             Roo.log("MenuManager hideAll");
2367             hideAll();
2368             e.stopEvent();
2369         }
2370         
2371         
2372    }
2373
2374    // private
2375    function onBeforeCheck(mi, state){
2376        if(state){
2377            var g = groups[mi.group];
2378            for(var i = 0, l = g.length; i < l; i++){
2379                if(g[i] != mi){
2380                    g[i].setChecked(false);
2381                }
2382            }
2383        }
2384    }
2385
2386    return {
2387
2388        /**
2389         * Hides all menus that are currently visible
2390         */
2391        hideAll : function(){
2392             hideAll();  
2393        },
2394
2395        // private
2396        register : function(menu){
2397            if(!menus){
2398                init();
2399            }
2400            menus[menu.id] = menu;
2401            menu.on("beforehide", onBeforeHide);
2402            menu.on("hide", onHide);
2403            menu.on("beforeshow", onBeforeShow);
2404            menu.on("show", onShow);
2405            var g = menu.group;
2406            if(g && menu.events["checkchange"]){
2407                if(!groups[g]){
2408                    groups[g] = [];
2409                }
2410                groups[g].push(menu);
2411                menu.on("checkchange", onCheck);
2412            }
2413        },
2414
2415         /**
2416          * Returns a {@link Roo.menu.Menu} object
2417          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
2418          * be used to generate and return a new Menu instance.
2419          */
2420        get : function(menu){
2421            if(typeof menu == "string"){ // menu id
2422                return menus[menu];
2423            }else if(menu.events){  // menu instance
2424                return menu;
2425            }
2426            /*else if(typeof menu.length == 'number'){ // array of menu items?
2427                return new Roo.bootstrap.Menu({items:menu});
2428            }else{ // otherwise, must be a config
2429                return new Roo.bootstrap.Menu(menu);
2430            }
2431            */
2432            return false;
2433        },
2434
2435        // private
2436        unregister : function(menu){
2437            delete menus[menu.id];
2438            menu.un("beforehide", onBeforeHide);
2439            menu.un("hide", onHide);
2440            menu.un("beforeshow", onBeforeShow);
2441            menu.un("show", onShow);
2442            var g = menu.group;
2443            if(g && menu.events["checkchange"]){
2444                groups[g].remove(menu);
2445                menu.un("checkchange", onCheck);
2446            }
2447        },
2448
2449        // private
2450        registerCheckable : function(menuItem){
2451            var g = menuItem.group;
2452            if(g){
2453                if(!groups[g]){
2454                    groups[g] = [];
2455                }
2456                groups[g].push(menuItem);
2457                menuItem.on("beforecheckchange", onBeforeCheck);
2458            }
2459        },
2460
2461        // private
2462        unregisterCheckable : function(menuItem){
2463            var g = menuItem.group;
2464            if(g){
2465                groups[g].remove(menuItem);
2466                menuItem.un("beforecheckchange", onBeforeCheck);
2467            }
2468        }
2469    };
2470 }();/*
2471  * - LGPL
2472  *
2473  * menu
2474  * 
2475  */
2476
2477 /**
2478  * @class Roo.bootstrap.Menu
2479  * @extends Roo.bootstrap.Component
2480  * Bootstrap Menu class - container for MenuItems
2481  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2482  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2483  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2484  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2485  * 
2486  * @constructor
2487  * Create a new Menu
2488  * @param {Object} config The config object
2489  */
2490
2491
2492 Roo.bootstrap.Menu = function(config){
2493     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2494     if (this.registerMenu && this.type != 'treeview')  {
2495         Roo.bootstrap.MenuMgr.register(this);
2496     }
2497     
2498     
2499     this.addEvents({
2500         /**
2501          * @event beforeshow
2502          * Fires before this menu is displayed (return false to block)
2503          * @param {Roo.menu.Menu} this
2504          */
2505         beforeshow : true,
2506         /**
2507          * @event beforehide
2508          * Fires before this menu is hidden (return false to block)
2509          * @param {Roo.menu.Menu} this
2510          */
2511         beforehide : true,
2512         /**
2513          * @event show
2514          * Fires after this menu is displayed
2515          * @param {Roo.menu.Menu} this
2516          */
2517         show : true,
2518         /**
2519          * @event hide
2520          * Fires after this menu is hidden
2521          * @param {Roo.menu.Menu} this
2522          */
2523         hide : true,
2524         /**
2525          * @event click
2526          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2527          * @param {Roo.menu.Menu} this
2528          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2529          * @param {Roo.EventObject} e
2530          */
2531         click : true,
2532         /**
2533          * @event mouseover
2534          * Fires when the mouse is hovering over this menu
2535          * @param {Roo.menu.Menu} this
2536          * @param {Roo.EventObject} e
2537          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2538          */
2539         mouseover : true,
2540         /**
2541          * @event mouseout
2542          * Fires when the mouse exits this menu
2543          * @param {Roo.menu.Menu} this
2544          * @param {Roo.EventObject} e
2545          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2546          */
2547         mouseout : true,
2548         /**
2549          * @event itemclick
2550          * Fires when a menu item contained in this menu is clicked
2551          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2552          * @param {Roo.EventObject} e
2553          */
2554         itemclick: true
2555     });
2556     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2557 };
2558
2559 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2560     
2561    /// html : false,
2562     //align : '',
2563     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2564     type: false,
2565     /**
2566      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2567      */
2568     registerMenu : true,
2569     
2570     menuItems :false, // stores the menu items..
2571     
2572     hidden:true,
2573         
2574     parentMenu : false,
2575     
2576     stopEvent : true,
2577     
2578     isLink : false,
2579     
2580     getChildContainer : function() {
2581         return this.el;  
2582     },
2583     
2584     getAutoCreate : function(){
2585          
2586         //if (['right'].indexOf(this.align)!==-1) {
2587         //    cfg.cn[1].cls += ' pull-right'
2588         //}
2589         
2590         
2591         var cfg = {
2592             tag : 'ul',
2593             cls : 'dropdown-menu' ,
2594             style : 'z-index:1000'
2595             
2596         };
2597         
2598         if (this.type === 'submenu') {
2599             cfg.cls = 'submenu active';
2600         }
2601         if (this.type === 'treeview') {
2602             cfg.cls = 'treeview-menu';
2603         }
2604         
2605         return cfg;
2606     },
2607     initEvents : function() {
2608         
2609        // Roo.log("ADD event");
2610        // Roo.log(this.triggerEl.dom);
2611         
2612         this.triggerEl.on('click', this.onTriggerClick, this);
2613         
2614         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2615         
2616         
2617         if (this.triggerEl.hasClass('nav-item')) {
2618             // dropdown toggle on the 'a' in BS4?
2619             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2620         } else {
2621             this.triggerEl.addClass('dropdown-toggle');
2622         }
2623         if (Roo.isTouch) {
2624             this.el.on('touchstart'  , this.onTouch, this);
2625         }
2626         this.el.on('click' , this.onClick, this);
2627
2628         this.el.on("mouseover", this.onMouseOver, this);
2629         this.el.on("mouseout", this.onMouseOut, this);
2630         
2631     },
2632     
2633     findTargetItem : function(e)
2634     {
2635         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2636         if(!t){
2637             return false;
2638         }
2639         //Roo.log(t);         Roo.log(t.id);
2640         if(t && t.id){
2641             //Roo.log(this.menuitems);
2642             return this.menuitems.get(t.id);
2643             
2644             //return this.items.get(t.menuItemId);
2645         }
2646         
2647         return false;
2648     },
2649     
2650     onTouch : function(e) 
2651     {
2652         Roo.log("menu.onTouch");
2653         //e.stopEvent(); this make the user popdown broken
2654         this.onClick(e);
2655     },
2656     
2657     onClick : function(e)
2658     {
2659         Roo.log("menu.onClick");
2660         
2661         var t = this.findTargetItem(e);
2662         if(!t || t.isContainer){
2663             return;
2664         }
2665         Roo.log(e);
2666         /*
2667         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2668             if(t == this.activeItem && t.shouldDeactivate(e)){
2669                 this.activeItem.deactivate();
2670                 delete this.activeItem;
2671                 return;
2672             }
2673             if(t.canActivate){
2674                 this.setActiveItem(t, true);
2675             }
2676             return;
2677             
2678             
2679         }
2680         */
2681        
2682         Roo.log('pass click event');
2683         
2684         t.onClick(e);
2685         
2686         this.fireEvent("click", this, t, e);
2687         
2688         var _this = this;
2689         
2690         if(!t.href.length || t.href == '#'){
2691             (function() { _this.hide(); }).defer(100);
2692         }
2693         
2694     },
2695     
2696     onMouseOver : function(e){
2697         var t  = this.findTargetItem(e);
2698         //Roo.log(t);
2699         //if(t){
2700         //    if(t.canActivate && !t.disabled){
2701         //        this.setActiveItem(t, true);
2702         //    }
2703         //}
2704         
2705         this.fireEvent("mouseover", this, e, t);
2706     },
2707     isVisible : function(){
2708         return !this.hidden;
2709     },
2710     onMouseOut : function(e){
2711         var t  = this.findTargetItem(e);
2712         
2713         //if(t ){
2714         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2715         //        this.activeItem.deactivate();
2716         //        delete this.activeItem;
2717         //    }
2718         //}
2719         this.fireEvent("mouseout", this, e, t);
2720     },
2721     
2722     
2723     /**
2724      * Displays this menu relative to another element
2725      * @param {String/HTMLElement/Roo.Element} element The element to align to
2726      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2727      * the element (defaults to this.defaultAlign)
2728      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2729      */
2730     show : function(el, pos, parentMenu)
2731     {
2732         if (false === this.fireEvent("beforeshow", this)) {
2733             Roo.log("show canceled");
2734             return;
2735         }
2736         this.parentMenu = parentMenu;
2737         if(!this.el){
2738             this.render();
2739         }
2740         
2741         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2742     },
2743      /**
2744      * Displays this menu at a specific xy position
2745      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2746      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2747      */
2748     showAt : function(xy, parentMenu, /* private: */_e){
2749         this.parentMenu = parentMenu;
2750         if(!this.el){
2751             this.render();
2752         }
2753         if(_e !== false){
2754             this.fireEvent("beforeshow", this);
2755             //xy = this.el.adjustForConstraints(xy);
2756         }
2757         
2758         //this.el.show();
2759         this.hideMenuItems();
2760         this.hidden = false;
2761         this.triggerEl.addClass('open');
2762         this.el.addClass('show');
2763         
2764         // reassign x when hitting right
2765         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2766             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2767         }
2768         
2769         // reassign y when hitting bottom
2770         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2771             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2772         }
2773         
2774         // but the list may align on trigger left or trigger top... should it be a properity?
2775         
2776         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2777             this.el.setXY(xy);
2778         }
2779         
2780         this.focus();
2781         this.fireEvent("show", this);
2782     },
2783     
2784     focus : function(){
2785         return;
2786         if(!this.hidden){
2787             this.doFocus.defer(50, this);
2788         }
2789     },
2790
2791     doFocus : function(){
2792         if(!this.hidden){
2793             this.focusEl.focus();
2794         }
2795     },
2796
2797     /**
2798      * Hides this menu and optionally all parent menus
2799      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2800      */
2801     hide : function(deep)
2802     {
2803         if (false === this.fireEvent("beforehide", this)) {
2804             Roo.log("hide canceled");
2805             return;
2806         }
2807         this.hideMenuItems();
2808         if(this.el && this.isVisible()){
2809            
2810             if(this.activeItem){
2811                 this.activeItem.deactivate();
2812                 this.activeItem = null;
2813             }
2814             this.triggerEl.removeClass('open');;
2815             this.el.removeClass('show');
2816             this.hidden = true;
2817             this.fireEvent("hide", this);
2818         }
2819         if(deep === true && this.parentMenu){
2820             this.parentMenu.hide(true);
2821         }
2822     },
2823     
2824     onTriggerClick : function(e)
2825     {
2826         Roo.log('trigger click');
2827         
2828         var target = e.getTarget();
2829         
2830         Roo.log(target.nodeName.toLowerCase());
2831         
2832         if(target.nodeName.toLowerCase() === 'i'){
2833             e.preventDefault();
2834         }
2835         
2836     },
2837     
2838     onTriggerPress  : function(e)
2839     {
2840         Roo.log('trigger press');
2841         //Roo.log(e.getTarget());
2842        // Roo.log(this.triggerEl.dom);
2843        
2844         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2845         var pel = Roo.get(e.getTarget());
2846         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2847             Roo.log('is treeview or dropdown?');
2848             return;
2849         }
2850         
2851         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2852             return;
2853         }
2854         
2855         if (this.isVisible()) {
2856             Roo.log('hide');
2857             this.hide();
2858         } else {
2859             Roo.log('show');
2860             this.show(this.triggerEl, '?', false);
2861         }
2862         
2863         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2864             e.stopEvent();
2865         }
2866         
2867     },
2868        
2869     
2870     hideMenuItems : function()
2871     {
2872         Roo.log("hide Menu Items");
2873         if (!this.el) { 
2874             return;
2875         }
2876         
2877         this.el.select('.open',true).each(function(aa) {
2878             
2879             aa.removeClass('open');
2880          
2881         });
2882     },
2883     addxtypeChild : function (tree, cntr) {
2884         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2885           
2886         this.menuitems.add(comp);
2887         return comp;
2888
2889     },
2890     getEl : function()
2891     {
2892         Roo.log(this.el);
2893         return this.el;
2894     },
2895     
2896     clear : function()
2897     {
2898         this.getEl().dom.innerHTML = '';
2899         this.menuitems.clear();
2900     }
2901 });
2902
2903  
2904  /*
2905  * - LGPL
2906  *
2907  * menu item
2908  * 
2909  */
2910
2911
2912 /**
2913  * @class Roo.bootstrap.MenuItem
2914  * @extends Roo.bootstrap.Component
2915  * Bootstrap MenuItem class
2916  * @cfg {String} html the menu label
2917  * @cfg {String} href the link
2918  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2919  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2920  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2921  * @cfg {String} fa favicon to show on left of menu item.
2922  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2923  * 
2924  * 
2925  * @constructor
2926  * Create a new MenuItem
2927  * @param {Object} config The config object
2928  */
2929
2930
2931 Roo.bootstrap.MenuItem = function(config){
2932     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2933     this.addEvents({
2934         // raw events
2935         /**
2936          * @event click
2937          * The raw click event for the entire grid.
2938          * @param {Roo.bootstrap.MenuItem} this
2939          * @param {Roo.EventObject} e
2940          */
2941         "click" : true
2942     });
2943 };
2944
2945 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2946     
2947     href : false,
2948     html : false,
2949     preventDefault: false,
2950     isContainer : false,
2951     active : false,
2952     fa: false,
2953     
2954     getAutoCreate : function(){
2955         
2956         if(this.isContainer){
2957             return {
2958                 tag: 'li',
2959                 cls: 'dropdown-menu-item '
2960             };
2961         }
2962         var ctag = {
2963             tag: 'span',
2964             html: 'Link'
2965         };
2966         
2967         var anc = {
2968             tag : 'a',
2969             cls : 'dropdown-item',
2970             href : '#',
2971             cn : [  ]
2972         };
2973         
2974         if (this.fa !== false) {
2975             anc.cn.push({
2976                 tag : 'i',
2977                 cls : 'fa fa-' + this.fa
2978             });
2979         }
2980         
2981         anc.cn.push(ctag);
2982         
2983         
2984         var cfg= {
2985             tag: 'li',
2986             cls: 'dropdown-menu-item',
2987             cn: [ anc ]
2988         };
2989         if (this.parent().type == 'treeview') {
2990             cfg.cls = 'treeview-menu';
2991         }
2992         if (this.active) {
2993             cfg.cls += ' active';
2994         }
2995         
2996         
2997         
2998         anc.href = this.href || cfg.cn[0].href ;
2999         ctag.html = this.html || cfg.cn[0].html ;
3000         return cfg;
3001     },
3002     
3003     initEvents: function()
3004     {
3005         if (this.parent().type == 'treeview') {
3006             this.el.select('a').on('click', this.onClick, this);
3007         }
3008         
3009         if (this.menu) {
3010             this.menu.parentType = this.xtype;
3011             this.menu.triggerEl = this.el;
3012             this.menu = this.addxtype(Roo.apply({}, this.menu));
3013         }
3014         
3015     },
3016     onClick : function(e)
3017     {
3018         Roo.log('item on click ');
3019         
3020         if(this.preventDefault){
3021             e.preventDefault();
3022         }
3023         //this.parent().hideMenuItems();
3024         
3025         this.fireEvent('click', this, e);
3026     },
3027     getEl : function()
3028     {
3029         return this.el;
3030     } 
3031 });
3032
3033  
3034
3035  /*
3036  * - LGPL
3037  *
3038  * menu separator
3039  * 
3040  */
3041
3042
3043 /**
3044  * @class Roo.bootstrap.MenuSeparator
3045  * @extends Roo.bootstrap.Component
3046  * Bootstrap MenuSeparator class
3047  * 
3048  * @constructor
3049  * Create a new MenuItem
3050  * @param {Object} config The config object
3051  */
3052
3053
3054 Roo.bootstrap.MenuSeparator = function(config){
3055     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3056 };
3057
3058 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3059     
3060     getAutoCreate : function(){
3061         var cfg = {
3062             cls: 'divider',
3063             tag : 'li'
3064         };
3065         
3066         return cfg;
3067     }
3068    
3069 });
3070
3071  
3072
3073  
3074 /*
3075 * Licence: LGPL
3076 */
3077
3078 /**
3079  * @class Roo.bootstrap.Modal
3080  * @extends Roo.bootstrap.Component
3081  * Bootstrap Modal class
3082  * @cfg {String} title Title of dialog
3083  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3084  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3085  * @cfg {Boolean} specificTitle default false
3086  * @cfg {Array} buttons Array of buttons or standard button set..
3087  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3088  * @cfg {Boolean} animate default true
3089  * @cfg {Boolean} allow_close default true
3090  * @cfg {Boolean} fitwindow default false
3091  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3092  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3093  * @cfg {String} size (sm|lg) default empty
3094  * @cfg {Number} max_width set the max width of modal
3095  *
3096  *
3097  * @constructor
3098  * Create a new Modal Dialog
3099  * @param {Object} config The config object
3100  */
3101
3102 Roo.bootstrap.Modal = function(config){
3103     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3104     this.addEvents({
3105         // raw events
3106         /**
3107          * @event btnclick
3108          * The raw btnclick event for the button
3109          * @param {Roo.EventObject} e
3110          */
3111         "btnclick" : true,
3112         /**
3113          * @event resize
3114          * Fire when dialog resize
3115          * @param {Roo.bootstrap.Modal} this
3116          * @param {Roo.EventObject} e
3117          */
3118         "resize" : true
3119     });
3120     this.buttons = this.buttons || [];
3121
3122     if (this.tmpl) {
3123         this.tmpl = Roo.factory(this.tmpl);
3124     }
3125
3126 };
3127
3128 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3129
3130     title : 'test dialog',
3131
3132     buttons : false,
3133
3134     // set on load...
3135
3136     html: false,
3137
3138     tmp: false,
3139
3140     specificTitle: false,
3141
3142     buttonPosition: 'right',
3143
3144     allow_close : true,
3145
3146     animate : true,
3147
3148     fitwindow: false,
3149     
3150      // private
3151     dialogEl: false,
3152     bodyEl:  false,
3153     footerEl:  false,
3154     titleEl:  false,
3155     closeEl:  false,
3156
3157     size: '',
3158     
3159     max_width: 0,
3160     
3161     max_height: 0,
3162     
3163     fit_content: false,
3164
3165     onRender : function(ct, position)
3166     {
3167         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3168
3169         if(!this.el){
3170             var cfg = Roo.apply({},  this.getAutoCreate());
3171             cfg.id = Roo.id();
3172             //if(!cfg.name){
3173             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3174             //}
3175             //if (!cfg.name.length) {
3176             //    delete cfg.name;
3177            // }
3178             if (this.cls) {
3179                 cfg.cls += ' ' + this.cls;
3180             }
3181             if (this.style) {
3182                 cfg.style = this.style;
3183             }
3184             this.el = Roo.get(document.body).createChild(cfg, position);
3185         }
3186         //var type = this.el.dom.type;
3187
3188
3189         if(this.tabIndex !== undefined){
3190             this.el.dom.setAttribute('tabIndex', this.tabIndex);
3191         }
3192
3193         this.dialogEl = this.el.select('.modal-dialog',true).first();
3194         this.bodyEl = this.el.select('.modal-body',true).first();
3195         this.closeEl = this.el.select('.modal-header .close', true).first();
3196         this.headerEl = this.el.select('.modal-header',true).first();
3197         this.titleEl = this.el.select('.modal-title',true).first();
3198         this.footerEl = this.el.select('.modal-footer',true).first();
3199
3200         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
3201         
3202         //this.el.addClass("x-dlg-modal");
3203
3204         if (this.buttons.length) {
3205             Roo.each(this.buttons, function(bb) {
3206                 var b = Roo.apply({}, bb);
3207                 b.xns = b.xns || Roo.bootstrap;
3208                 b.xtype = b.xtype || 'Button';
3209                 if (typeof(b.listeners) == 'undefined') {
3210                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
3211                 }
3212
3213                 var btn = Roo.factory(b);
3214
3215                 btn.render(this.getButtonContainer());
3216
3217             },this);
3218         }
3219         // render the children.
3220         var nitems = [];
3221
3222         if(typeof(this.items) != 'undefined'){
3223             var items = this.items;
3224             delete this.items;
3225
3226             for(var i =0;i < items.length;i++) {
3227                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
3228             }
3229         }
3230
3231         this.items = nitems;
3232
3233         // where are these used - they used to be body/close/footer
3234
3235
3236         this.initEvents();
3237         //this.el.addClass([this.fieldClass, this.cls]);
3238
3239     },
3240
3241     getAutoCreate : function()
3242     {
3243         // we will default to modal-body-overflow - might need to remove or make optional later.
3244         var bdy = {
3245                 cls : 'modal-body enable-modal-body-overflow ', 
3246                 html : this.html || ''
3247         };
3248
3249         var title = {
3250             tag: 'h4',
3251             cls : 'modal-title',
3252             html : this.title
3253         };
3254
3255         if(this.specificTitle){
3256             title = this.title;
3257
3258         }
3259
3260         var header = [];
3261         if (this.allow_close && Roo.bootstrap.version == 3) {
3262             header.push({
3263                 tag: 'button',
3264                 cls : 'close',
3265                 html : '&times'
3266             });
3267         }
3268
3269         header.push(title);
3270
3271         if (this.allow_close && Roo.bootstrap.version == 4) {
3272             header.push({
3273                 tag: 'button',
3274                 cls : 'close',
3275                 html : '&times'
3276             });
3277         }
3278         
3279         var size = '';
3280
3281         if(this.size.length){
3282             size = 'modal-' + this.size;
3283         }
3284         
3285         var footer = Roo.bootstrap.version == 3 ?
3286             {
3287                 cls : 'modal-footer',
3288                 cn : [
3289                     {
3290                         tag: 'div',
3291                         cls: 'btn-' + this.buttonPosition
3292                     }
3293                 ]
3294
3295             } :
3296             {  // BS4 uses mr-auto on left buttons....
3297                 cls : 'modal-footer'
3298             };
3299
3300             
3301
3302         
3303         
3304         var modal = {
3305             cls: "modal",
3306              cn : [
3307                 {
3308                     cls: "modal-dialog " + size,
3309                     cn : [
3310                         {
3311                             cls : "modal-content",
3312                             cn : [
3313                                 {
3314                                     cls : 'modal-header',
3315                                     cn : header
3316                                 },
3317                                 bdy,
3318                                 footer
3319                             ]
3320
3321                         }
3322                     ]
3323
3324                 }
3325             ]
3326         };
3327
3328         if(this.animate){
3329             modal.cls += ' fade';
3330         }
3331
3332         return modal;
3333
3334     },
3335     getChildContainer : function() {
3336
3337          return this.bodyEl;
3338
3339     },
3340     getButtonContainer : function() {
3341         
3342          return Roo.bootstrap.version == 4 ?
3343             this.el.select('.modal-footer',true).first()
3344             : this.el.select('.modal-footer div',true).first();
3345
3346     },
3347     initEvents : function()
3348     {
3349         if (this.allow_close) {
3350             this.closeEl.on('click', this.hide, this);
3351         }
3352         Roo.EventManager.onWindowResize(this.resize, this, true);
3353
3354
3355     },
3356   
3357
3358     resize : function()
3359     {
3360         this.maskEl.setSize(
3361             Roo.lib.Dom.getViewWidth(true),
3362             Roo.lib.Dom.getViewHeight(true)
3363         );
3364         
3365         if (this.fitwindow) {
3366             
3367            
3368             this.setSize(
3369                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
3370                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
3371             );
3372             return;
3373         }
3374         
3375         if(this.max_width !== 0) {
3376             
3377             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
3378             
3379             if(this.height) {
3380                 this.setSize(w, this.height);
3381                 return;
3382             }
3383             
3384             if(this.max_height) {
3385                 this.setSize(w,Math.min(
3386                     this.max_height,
3387                     Roo.lib.Dom.getViewportHeight(true) - 60
3388                 ));
3389                 
3390                 return;
3391             }
3392             
3393             if(!this.fit_content) {
3394                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
3395                 return;
3396             }
3397             
3398             this.setSize(w, Math.min(
3399                 60 +
3400                 this.headerEl.getHeight() + 
3401                 this.footerEl.getHeight() + 
3402                 this.getChildHeight(this.bodyEl.dom.childNodes),
3403                 Roo.lib.Dom.getViewportHeight(true) - 60)
3404             );
3405         }
3406         
3407     },
3408
3409     setSize : function(w,h)
3410     {
3411         if (!w && !h) {
3412             return;
3413         }
3414         
3415         this.resizeTo(w,h);
3416     },
3417
3418     show : function() {
3419
3420         if (!this.rendered) {
3421             this.render();
3422         }
3423
3424         //this.el.setStyle('display', 'block');
3425         this.el.removeClass('hideing');
3426         this.el.dom.style.display='block';
3427         
3428         Roo.get(document.body).addClass('modal-open');
3429  
3430         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
3431             
3432             (function(){
3433                 this.el.addClass('show');
3434                 this.el.addClass('in');
3435             }).defer(50, this);
3436         }else{
3437             this.el.addClass('show');
3438             this.el.addClass('in');
3439         }
3440
3441         // not sure how we can show data in here..
3442         //if (this.tmpl) {
3443         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3444         //}
3445
3446         Roo.get(document.body).addClass("x-body-masked");
3447         
3448         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3449         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3450         this.maskEl.dom.style.display = 'block';
3451         this.maskEl.addClass('show');
3452         
3453         
3454         this.resize();
3455         
3456         this.fireEvent('show', this);
3457
3458         // set zindex here - otherwise it appears to be ignored...
3459         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3460
3461         (function () {
3462             this.items.forEach( function(e) {
3463                 e.layout ? e.layout() : false;
3464
3465             });
3466         }).defer(100,this);
3467
3468     },
3469     hide : function()
3470     {
3471         if(this.fireEvent("beforehide", this) !== false){
3472             
3473             this.maskEl.removeClass('show');
3474             
3475             this.maskEl.dom.style.display = '';
3476             Roo.get(document.body).removeClass("x-body-masked");
3477             this.el.removeClass('in');
3478             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3479
3480             if(this.animate){ // why
3481                 this.el.addClass('hideing');
3482                 this.el.removeClass('show');
3483                 (function(){
3484                     if (!this.el.hasClass('hideing')) {
3485                         return; // it's been shown again...
3486                     }
3487                     
3488                     this.el.dom.style.display='';
3489
3490                     Roo.get(document.body).removeClass('modal-open');
3491                     this.el.removeClass('hideing');
3492                 }).defer(150,this);
3493                 
3494             }else{
3495                 this.el.removeClass('show');
3496                 this.el.dom.style.display='';
3497                 Roo.get(document.body).removeClass('modal-open');
3498
3499             }
3500             this.fireEvent('hide', this);
3501         }
3502     },
3503     isVisible : function()
3504     {
3505         
3506         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3507         
3508     },
3509
3510     addButton : function(str, cb)
3511     {
3512
3513
3514         var b = Roo.apply({}, { html : str } );
3515         b.xns = b.xns || Roo.bootstrap;
3516         b.xtype = b.xtype || 'Button';
3517         if (typeof(b.listeners) == 'undefined') {
3518             b.listeners = { click : cb.createDelegate(this)  };
3519         }
3520
3521         var btn = Roo.factory(b);
3522
3523         btn.render(this.getButtonContainer());
3524
3525         return btn;
3526
3527     },
3528
3529     setDefaultButton : function(btn)
3530     {
3531         //this.el.select('.modal-footer').()
3532     },
3533
3534     resizeTo: function(w,h)
3535     {
3536         this.dialogEl.setWidth(w);
3537         
3538         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
3539
3540         this.bodyEl.setHeight(h - diff);
3541         
3542         this.fireEvent('resize', this);
3543     },
3544     
3545     setContentSize  : function(w, h)
3546     {
3547
3548     },
3549     onButtonClick: function(btn,e)
3550     {
3551         //Roo.log([a,b,c]);
3552         this.fireEvent('btnclick', btn.name, e);
3553     },
3554      /**
3555      * Set the title of the Dialog
3556      * @param {String} str new Title
3557      */
3558     setTitle: function(str) {
3559         this.titleEl.dom.innerHTML = str;
3560     },
3561     /**
3562      * Set the body of the Dialog
3563      * @param {String} str new Title
3564      */
3565     setBody: function(str) {
3566         this.bodyEl.dom.innerHTML = str;
3567     },
3568     /**
3569      * Set the body of the Dialog using the template
3570      * @param {Obj} data - apply this data to the template and replace the body contents.
3571      */
3572     applyBody: function(obj)
3573     {
3574         if (!this.tmpl) {
3575             Roo.log("Error - using apply Body without a template");
3576             //code
3577         }
3578         this.tmpl.overwrite(this.bodyEl, obj);
3579     },
3580     
3581     getChildHeight : function(child_nodes)
3582     {
3583         if(
3584             !child_nodes ||
3585             child_nodes.length == 0
3586         ) {
3587             return;
3588         }
3589         
3590         var child_height = 0;
3591         
3592         for(var i = 0; i < child_nodes.length; i++) {
3593             
3594             /*
3595             * for modal with tabs...
3596             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3597                 
3598                 var layout_childs = child_nodes[i].childNodes;
3599                 
3600                 for(var j = 0; j < layout_childs.length; j++) {
3601                     
3602                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3603                         
3604                         var layout_body_childs = layout_childs[j].childNodes;
3605                         
3606                         for(var k = 0; k < layout_body_childs.length; k++) {
3607                             
3608                             if(layout_body_childs[k].classList.contains('navbar')) {
3609                                 child_height += layout_body_childs[k].offsetHeight;
3610                                 continue;
3611                             }
3612                             
3613                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3614                                 
3615                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3616                                 
3617                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3618                                     
3619                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3620                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3621                                         continue;
3622                                     }
3623                                     
3624                                 }
3625                                 
3626                             }
3627                             
3628                         }
3629                     }
3630                 }
3631                 continue;
3632             }
3633             */
3634             
3635             child_height += child_nodes[i].offsetHeight;
3636             // Roo.log(child_nodes[i].offsetHeight);
3637         }
3638         
3639         return child_height;
3640     }
3641
3642 });
3643
3644
3645 Roo.apply(Roo.bootstrap.Modal,  {
3646     /**
3647          * Button config that displays a single OK button
3648          * @type Object
3649          */
3650         OK :  [{
3651             name : 'ok',
3652             weight : 'primary',
3653             html : 'OK'
3654         }],
3655         /**
3656          * Button config that displays Yes and No buttons
3657          * @type Object
3658          */
3659         YESNO : [
3660             {
3661                 name  : 'no',
3662                 html : 'No'
3663             },
3664             {
3665                 name  :'yes',
3666                 weight : 'primary',
3667                 html : 'Yes'
3668             }
3669         ],
3670
3671         /**
3672          * Button config that displays OK and Cancel buttons
3673          * @type Object
3674          */
3675         OKCANCEL : [
3676             {
3677                name : 'cancel',
3678                 html : 'Cancel'
3679             },
3680             {
3681                 name : 'ok',
3682                 weight : 'primary',
3683                 html : 'OK'
3684             }
3685         ],
3686         /**
3687          * Button config that displays Yes, No and Cancel buttons
3688          * @type Object
3689          */
3690         YESNOCANCEL : [
3691             {
3692                 name : 'yes',
3693                 weight : 'primary',
3694                 html : 'Yes'
3695             },
3696             {
3697                 name : 'no',
3698                 html : 'No'
3699             },
3700             {
3701                 name : 'cancel',
3702                 html : 'Cancel'
3703             }
3704         ],
3705         
3706         zIndex : 10001
3707 });
3708 /*
3709  * - LGPL
3710  *
3711  * messagebox - can be used as a replace
3712  * 
3713  */
3714 /**
3715  * @class Roo.MessageBox
3716  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3717  * Example usage:
3718  *<pre><code>
3719 // Basic alert:
3720 Roo.Msg.alert('Status', 'Changes saved successfully.');
3721
3722 // Prompt for user data:
3723 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3724     if (btn == 'ok'){
3725         // process text value...
3726     }
3727 });
3728
3729 // Show a dialog using config options:
3730 Roo.Msg.show({
3731    title:'Save Changes?',
3732    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3733    buttons: Roo.Msg.YESNOCANCEL,
3734    fn: processResult,
3735    animEl: 'elId'
3736 });
3737 </code></pre>
3738  * @singleton
3739  */
3740 Roo.bootstrap.MessageBox = function(){
3741     var dlg, opt, mask, waitTimer;
3742     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3743     var buttons, activeTextEl, bwidth;
3744
3745     
3746     // private
3747     var handleButton = function(button){
3748         dlg.hide();
3749         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3750     };
3751
3752     // private
3753     var handleHide = function(){
3754         if(opt && opt.cls){
3755             dlg.el.removeClass(opt.cls);
3756         }
3757         //if(waitTimer){
3758         //    Roo.TaskMgr.stop(waitTimer);
3759         //    waitTimer = null;
3760         //}
3761     };
3762
3763     // private
3764     var updateButtons = function(b){
3765         var width = 0;
3766         if(!b){
3767             buttons["ok"].hide();
3768             buttons["cancel"].hide();
3769             buttons["yes"].hide();
3770             buttons["no"].hide();
3771             dlg.footerEl.hide();
3772             
3773             return width;
3774         }
3775         dlg.footerEl.show();
3776         for(var k in buttons){
3777             if(typeof buttons[k] != "function"){
3778                 if(b[k]){
3779                     buttons[k].show();
3780                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3781                     width += buttons[k].el.getWidth()+15;
3782                 }else{
3783                     buttons[k].hide();
3784                 }
3785             }
3786         }
3787         return width;
3788     };
3789
3790     // private
3791     var handleEsc = function(d, k, e){
3792         if(opt && opt.closable !== false){
3793             dlg.hide();
3794         }
3795         if(e){
3796             e.stopEvent();
3797         }
3798     };
3799
3800     return {
3801         /**
3802          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3803          * @return {Roo.BasicDialog} The BasicDialog element
3804          */
3805         getDialog : function(){
3806            if(!dlg){
3807                 dlg = new Roo.bootstrap.Modal( {
3808                     //draggable: true,
3809                     //resizable:false,
3810                     //constraintoviewport:false,
3811                     //fixedcenter:true,
3812                     //collapsible : false,
3813                     //shim:true,
3814                     //modal: true,
3815                 //    width: 'auto',
3816                   //  height:100,
3817                     //buttonAlign:"center",
3818                     closeClick : function(){
3819                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3820                             handleButton("no");
3821                         }else{
3822                             handleButton("cancel");
3823                         }
3824                     }
3825                 });
3826                 dlg.render();
3827                 dlg.on("hide", handleHide);
3828                 mask = dlg.mask;
3829                 //dlg.addKeyListener(27, handleEsc);
3830                 buttons = {};
3831                 this.buttons = buttons;
3832                 var bt = this.buttonText;
3833                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3834                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3835                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3836                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3837                 //Roo.log(buttons);
3838                 bodyEl = dlg.bodyEl.createChild({
3839
3840                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3841                         '<textarea class="roo-mb-textarea"></textarea>' +
3842                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3843                 });
3844                 msgEl = bodyEl.dom.firstChild;
3845                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3846                 textboxEl.enableDisplayMode();
3847                 textboxEl.addKeyListener([10,13], function(){
3848                     if(dlg.isVisible() && opt && opt.buttons){
3849                         if(opt.buttons.ok){
3850                             handleButton("ok");
3851                         }else if(opt.buttons.yes){
3852                             handleButton("yes");
3853                         }
3854                     }
3855                 });
3856                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3857                 textareaEl.enableDisplayMode();
3858                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3859                 progressEl.enableDisplayMode();
3860                 
3861                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3862                 var pf = progressEl.dom.firstChild;
3863                 if (pf) {
3864                     pp = Roo.get(pf.firstChild);
3865                     pp.setHeight(pf.offsetHeight);
3866                 }
3867                 
3868             }
3869             return dlg;
3870         },
3871
3872         /**
3873          * Updates the message box body text
3874          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3875          * the XHTML-compliant non-breaking space character '&amp;#160;')
3876          * @return {Roo.MessageBox} This message box
3877          */
3878         updateText : function(text)
3879         {
3880             if(!dlg.isVisible() && !opt.width){
3881                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3882                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3883             }
3884             msgEl.innerHTML = text || '&#160;';
3885       
3886             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3887             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3888             var w = Math.max(
3889                     Math.min(opt.width || cw , this.maxWidth), 
3890                     Math.max(opt.minWidth || this.minWidth, bwidth)
3891             );
3892             if(opt.prompt){
3893                 activeTextEl.setWidth(w);
3894             }
3895             if(dlg.isVisible()){
3896                 dlg.fixedcenter = false;
3897             }
3898             // to big, make it scroll. = But as usual stupid IE does not support
3899             // !important..
3900             
3901             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3902                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3903                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3904             } else {
3905                 bodyEl.dom.style.height = '';
3906                 bodyEl.dom.style.overflowY = '';
3907             }
3908             if (cw > w) {
3909                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3910             } else {
3911                 bodyEl.dom.style.overflowX = '';
3912             }
3913             
3914             dlg.setContentSize(w, bodyEl.getHeight());
3915             if(dlg.isVisible()){
3916                 dlg.fixedcenter = true;
3917             }
3918             return this;
3919         },
3920
3921         /**
3922          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3923          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3924          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3925          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3926          * @return {Roo.MessageBox} This message box
3927          */
3928         updateProgress : function(value, text){
3929             if(text){
3930                 this.updateText(text);
3931             }
3932             
3933             if (pp) { // weird bug on my firefox - for some reason this is not defined
3934                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3935                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3936             }
3937             return this;
3938         },        
3939
3940         /**
3941          * Returns true if the message box is currently displayed
3942          * @return {Boolean} True if the message box is visible, else false
3943          */
3944         isVisible : function(){
3945             return dlg && dlg.isVisible();  
3946         },
3947
3948         /**
3949          * Hides the message box if it is displayed
3950          */
3951         hide : function(){
3952             if(this.isVisible()){
3953                 dlg.hide();
3954             }  
3955         },
3956
3957         /**
3958          * Displays a new message box, or reinitializes an existing message box, based on the config options
3959          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3960          * The following config object properties are supported:
3961          * <pre>
3962 Property    Type             Description
3963 ----------  ---------------  ------------------------------------------------------------------------------------
3964 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3965                                    closes (defaults to undefined)
3966 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3967                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3968 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3969                                    progress and wait dialogs will ignore this property and always hide the
3970                                    close button as they can only be closed programmatically.
3971 cls               String           A custom CSS class to apply to the message box element
3972 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3973                                    displayed (defaults to 75)
3974 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3975                                    function will be btn (the name of the button that was clicked, if applicable,
3976                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3977                                    Progress and wait dialogs will ignore this option since they do not respond to
3978                                    user actions and can only be closed programmatically, so any required function
3979                                    should be called by the same code after it closes the dialog.
3980 icon              String           A CSS class that provides a background image to be used as an icon for
3981                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3982 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3983 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3984 modal             Boolean          False to allow user interaction with the page while the message box is
3985                                    displayed (defaults to true)
3986 msg               String           A string that will replace the existing message box body text (defaults
3987                                    to the XHTML-compliant non-breaking space character '&#160;')
3988 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3989 progress          Boolean          True to display a progress bar (defaults to false)
3990 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3991 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3992 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3993 title             String           The title text
3994 value             String           The string value to set into the active textbox element if displayed
3995 wait              Boolean          True to display a progress bar (defaults to false)
3996 width             Number           The width of the dialog in pixels
3997 </pre>
3998          *
3999          * Example usage:
4000          * <pre><code>
4001 Roo.Msg.show({
4002    title: 'Address',
4003    msg: 'Please enter your address:',
4004    width: 300,
4005    buttons: Roo.MessageBox.OKCANCEL,
4006    multiline: true,
4007    fn: saveAddress,
4008    animEl: 'addAddressBtn'
4009 });
4010 </code></pre>
4011          * @param {Object} config Configuration options
4012          * @return {Roo.MessageBox} This message box
4013          */
4014         show : function(options)
4015         {
4016             
4017             // this causes nightmares if you show one dialog after another
4018             // especially on callbacks..
4019              
4020             if(this.isVisible()){
4021                 
4022                 this.hide();
4023                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4024                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4025                 Roo.log("New Dialog Message:" +  options.msg )
4026                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4027                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4028                 
4029             }
4030             var d = this.getDialog();
4031             opt = options;
4032             d.setTitle(opt.title || "&#160;");
4033             d.closeEl.setDisplayed(opt.closable !== false);
4034             activeTextEl = textboxEl;
4035             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4036             if(opt.prompt){
4037                 if(opt.multiline){
4038                     textboxEl.hide();
4039                     textareaEl.show();
4040                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4041                         opt.multiline : this.defaultTextHeight);
4042                     activeTextEl = textareaEl;
4043                 }else{
4044                     textboxEl.show();
4045                     textareaEl.hide();
4046                 }
4047             }else{
4048                 textboxEl.hide();
4049                 textareaEl.hide();
4050             }
4051             progressEl.setDisplayed(opt.progress === true);
4052             if (opt.progress) {
4053                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4054             }
4055             this.updateProgress(0);
4056             activeTextEl.dom.value = opt.value || "";
4057             if(opt.prompt){
4058                 dlg.setDefaultButton(activeTextEl);
4059             }else{
4060                 var bs = opt.buttons;
4061                 var db = null;
4062                 if(bs && bs.ok){
4063                     db = buttons["ok"];
4064                 }else if(bs && bs.yes){
4065                     db = buttons["yes"];
4066                 }
4067                 dlg.setDefaultButton(db);
4068             }
4069             bwidth = updateButtons(opt.buttons);
4070             this.updateText(opt.msg);
4071             if(opt.cls){
4072                 d.el.addClass(opt.cls);
4073             }
4074             d.proxyDrag = opt.proxyDrag === true;
4075             d.modal = opt.modal !== false;
4076             d.mask = opt.modal !== false ? mask : false;
4077             if(!d.isVisible()){
4078                 // force it to the end of the z-index stack so it gets a cursor in FF
4079                 document.body.appendChild(dlg.el.dom);
4080                 d.animateTarget = null;
4081                 d.show(options.animEl);
4082             }
4083             return this;
4084         },
4085
4086         /**
4087          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
4088          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
4089          * and closing the message box when the process is complete.
4090          * @param {String} title The title bar text
4091          * @param {String} msg The message box body text
4092          * @return {Roo.MessageBox} This message box
4093          */
4094         progress : function(title, msg){
4095             this.show({
4096                 title : title,
4097                 msg : msg,
4098                 buttons: false,
4099                 progress:true,
4100                 closable:false,
4101                 minWidth: this.minProgressWidth,
4102                 modal : true
4103             });
4104             return this;
4105         },
4106
4107         /**
4108          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
4109          * If a callback function is passed it will be called after the user clicks the button, and the
4110          * id of the button that was clicked will be passed as the only parameter to the callback
4111          * (could also be the top-right close button).
4112          * @param {String} title The title bar text
4113          * @param {String} msg The message box body text
4114          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4115          * @param {Object} scope (optional) The scope of the callback function
4116          * @return {Roo.MessageBox} This message box
4117          */
4118         alert : function(title, msg, fn, scope)
4119         {
4120             this.show({
4121                 title : title,
4122                 msg : msg,
4123                 buttons: this.OK,
4124                 fn: fn,
4125                 closable : false,
4126                 scope : scope,
4127                 modal : true
4128             });
4129             return this;
4130         },
4131
4132         /**
4133          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
4134          * interaction while waiting for a long-running process to complete that does not have defined intervals.
4135          * You are responsible for closing the message box when the process is complete.
4136          * @param {String} msg The message box body text
4137          * @param {String} title (optional) The title bar text
4138          * @return {Roo.MessageBox} This message box
4139          */
4140         wait : function(msg, title){
4141             this.show({
4142                 title : title,
4143                 msg : msg,
4144                 buttons: false,
4145                 closable:false,
4146                 progress:true,
4147                 modal:true,
4148                 width:300,
4149                 wait:true
4150             });
4151             waitTimer = Roo.TaskMgr.start({
4152                 run: function(i){
4153                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
4154                 },
4155                 interval: 1000
4156             });
4157             return this;
4158         },
4159
4160         /**
4161          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
4162          * If a callback function is passed it will be called after the user clicks either button, and the id of the
4163          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
4164          * @param {String} title The title bar text
4165          * @param {String} msg The message box body text
4166          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4167          * @param {Object} scope (optional) The scope of the callback function
4168          * @return {Roo.MessageBox} This message box
4169          */
4170         confirm : function(title, msg, fn, scope){
4171             this.show({
4172                 title : title,
4173                 msg : msg,
4174                 buttons: this.YESNO,
4175                 fn: fn,
4176                 scope : scope,
4177                 modal : true
4178             });
4179             return this;
4180         },
4181
4182         /**
4183          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
4184          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
4185          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
4186          * (could also be the top-right close button) and the text that was entered will be passed as the two
4187          * parameters to the callback.
4188          * @param {String} title The title bar text
4189          * @param {String} msg The message box body text
4190          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4191          * @param {Object} scope (optional) The scope of the callback function
4192          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
4193          * property, or the height in pixels to create the textbox (defaults to false / single-line)
4194          * @return {Roo.MessageBox} This message box
4195          */
4196         prompt : function(title, msg, fn, scope, multiline){
4197             this.show({
4198                 title : title,
4199                 msg : msg,
4200                 buttons: this.OKCANCEL,
4201                 fn: fn,
4202                 minWidth:250,
4203                 scope : scope,
4204                 prompt:true,
4205                 multiline: multiline,
4206                 modal : true
4207             });
4208             return this;
4209         },
4210
4211         /**
4212          * Button config that displays a single OK button
4213          * @type Object
4214          */
4215         OK : {ok:true},
4216         /**
4217          * Button config that displays Yes and No buttons
4218          * @type Object
4219          */
4220         YESNO : {yes:true, no:true},
4221         /**
4222          * Button config that displays OK and Cancel buttons
4223          * @type Object
4224          */
4225         OKCANCEL : {ok:true, cancel:true},
4226         /**
4227          * Button config that displays Yes, No and Cancel buttons
4228          * @type Object
4229          */
4230         YESNOCANCEL : {yes:true, no:true, cancel:true},
4231
4232         /**
4233          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
4234          * @type Number
4235          */
4236         defaultTextHeight : 75,
4237         /**
4238          * The maximum width in pixels of the message box (defaults to 600)
4239          * @type Number
4240          */
4241         maxWidth : 600,
4242         /**
4243          * The minimum width in pixels of the message box (defaults to 100)
4244          * @type Number
4245          */
4246         minWidth : 100,
4247         /**
4248          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
4249          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
4250          * @type Number
4251          */
4252         minProgressWidth : 250,
4253         /**
4254          * An object containing the default button text strings that can be overriden for localized language support.
4255          * Supported properties are: ok, cancel, yes and no.
4256          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
4257          * @type Object
4258          */
4259         buttonText : {
4260             ok : "OK",
4261             cancel : "Cancel",
4262             yes : "Yes",
4263             no : "No"
4264         }
4265     };
4266 }();
4267
4268 /**
4269  * Shorthand for {@link Roo.MessageBox}
4270  */
4271 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
4272 Roo.Msg = Roo.Msg || Roo.MessageBox;
4273 /*
4274  * - LGPL
4275  *
4276  * navbar
4277  * 
4278  */
4279
4280 /**
4281  * @class Roo.bootstrap.Navbar
4282  * @extends Roo.bootstrap.Component
4283  * Bootstrap Navbar class
4284
4285  * @constructor
4286  * Create a new Navbar
4287  * @param {Object} config The config object
4288  */
4289
4290
4291 Roo.bootstrap.Navbar = function(config){
4292     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
4293     this.addEvents({
4294         // raw events
4295         /**
4296          * @event beforetoggle
4297          * Fire before toggle the menu
4298          * @param {Roo.EventObject} e
4299          */
4300         "beforetoggle" : true
4301     });
4302 };
4303
4304 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
4305     
4306     
4307    
4308     // private
4309     navItems : false,
4310     loadMask : false,
4311     
4312     
4313     getAutoCreate : function(){
4314         
4315         
4316         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
4317         
4318     },
4319     
4320     initEvents :function ()
4321     {
4322         //Roo.log(this.el.select('.navbar-toggle',true));
4323         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
4324         
4325         var mark = {
4326             tag: "div",
4327             cls:"x-dlg-mask"
4328         };
4329         
4330         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
4331         
4332         var size = this.el.getSize();
4333         this.maskEl.setSize(size.width, size.height);
4334         this.maskEl.enableDisplayMode("block");
4335         this.maskEl.hide();
4336         
4337         if(this.loadMask){
4338             this.maskEl.show();
4339         }
4340     },
4341     
4342     
4343     getChildContainer : function()
4344     {
4345         if (this.el && this.el.select('.collapse').getCount()) {
4346             return this.el.select('.collapse',true).first();
4347         }
4348         
4349         return this.el;
4350     },
4351     
4352     mask : function()
4353     {
4354         this.maskEl.show();
4355     },
4356     
4357     unmask : function()
4358     {
4359         this.maskEl.hide();
4360     },
4361     onToggle : function()
4362     {
4363         
4364         if(this.fireEvent('beforetoggle', this) === false){
4365             return;
4366         }
4367         var ce = this.el.select('.navbar-collapse',true).first();
4368       
4369         if (!ce.hasClass('show')) {
4370            this.expand();
4371         } else {
4372             this.collapse();
4373         }
4374         
4375         
4376     
4377     },
4378     /**
4379      * Expand the navbar pulldown 
4380      */
4381     expand : function ()
4382     {
4383        
4384         var ce = this.el.select('.navbar-collapse',true).first();
4385         if (ce.hasClass('collapsing')) {
4386             return;
4387         }
4388         ce.dom.style.height = '';
4389                // show it...
4390         ce.addClass('in'); // old...
4391         ce.removeClass('collapse');
4392         ce.addClass('show');
4393         var h = ce.getHeight();
4394         Roo.log(h);
4395         ce.removeClass('show');
4396         // at this point we should be able to see it..
4397         ce.addClass('collapsing');
4398         
4399         ce.setHeight(0); // resize it ...
4400         ce.on('transitionend', function() {
4401             //Roo.log('done transition');
4402             ce.removeClass('collapsing');
4403             ce.addClass('show');
4404             ce.removeClass('collapse');
4405
4406             ce.dom.style.height = '';
4407         }, this, { single: true} );
4408         ce.setHeight(h);
4409         ce.dom.scrollTop = 0;
4410     },
4411     /**
4412      * Collapse the navbar pulldown 
4413      */
4414     collapse : function()
4415     {
4416          var ce = this.el.select('.navbar-collapse',true).first();
4417        
4418         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
4419             // it's collapsed or collapsing..
4420             return;
4421         }
4422         ce.removeClass('in'); // old...
4423         ce.setHeight(ce.getHeight());
4424         ce.removeClass('show');
4425         ce.addClass('collapsing');
4426         
4427         ce.on('transitionend', function() {
4428             ce.dom.style.height = '';
4429             ce.removeClass('collapsing');
4430             ce.addClass('collapse');
4431         }, this, { single: true} );
4432         ce.setHeight(0);
4433     }
4434     
4435     
4436     
4437 });
4438
4439
4440
4441  
4442
4443  /*
4444  * - LGPL
4445  *
4446  * navbar
4447  * 
4448  */
4449
4450 /**
4451  * @class Roo.bootstrap.NavSimplebar
4452  * @extends Roo.bootstrap.Navbar
4453  * Bootstrap Sidebar class
4454  *
4455  * @cfg {Boolean} inverse is inverted color
4456  * 
4457  * @cfg {String} type (nav | pills | tabs)
4458  * @cfg {Boolean} arrangement stacked | justified
4459  * @cfg {String} align (left | right) alignment
4460  * 
4461  * @cfg {Boolean} main (true|false) main nav bar? default false
4462  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4463  * 
4464  * @cfg {String} tag (header|footer|nav|div) default is nav 
4465
4466  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4467  * 
4468  * 
4469  * @constructor
4470  * Create a new Sidebar
4471  * @param {Object} config The config object
4472  */
4473
4474
4475 Roo.bootstrap.NavSimplebar = function(config){
4476     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4477 };
4478
4479 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4480     
4481     inverse: false,
4482     
4483     type: false,
4484     arrangement: '',
4485     align : false,
4486     
4487     weight : 'light',
4488     
4489     main : false,
4490     
4491     
4492     tag : false,
4493     
4494     
4495     getAutoCreate : function(){
4496         
4497         
4498         var cfg = {
4499             tag : this.tag || 'div',
4500             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
4501         };
4502         if (['light','white'].indexOf(this.weight) > -1) {
4503             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4504         }
4505         cfg.cls += ' bg-' + this.weight;
4506         
4507         if (this.inverse) {
4508             cfg.cls += ' navbar-inverse';
4509             
4510         }
4511         
4512         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4513         
4514         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
4515             return cfg;
4516         }
4517         
4518         
4519     
4520         
4521         cfg.cn = [
4522             {
4523                 cls: 'nav nav-' + this.xtype,
4524                 tag : 'ul'
4525             }
4526         ];
4527         
4528          
4529         this.type = this.type || 'nav';
4530         if (['tabs','pills'].indexOf(this.type) != -1) {
4531             cfg.cn[0].cls += ' nav-' + this.type
4532         
4533         
4534         } else {
4535             if (this.type!=='nav') {
4536                 Roo.log('nav type must be nav/tabs/pills')
4537             }
4538             cfg.cn[0].cls += ' navbar-nav'
4539         }
4540         
4541         
4542         
4543         
4544         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4545             cfg.cn[0].cls += ' nav-' + this.arrangement;
4546         }
4547         
4548         
4549         if (this.align === 'right') {
4550             cfg.cn[0].cls += ' navbar-right';
4551         }
4552         
4553         
4554         
4555         
4556         return cfg;
4557     
4558         
4559     }
4560     
4561     
4562     
4563 });
4564
4565
4566
4567  
4568
4569  
4570        /*
4571  * - LGPL
4572  *
4573  * navbar
4574  * navbar-fixed-top
4575  * navbar-expand-md  fixed-top 
4576  */
4577
4578 /**
4579  * @class Roo.bootstrap.NavHeaderbar
4580  * @extends Roo.bootstrap.NavSimplebar
4581  * Bootstrap Sidebar class
4582  *
4583  * @cfg {String} brand what is brand
4584  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4585  * @cfg {String} brand_href href of the brand
4586  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4587  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4588  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4589  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4590  * 
4591  * @constructor
4592  * Create a new Sidebar
4593  * @param {Object} config The config object
4594  */
4595
4596
4597 Roo.bootstrap.NavHeaderbar = function(config){
4598     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4599       
4600 };
4601
4602 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4603     
4604     position: '',
4605     brand: '',
4606     brand_href: false,
4607     srButton : true,
4608     autohide : false,
4609     desktopCenter : false,
4610    
4611     
4612     getAutoCreate : function(){
4613         
4614         var   cfg = {
4615             tag: this.nav || 'nav',
4616             cls: 'navbar navbar-expand-md',
4617             role: 'navigation',
4618             cn: []
4619         };
4620         
4621         var cn = cfg.cn;
4622         if (this.desktopCenter) {
4623             cn.push({cls : 'container', cn : []});
4624             cn = cn[0].cn;
4625         }
4626         
4627         if(this.srButton){
4628             var btn = {
4629                 tag: 'button',
4630                 type: 'button',
4631                 cls: 'navbar-toggle navbar-toggler',
4632                 'data-toggle': 'collapse',
4633                 cn: [
4634                     {
4635                         tag: 'span',
4636                         cls: 'sr-only',
4637                         html: 'Toggle navigation'
4638                     },
4639                     {
4640                         tag: 'span',
4641                         cls: 'icon-bar navbar-toggler-icon'
4642                     },
4643                     {
4644                         tag: 'span',
4645                         cls: 'icon-bar'
4646                     },
4647                     {
4648                         tag: 'span',
4649                         cls: 'icon-bar'
4650                     }
4651                 ]
4652             };
4653             
4654             cn.push( Roo.bootstrap.version == 4 ? btn : {
4655                 tag: 'div',
4656                 cls: 'navbar-header',
4657                 cn: [
4658                     btn
4659                 ]
4660             });
4661         }
4662         
4663         cn.push({
4664             tag: 'div',
4665             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
4666             cn : []
4667         });
4668         
4669         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4670         
4671         if (['light','white'].indexOf(this.weight) > -1) {
4672             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4673         }
4674         cfg.cls += ' bg-' + this.weight;
4675         
4676         
4677         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4678             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4679             
4680             // tag can override this..
4681             
4682             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4683         }
4684         
4685         if (this.brand !== '') {
4686             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4687             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4688                 tag: 'a',
4689                 href: this.brand_href ? this.brand_href : '#',
4690                 cls: 'navbar-brand',
4691                 cn: [
4692                 this.brand
4693                 ]
4694             });
4695         }
4696         
4697         if(this.main){
4698             cfg.cls += ' main-nav';
4699         }
4700         
4701         
4702         return cfg;
4703
4704         
4705     },
4706     getHeaderChildContainer : function()
4707     {
4708         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4709             return this.el.select('.navbar-header',true).first();
4710         }
4711         
4712         return this.getChildContainer();
4713     },
4714     
4715     getChildContainer : function()
4716     {
4717          
4718         return this.el.select('.roo-navbar-collapse',true).first();
4719          
4720         
4721     },
4722     
4723     initEvents : function()
4724     {
4725         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4726         
4727         if (this.autohide) {
4728             
4729             var prevScroll = 0;
4730             var ft = this.el;
4731             
4732             Roo.get(document).on('scroll',function(e) {
4733                 var ns = Roo.get(document).getScroll().top;
4734                 var os = prevScroll;
4735                 prevScroll = ns;
4736                 
4737                 if(ns > os){
4738                     ft.removeClass('slideDown');
4739                     ft.addClass('slideUp');
4740                     return;
4741                 }
4742                 ft.removeClass('slideUp');
4743                 ft.addClass('slideDown');
4744                  
4745               
4746           },this);
4747         }
4748     }    
4749     
4750 });
4751
4752
4753
4754  
4755
4756  /*
4757  * - LGPL
4758  *
4759  * navbar
4760  * 
4761  */
4762
4763 /**
4764  * @class Roo.bootstrap.NavSidebar
4765  * @extends Roo.bootstrap.Navbar
4766  * Bootstrap Sidebar class
4767  * 
4768  * @constructor
4769  * Create a new Sidebar
4770  * @param {Object} config The config object
4771  */
4772
4773
4774 Roo.bootstrap.NavSidebar = function(config){
4775     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4776 };
4777
4778 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4779     
4780     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4781     
4782     getAutoCreate : function(){
4783         
4784         
4785         return  {
4786             tag: 'div',
4787             cls: 'sidebar sidebar-nav'
4788         };
4789     
4790         
4791     }
4792     
4793     
4794     
4795 });
4796
4797
4798
4799  
4800
4801  /*
4802  * - LGPL
4803  *
4804  * nav group
4805  * 
4806  */
4807
4808 /**
4809  * @class Roo.bootstrap.NavGroup
4810  * @extends Roo.bootstrap.Component
4811  * Bootstrap NavGroup class
4812  * @cfg {String} align (left|right)
4813  * @cfg {Boolean} inverse
4814  * @cfg {String} type (nav|pills|tab) default nav
4815  * @cfg {String} navId - reference Id for navbar.
4816
4817  * 
4818  * @constructor
4819  * Create a new nav group
4820  * @param {Object} config The config object
4821  */
4822
4823 Roo.bootstrap.NavGroup = function(config){
4824     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4825     this.navItems = [];
4826    
4827     Roo.bootstrap.NavGroup.register(this);
4828      this.addEvents({
4829         /**
4830              * @event changed
4831              * Fires when the active item changes
4832              * @param {Roo.bootstrap.NavGroup} this
4833              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4834              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4835          */
4836         'changed': true
4837      });
4838     
4839 };
4840
4841 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4842     
4843     align: '',
4844     inverse: false,
4845     form: false,
4846     type: 'nav',
4847     navId : '',
4848     // private
4849     
4850     navItems : false, 
4851     
4852     getAutoCreate : function()
4853     {
4854         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4855         
4856         cfg = {
4857             tag : 'ul',
4858             cls: 'nav' 
4859         };
4860         if (Roo.bootstrap.version == 4) {
4861             if (['tabs','pills'].indexOf(this.type) != -1) {
4862                 cfg.cls += ' nav-' + this.type; 
4863             } else {
4864                 // trying to remove so header bar can right align top?
4865                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
4866                     // do not use on header bar... 
4867                     cfg.cls += ' navbar-nav';
4868                 }
4869             }
4870             
4871         } else {
4872             if (['tabs','pills'].indexOf(this.type) != -1) {
4873                 cfg.cls += ' nav-' + this.type
4874             } else {
4875                 if (this.type !== 'nav') {
4876                     Roo.log('nav type must be nav/tabs/pills')
4877                 }
4878                 cfg.cls += ' navbar-nav'
4879             }
4880         }
4881         
4882         if (this.parent() && this.parent().sidebar) {
4883             cfg = {
4884                 tag: 'ul',
4885                 cls: 'dashboard-menu sidebar-menu'
4886             };
4887             
4888             return cfg;
4889         }
4890         
4891         if (this.form === true) {
4892             cfg = {
4893                 tag: 'form',
4894                 cls: 'navbar-form form-inline'
4895             };
4896             //nav navbar-right ml-md-auto
4897             if (this.align === 'right') {
4898                 cfg.cls += ' navbar-right ml-md-auto';
4899             } else {
4900                 cfg.cls += ' navbar-left';
4901             }
4902         }
4903         
4904         if (this.align === 'right') {
4905             cfg.cls += ' navbar-right ml-md-auto';
4906         } else {
4907             cfg.cls += ' mr-auto';
4908         }
4909         
4910         if (this.inverse) {
4911             cfg.cls += ' navbar-inverse';
4912             
4913         }
4914         
4915         
4916         return cfg;
4917     },
4918     /**
4919     * sets the active Navigation item
4920     * @param {Roo.bootstrap.NavItem} the new current navitem
4921     */
4922     setActiveItem : function(item)
4923     {
4924         var prev = false;
4925         Roo.each(this.navItems, function(v){
4926             if (v == item) {
4927                 return ;
4928             }
4929             if (v.isActive()) {
4930                 v.setActive(false, true);
4931                 prev = v;
4932                 
4933             }
4934             
4935         });
4936
4937         item.setActive(true, true);
4938         this.fireEvent('changed', this, item, prev);
4939         
4940         
4941     },
4942     /**
4943     * gets the active Navigation item
4944     * @return {Roo.bootstrap.NavItem} the current navitem
4945     */
4946     getActive : function()
4947     {
4948         
4949         var prev = false;
4950         Roo.each(this.navItems, function(v){
4951             
4952             if (v.isActive()) {
4953                 prev = v;
4954                 
4955             }
4956             
4957         });
4958         return prev;
4959     },
4960     
4961     indexOfNav : function()
4962     {
4963         
4964         var prev = false;
4965         Roo.each(this.navItems, function(v,i){
4966             
4967             if (v.isActive()) {
4968                 prev = i;
4969                 
4970             }
4971             
4972         });
4973         return prev;
4974     },
4975     /**
4976     * adds a Navigation item
4977     * @param {Roo.bootstrap.NavItem} the navitem to add
4978     */
4979     addItem : function(cfg)
4980     {
4981         if (this.form && Roo.bootstrap.version == 4) {
4982             cfg.tag = 'div';
4983         }
4984         var cn = new Roo.bootstrap.NavItem(cfg);
4985         this.register(cn);
4986         cn.parentId = this.id;
4987         cn.onRender(this.el, null);
4988         return cn;
4989     },
4990     /**
4991     * register a Navigation item
4992     * @param {Roo.bootstrap.NavItem} the navitem to add
4993     */
4994     register : function(item)
4995     {
4996         this.navItems.push( item);
4997         item.navId = this.navId;
4998     
4999     },
5000     
5001     /**
5002     * clear all the Navigation item
5003     */
5004    
5005     clearAll : function()
5006     {
5007         this.navItems = [];
5008         this.el.dom.innerHTML = '';
5009     },
5010     
5011     getNavItem: function(tabId)
5012     {
5013         var ret = false;
5014         Roo.each(this.navItems, function(e) {
5015             if (e.tabId == tabId) {
5016                ret =  e;
5017                return false;
5018             }
5019             return true;
5020             
5021         });
5022         return ret;
5023     },
5024     
5025     setActiveNext : function()
5026     {
5027         var i = this.indexOfNav(this.getActive());
5028         if (i > this.navItems.length) {
5029             return;
5030         }
5031         this.setActiveItem(this.navItems[i+1]);
5032     },
5033     setActivePrev : function()
5034     {
5035         var i = this.indexOfNav(this.getActive());
5036         if (i  < 1) {
5037             return;
5038         }
5039         this.setActiveItem(this.navItems[i-1]);
5040     },
5041     clearWasActive : function(except) {
5042         Roo.each(this.navItems, function(e) {
5043             if (e.tabId != except.tabId && e.was_active) {
5044                e.was_active = false;
5045                return false;
5046             }
5047             return true;
5048             
5049         });
5050     },
5051     getWasActive : function ()
5052     {
5053         var r = false;
5054         Roo.each(this.navItems, function(e) {
5055             if (e.was_active) {
5056                r = e;
5057                return false;
5058             }
5059             return true;
5060             
5061         });
5062         return r;
5063     }
5064     
5065     
5066 });
5067
5068  
5069 Roo.apply(Roo.bootstrap.NavGroup, {
5070     
5071     groups: {},
5072      /**
5073     * register a Navigation Group
5074     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5075     */
5076     register : function(navgrp)
5077     {
5078         this.groups[navgrp.navId] = navgrp;
5079         
5080     },
5081     /**
5082     * fetch a Navigation Group based on the navigation ID
5083     * @param {string} the navgroup to add
5084     * @returns {Roo.bootstrap.NavGroup} the navgroup 
5085     */
5086     get: function(navId) {
5087         if (typeof(this.groups[navId]) == 'undefined') {
5088             return false;
5089             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
5090         }
5091         return this.groups[navId] ;
5092     }
5093     
5094     
5095     
5096 });
5097
5098  /*
5099  * - LGPL
5100  *
5101  * row
5102  * 
5103  */
5104
5105 /**
5106  * @class Roo.bootstrap.NavItem
5107  * @extends Roo.bootstrap.Component
5108  * Bootstrap Navbar.NavItem class
5109  * @cfg {String} href  link to
5110  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
5111
5112  * @cfg {String} html content of button
5113  * @cfg {String} badge text inside badge
5114  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
5115  * @cfg {String} glyphicon DEPRICATED - use fa
5116  * @cfg {String} icon DEPRICATED - use fa
5117  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
5118  * @cfg {Boolean} active Is item active
5119  * @cfg {Boolean} disabled Is item disabled
5120  
5121  * @cfg {Boolean} preventDefault (true | false) default false
5122  * @cfg {String} tabId the tab that this item activates.
5123  * @cfg {String} tagtype (a|span) render as a href or span?
5124  * @cfg {Boolean} animateRef (true|false) link to element default false  
5125   
5126  * @constructor
5127  * Create a new Navbar Item
5128  * @param {Object} config The config object
5129  */
5130 Roo.bootstrap.NavItem = function(config){
5131     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
5132     this.addEvents({
5133         // raw events
5134         /**
5135          * @event click
5136          * The raw click event for the entire grid.
5137          * @param {Roo.EventObject} e
5138          */
5139         "click" : true,
5140          /**
5141             * @event changed
5142             * Fires when the active item active state changes
5143             * @param {Roo.bootstrap.NavItem} this
5144             * @param {boolean} state the new state
5145              
5146          */
5147         'changed': true,
5148         /**
5149             * @event scrollto
5150             * Fires when scroll to element
5151             * @param {Roo.bootstrap.NavItem} this
5152             * @param {Object} options
5153             * @param {Roo.EventObject} e
5154              
5155          */
5156         'scrollto': true
5157     });
5158    
5159 };
5160
5161 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
5162     
5163     href: false,
5164     html: '',
5165     badge: '',
5166     icon: false,
5167     fa : false,
5168     glyphicon: false,
5169     active: false,
5170     preventDefault : false,
5171     tabId : false,
5172     tagtype : 'a',
5173     tag: 'li',
5174     disabled : false,
5175     animateRef : false,
5176     was_active : false,
5177     button_weight : '',
5178     button_outline : false,
5179     
5180     navLink: false,
5181     
5182     getAutoCreate : function(){
5183          
5184         var cfg = {
5185             tag: this.tag,
5186             cls: 'nav-item'
5187         };
5188         
5189         if (this.active) {
5190             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
5191         }
5192         if (this.disabled) {
5193             cfg.cls += ' disabled';
5194         }
5195         
5196         // BS4 only?
5197         if (this.button_weight.length) {
5198             cfg.tag = this.href ? 'a' : 'button';
5199             cfg.html = this.html || '';
5200             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
5201             if (this.href) {
5202                 cfg.href = this.href;
5203             }
5204             if (this.fa) {
5205                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
5206             }
5207             
5208             // menu .. should add dropdown-menu class - so no need for carat..
5209             
5210             if (this.badge !== '') {
5211                  
5212                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5213             }
5214             return cfg;
5215         }
5216         
5217         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
5218             cfg.cn = [
5219                 {
5220                     tag: this.tagtype,
5221                     href : this.href || "#",
5222                     html: this.html || ''
5223                 }
5224             ];
5225             if (this.tagtype == 'a') {
5226                 cfg.cn[0].cls = 'nav-link';
5227             }
5228             if (this.icon) {
5229                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
5230             }
5231             if (this.fa) {
5232                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
5233             }
5234             if(this.glyphicon) {
5235                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
5236             }
5237             
5238             if (this.menu) {
5239                 
5240                 cfg.cn[0].html += " <span class='caret'></span>";
5241              
5242             }
5243             
5244             if (this.badge !== '') {
5245                  
5246                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
5247             }
5248         }
5249         
5250         
5251         
5252         return cfg;
5253     },
5254     onRender : function(ct, position)
5255     {
5256        // Roo.log("Call onRender: " + this.xtype);
5257         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
5258             this.tag = 'div';
5259         }
5260         
5261         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
5262         this.navLink = this.el.select('.nav-link',true).first();
5263         return ret;
5264     },
5265       
5266     
5267     initEvents: function() 
5268     {
5269         if (typeof (this.menu) != 'undefined') {
5270             this.menu.parentType = this.xtype;
5271             this.menu.triggerEl = this.el;
5272             this.menu = this.addxtype(Roo.apply({}, this.menu));
5273         }
5274         
5275         this.el.select('a',true).on('click', this.onClick, this);
5276         
5277         if(this.tagtype == 'span'){
5278             this.el.select('span',true).on('click', this.onClick, this);
5279         }
5280        
5281         // at this point parent should be available..
5282         this.parent().register(this);
5283     },
5284     
5285     onClick : function(e)
5286     {
5287         if (e.getTarget('.dropdown-menu-item')) {
5288             // did you click on a menu itemm.... - then don't trigger onclick..
5289             return;
5290         }
5291         
5292         if(
5293                 this.preventDefault || 
5294                 this.href == '#' 
5295         ){
5296             Roo.log("NavItem - prevent Default?");
5297             e.preventDefault();
5298         }
5299         
5300         if (this.disabled) {
5301             return;
5302         }
5303         
5304         var tg = Roo.bootstrap.TabGroup.get(this.navId);
5305         if (tg && tg.transition) {
5306             Roo.log("waiting for the transitionend");
5307             return;
5308         }
5309         
5310         
5311         
5312         //Roo.log("fire event clicked");
5313         if(this.fireEvent('click', this, e) === false){
5314             return;
5315         };
5316         
5317         if(this.tagtype == 'span'){
5318             return;
5319         }
5320         
5321         //Roo.log(this.href);
5322         var ael = this.el.select('a',true).first();
5323         //Roo.log(ael);
5324         
5325         if(ael && this.animateRef && this.href.indexOf('#') > -1){
5326             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
5327             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
5328                 return; // ignore... - it's a 'hash' to another page.
5329             }
5330             Roo.log("NavItem - prevent Default?");
5331             e.preventDefault();
5332             this.scrollToElement(e);
5333         }
5334         
5335         
5336         var p =  this.parent();
5337    
5338         if (['tabs','pills'].indexOf(p.type)!==-1) {
5339             if (typeof(p.setActiveItem) !== 'undefined') {
5340                 p.setActiveItem(this);
5341             }
5342         }
5343         
5344         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
5345         if (p.parentType == 'NavHeaderbar' && !this.menu) {
5346             // remove the collapsed menu expand...
5347             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
5348         }
5349     },
5350     
5351     isActive: function () {
5352         return this.active
5353     },
5354     setActive : function(state, fire, is_was_active)
5355     {
5356         if (this.active && !state && this.navId) {
5357             this.was_active = true;
5358             var nv = Roo.bootstrap.NavGroup.get(this.navId);
5359             if (nv) {
5360                 nv.clearWasActive(this);
5361             }
5362             
5363         }
5364         this.active = state;
5365         
5366         if (!state ) {
5367             this.el.removeClass('active');
5368             this.navLink ? this.navLink.removeClass('active') : false;
5369         } else if (!this.el.hasClass('active')) {
5370             
5371             this.el.addClass('active');
5372             if (Roo.bootstrap.version == 4 && this.navLink ) {
5373                 this.navLink.addClass('active');
5374             }
5375             
5376         }
5377         if (fire) {
5378             this.fireEvent('changed', this, state);
5379         }
5380         
5381         // show a panel if it's registered and related..
5382         
5383         if (!this.navId || !this.tabId || !state || is_was_active) {
5384             return;
5385         }
5386         
5387         var tg = Roo.bootstrap.TabGroup.get(this.navId);
5388         if (!tg) {
5389             return;
5390         }
5391         var pan = tg.getPanelByName(this.tabId);
5392         if (!pan) {
5393             return;
5394         }
5395         // if we can not flip to new panel - go back to old nav highlight..
5396         if (false == tg.showPanel(pan)) {
5397             var nv = Roo.bootstrap.NavGroup.get(this.navId);
5398             if (nv) {
5399                 var onav = nv.getWasActive();
5400                 if (onav) {
5401                     onav.setActive(true, false, true);
5402                 }
5403             }
5404             
5405         }
5406         
5407         
5408         
5409     },
5410      // this should not be here...
5411     setDisabled : function(state)
5412     {
5413         this.disabled = state;
5414         if (!state ) {
5415             this.el.removeClass('disabled');
5416         } else if (!this.el.hasClass('disabled')) {
5417             this.el.addClass('disabled');
5418         }
5419         
5420     },
5421     
5422     /**
5423      * Fetch the element to display the tooltip on.
5424      * @return {Roo.Element} defaults to this.el
5425      */
5426     tooltipEl : function()
5427     {
5428         return this.el.select('' + this.tagtype + '', true).first();
5429     },
5430     
5431     scrollToElement : function(e)
5432     {
5433         var c = document.body;
5434         
5435         /*
5436          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
5437          */
5438         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
5439             c = document.documentElement;
5440         }
5441         
5442         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
5443         
5444         if(!target){
5445             return;
5446         }
5447
5448         var o = target.calcOffsetsTo(c);
5449         
5450         var options = {
5451             target : target,
5452             value : o[1]
5453         };
5454         
5455         this.fireEvent('scrollto', this, options, e);
5456         
5457         Roo.get(c).scrollTo('top', options.value, true);
5458         
5459         return;
5460     }
5461 });
5462  
5463
5464  /*
5465  * - LGPL
5466  *
5467  * sidebar item
5468  *
5469  *  li
5470  *    <span> icon </span>
5471  *    <span> text </span>
5472  *    <span>badge </span>
5473  */
5474
5475 /**
5476  * @class Roo.bootstrap.NavSidebarItem
5477  * @extends Roo.bootstrap.NavItem
5478  * Bootstrap Navbar.NavSidebarItem class
5479  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5480  * {Boolean} open is the menu open
5481  * {Boolean} buttonView use button as the tigger el rather that a (default false)
5482  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5483  * {String} buttonSize (sm|md|lg)the extra classes for the button
5484  * {Boolean} showArrow show arrow next to the text (default true)
5485  * @constructor
5486  * Create a new Navbar Button
5487  * @param {Object} config The config object
5488  */
5489 Roo.bootstrap.NavSidebarItem = function(config){
5490     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5491     this.addEvents({
5492         // raw events
5493         /**
5494          * @event click
5495          * The raw click event for the entire grid.
5496          * @param {Roo.EventObject} e
5497          */
5498         "click" : true,
5499          /**
5500             * @event changed
5501             * Fires when the active item active state changes
5502             * @param {Roo.bootstrap.NavSidebarItem} this
5503             * @param {boolean} state the new state
5504              
5505          */
5506         'changed': true
5507     });
5508    
5509 };
5510
5511 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
5512     
5513     badgeWeight : 'default',
5514     
5515     open: false,
5516     
5517     buttonView : false,
5518     
5519     buttonWeight : 'default',
5520     
5521     buttonSize : 'md',
5522     
5523     showArrow : true,
5524     
5525     getAutoCreate : function(){
5526         
5527         
5528         var a = {
5529                 tag: 'a',
5530                 href : this.href || '#',
5531                 cls: '',
5532                 html : '',
5533                 cn : []
5534         };
5535         
5536         if(this.buttonView){
5537             a = {
5538                 tag: 'button',
5539                 href : this.href || '#',
5540                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5541                 html : this.html,
5542                 cn : []
5543             };
5544         }
5545         
5546         var cfg = {
5547             tag: 'li',
5548             cls: '',
5549             cn: [ a ]
5550         };
5551         
5552         if (this.active) {
5553             cfg.cls += ' active';
5554         }
5555         
5556         if (this.disabled) {
5557             cfg.cls += ' disabled';
5558         }
5559         if (this.open) {
5560             cfg.cls += ' open x-open';
5561         }
5562         // left icon..
5563         if (this.glyphicon || this.icon) {
5564             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5565             a.cn.push({ tag : 'i', cls : c }) ;
5566         }
5567         
5568         if(!this.buttonView){
5569             var span = {
5570                 tag: 'span',
5571                 html : this.html || ''
5572             };
5573
5574             a.cn.push(span);
5575             
5576         }
5577         
5578         if (this.badge !== '') {
5579             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5580         }
5581         
5582         if (this.menu) {
5583             
5584             if(this.showArrow){
5585                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5586             }
5587             
5588             a.cls += ' dropdown-toggle treeview' ;
5589         }
5590         
5591         return cfg;
5592     },
5593     
5594     initEvents : function()
5595     { 
5596         if (typeof (this.menu) != 'undefined') {
5597             this.menu.parentType = this.xtype;
5598             this.menu.triggerEl = this.el;
5599             this.menu = this.addxtype(Roo.apply({}, this.menu));
5600         }
5601         
5602         this.el.on('click', this.onClick, this);
5603         
5604         if(this.badge !== ''){
5605             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5606         }
5607         
5608     },
5609     
5610     onClick : function(e)
5611     {
5612         if(this.disabled){
5613             e.preventDefault();
5614             return;
5615         }
5616         
5617         if(this.preventDefault){
5618             e.preventDefault();
5619         }
5620         
5621         this.fireEvent('click', this, e);
5622     },
5623     
5624     disable : function()
5625     {
5626         this.setDisabled(true);
5627     },
5628     
5629     enable : function()
5630     {
5631         this.setDisabled(false);
5632     },
5633     
5634     setDisabled : function(state)
5635     {
5636         if(this.disabled == state){
5637             return;
5638         }
5639         
5640         this.disabled = state;
5641         
5642         if (state) {
5643             this.el.addClass('disabled');
5644             return;
5645         }
5646         
5647         this.el.removeClass('disabled');
5648         
5649         return;
5650     },
5651     
5652     setActive : function(state)
5653     {
5654         if(this.active == state){
5655             return;
5656         }
5657         
5658         this.active = state;
5659         
5660         if (state) {
5661             this.el.addClass('active');
5662             return;
5663         }
5664         
5665         this.el.removeClass('active');
5666         
5667         return;
5668     },
5669     
5670     isActive: function () 
5671     {
5672         return this.active;
5673     },
5674     
5675     setBadge : function(str)
5676     {
5677         if(!this.badgeEl){
5678             return;
5679         }
5680         
5681         this.badgeEl.dom.innerHTML = str;
5682     }
5683     
5684    
5685      
5686  
5687 });
5688  
5689
5690  /*
5691  * - LGPL
5692  *
5693  * row
5694  * 
5695  */
5696
5697 /**
5698  * @class Roo.bootstrap.Row
5699  * @extends Roo.bootstrap.Component
5700  * Bootstrap Row class (contains columns...)
5701  * 
5702  * @constructor
5703  * Create a new Row
5704  * @param {Object} config The config object
5705  */
5706
5707 Roo.bootstrap.Row = function(config){
5708     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5709 };
5710
5711 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5712     
5713     getAutoCreate : function(){
5714        return {
5715             cls: 'row clearfix'
5716        };
5717     }
5718     
5719     
5720 });
5721
5722  
5723
5724  /*
5725  * - LGPL
5726  *
5727  * pagination
5728  * 
5729  */
5730
5731 /**
5732  * @class Roo.bootstrap.Pagination
5733  * @extends Roo.bootstrap.Component
5734  * Bootstrap Pagination class
5735  * @cfg {String} size xs | sm | md | lg
5736  * @cfg {Boolean} inverse false | true
5737  * 
5738  * @constructor
5739  * Create a new Pagination
5740  * @param {Object} config The config object
5741  */
5742
5743 Roo.bootstrap.Pagination = function(config){
5744     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5745 };
5746
5747 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5748     
5749     cls: false,
5750     size: false,
5751     inverse: false,
5752     
5753     getAutoCreate : function(){
5754         var cfg = {
5755             tag: 'ul',
5756                 cls: 'pagination'
5757         };
5758         if (this.inverse) {
5759             cfg.cls += ' inverse';
5760         }
5761         if (this.html) {
5762             cfg.html=this.html;
5763         }
5764         if (this.cls) {
5765             cfg.cls += " " + this.cls;
5766         }
5767         return cfg;
5768     }
5769    
5770 });
5771
5772  
5773
5774  /*
5775  * - LGPL
5776  *
5777  * Pagination item
5778  * 
5779  */
5780
5781
5782 /**
5783  * @class Roo.bootstrap.PaginationItem
5784  * @extends Roo.bootstrap.Component
5785  * Bootstrap PaginationItem class
5786  * @cfg {String} html text
5787  * @cfg {String} href the link
5788  * @cfg {Boolean} preventDefault (true | false) default true
5789  * @cfg {Boolean} active (true | false) default false
5790  * @cfg {Boolean} disabled default false
5791  * 
5792  * 
5793  * @constructor
5794  * Create a new PaginationItem
5795  * @param {Object} config The config object
5796  */
5797
5798
5799 Roo.bootstrap.PaginationItem = function(config){
5800     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5801     this.addEvents({
5802         // raw events
5803         /**
5804          * @event click
5805          * The raw click event for the entire grid.
5806          * @param {Roo.EventObject} e
5807          */
5808         "click" : true
5809     });
5810 };
5811
5812 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5813     
5814     href : false,
5815     html : false,
5816     preventDefault: true,
5817     active : false,
5818     cls : false,
5819     disabled: false,
5820     
5821     getAutoCreate : function(){
5822         var cfg= {
5823             tag: 'li',
5824             cn: [
5825                 {
5826                     tag : 'a',
5827                     href : this.href ? this.href : '#',
5828                     html : this.html ? this.html : ''
5829                 }
5830             ]
5831         };
5832         
5833         if(this.cls){
5834             cfg.cls = this.cls;
5835         }
5836         
5837         if(this.disabled){
5838             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5839         }
5840         
5841         if(this.active){
5842             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5843         }
5844         
5845         return cfg;
5846     },
5847     
5848     initEvents: function() {
5849         
5850         this.el.on('click', this.onClick, this);
5851         
5852     },
5853     onClick : function(e)
5854     {
5855         Roo.log('PaginationItem on click ');
5856         if(this.preventDefault){
5857             e.preventDefault();
5858         }
5859         
5860         if(this.disabled){
5861             return;
5862         }
5863         
5864         this.fireEvent('click', this, e);
5865     }
5866    
5867 });
5868
5869  
5870
5871  /*
5872  * - LGPL
5873  *
5874  * slider
5875  * 
5876  */
5877
5878
5879 /**
5880  * @class Roo.bootstrap.Slider
5881  * @extends Roo.bootstrap.Component
5882  * Bootstrap Slider class
5883  *    
5884  * @constructor
5885  * Create a new Slider
5886  * @param {Object} config The config object
5887  */
5888
5889 Roo.bootstrap.Slider = function(config){
5890     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5891 };
5892
5893 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5894     
5895     getAutoCreate : function(){
5896         
5897         var cfg = {
5898             tag: 'div',
5899             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5900             cn: [
5901                 {
5902                     tag: 'a',
5903                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5904                 }
5905             ]
5906         };
5907         
5908         return cfg;
5909     }
5910    
5911 });
5912
5913  /*
5914  * Based on:
5915  * Ext JS Library 1.1.1
5916  * Copyright(c) 2006-2007, Ext JS, LLC.
5917  *
5918  * Originally Released Under LGPL - original licence link has changed is not relivant.
5919  *
5920  * Fork - LGPL
5921  * <script type="text/javascript">
5922  */
5923  
5924
5925 /**
5926  * @class Roo.grid.ColumnModel
5927  * @extends Roo.util.Observable
5928  * This is the default implementation of a ColumnModel used by the Grid. It defines
5929  * the columns in the grid.
5930  * <br>Usage:<br>
5931  <pre><code>
5932  var colModel = new Roo.grid.ColumnModel([
5933         {header: "Ticker", width: 60, sortable: true, locked: true},
5934         {header: "Company Name", width: 150, sortable: true},
5935         {header: "Market Cap.", width: 100, sortable: true},
5936         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5937         {header: "Employees", width: 100, sortable: true, resizable: false}
5938  ]);
5939  </code></pre>
5940  * <p>
5941  
5942  * The config options listed for this class are options which may appear in each
5943  * individual column definition.
5944  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5945  * @constructor
5946  * @param {Object} config An Array of column config objects. See this class's
5947  * config objects for details.
5948 */
5949 Roo.grid.ColumnModel = function(config){
5950         /**
5951      * The config passed into the constructor
5952      */
5953     this.config = config;
5954     this.lookup = {};
5955
5956     // if no id, create one
5957     // if the column does not have a dataIndex mapping,
5958     // map it to the order it is in the config
5959     for(var i = 0, len = config.length; i < len; i++){
5960         var c = config[i];
5961         if(typeof c.dataIndex == "undefined"){
5962             c.dataIndex = i;
5963         }
5964         if(typeof c.renderer == "string"){
5965             c.renderer = Roo.util.Format[c.renderer];
5966         }
5967         if(typeof c.id == "undefined"){
5968             c.id = Roo.id();
5969         }
5970         if(c.editor && c.editor.xtype){
5971             c.editor  = Roo.factory(c.editor, Roo.grid);
5972         }
5973         if(c.editor && c.editor.isFormField){
5974             c.editor = new Roo.grid.GridEditor(c.editor);
5975         }
5976         this.lookup[c.id] = c;
5977     }
5978
5979     /**
5980      * The width of columns which have no width specified (defaults to 100)
5981      * @type Number
5982      */
5983     this.defaultWidth = 100;
5984
5985     /**
5986      * Default sortable of columns which have no sortable specified (defaults to false)
5987      * @type Boolean
5988      */
5989     this.defaultSortable = false;
5990
5991     this.addEvents({
5992         /**
5993              * @event widthchange
5994              * Fires when the width of a column changes.
5995              * @param {ColumnModel} this
5996              * @param {Number} columnIndex The column index
5997              * @param {Number} newWidth The new width
5998              */
5999             "widthchange": true,
6000         /**
6001              * @event headerchange
6002              * Fires when the text of a header changes.
6003              * @param {ColumnModel} this
6004              * @param {Number} columnIndex The column index
6005              * @param {Number} newText The new header text
6006              */
6007             "headerchange": true,
6008         /**
6009              * @event hiddenchange
6010              * Fires when a column is hidden or "unhidden".
6011              * @param {ColumnModel} this
6012              * @param {Number} columnIndex The column index
6013              * @param {Boolean} hidden true if hidden, false otherwise
6014              */
6015             "hiddenchange": true,
6016             /**
6017          * @event columnmoved
6018          * Fires when a column is moved.
6019          * @param {ColumnModel} this
6020          * @param {Number} oldIndex
6021          * @param {Number} newIndex
6022          */
6023         "columnmoved" : true,
6024         /**
6025          * @event columlockchange
6026          * Fires when a column's locked state is changed
6027          * @param {ColumnModel} this
6028          * @param {Number} colIndex
6029          * @param {Boolean} locked true if locked
6030          */
6031         "columnlockchange" : true
6032     });
6033     Roo.grid.ColumnModel.superclass.constructor.call(this);
6034 };
6035 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6036     /**
6037      * @cfg {String} header The header text to display in the Grid view.
6038      */
6039     /**
6040      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6041      * {@link Roo.data.Record} definition from which to draw the column's value. If not
6042      * specified, the column's index is used as an index into the Record's data Array.
6043      */
6044     /**
6045      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6046      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6047      */
6048     /**
6049      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6050      * Defaults to the value of the {@link #defaultSortable} property.
6051      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6052      */
6053     /**
6054      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
6055      */
6056     /**
6057      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
6058      */
6059     /**
6060      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6061      */
6062     /**
6063      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6064      */
6065     /**
6066      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6067      * given the cell's data value. See {@link #setRenderer}. If not specified, the
6068      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6069      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6070      */
6071        /**
6072      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
6073      */
6074     /**
6075      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
6076      */
6077     /**
6078      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
6079      */
6080     /**
6081      * @cfg {String} cursor (Optional)
6082      */
6083     /**
6084      * @cfg {String} tooltip (Optional)
6085      */
6086     /**
6087      * @cfg {Number} xs (Optional)
6088      */
6089     /**
6090      * @cfg {Number} sm (Optional)
6091      */
6092     /**
6093      * @cfg {Number} md (Optional)
6094      */
6095     /**
6096      * @cfg {Number} lg (Optional)
6097      */
6098     /**
6099      * Returns the id of the column at the specified index.
6100      * @param {Number} index The column index
6101      * @return {String} the id
6102      */
6103     getColumnId : function(index){
6104         return this.config[index].id;
6105     },
6106
6107     /**
6108      * Returns the column for a specified id.
6109      * @param {String} id The column id
6110      * @return {Object} the column
6111      */
6112     getColumnById : function(id){
6113         return this.lookup[id];
6114     },
6115
6116     
6117     /**
6118      * Returns the column for a specified dataIndex.
6119      * @param {String} dataIndex The column dataIndex
6120      * @return {Object|Boolean} the column or false if not found
6121      */
6122     getColumnByDataIndex: function(dataIndex){
6123         var index = this.findColumnIndex(dataIndex);
6124         return index > -1 ? this.config[index] : false;
6125     },
6126     
6127     /**
6128      * Returns the index for a specified column id.
6129      * @param {String} id The column id
6130      * @return {Number} the index, or -1 if not found
6131      */
6132     getIndexById : function(id){
6133         for(var i = 0, len = this.config.length; i < len; i++){
6134             if(this.config[i].id == id){
6135                 return i;
6136             }
6137         }
6138         return -1;
6139     },
6140     
6141     /**
6142      * Returns the index for a specified column dataIndex.
6143      * @param {String} dataIndex The column dataIndex
6144      * @return {Number} the index, or -1 if not found
6145      */
6146     
6147     findColumnIndex : function(dataIndex){
6148         for(var i = 0, len = this.config.length; i < len; i++){
6149             if(this.config[i].dataIndex == dataIndex){
6150                 return i;
6151             }
6152         }
6153         return -1;
6154     },
6155     
6156     
6157     moveColumn : function(oldIndex, newIndex){
6158         var c = this.config[oldIndex];
6159         this.config.splice(oldIndex, 1);
6160         this.config.splice(newIndex, 0, c);
6161         this.dataMap = null;
6162         this.fireEvent("columnmoved", this, oldIndex, newIndex);
6163     },
6164
6165     isLocked : function(colIndex){
6166         return this.config[colIndex].locked === true;
6167     },
6168
6169     setLocked : function(colIndex, value, suppressEvent){
6170         if(this.isLocked(colIndex) == value){
6171             return;
6172         }
6173         this.config[colIndex].locked = value;
6174         if(!suppressEvent){
6175             this.fireEvent("columnlockchange", this, colIndex, value);
6176         }
6177     },
6178
6179     getTotalLockedWidth : function(){
6180         var totalWidth = 0;
6181         for(var i = 0; i < this.config.length; i++){
6182             if(this.isLocked(i) && !this.isHidden(i)){
6183                 this.totalWidth += this.getColumnWidth(i);
6184             }
6185         }
6186         return totalWidth;
6187     },
6188
6189     getLockedCount : function(){
6190         for(var i = 0, len = this.config.length; i < len; i++){
6191             if(!this.isLocked(i)){
6192                 return i;
6193             }
6194         }
6195         
6196         return this.config.length;
6197     },
6198
6199     /**
6200      * Returns the number of columns.
6201      * @return {Number}
6202      */
6203     getColumnCount : function(visibleOnly){
6204         if(visibleOnly === true){
6205             var c = 0;
6206             for(var i = 0, len = this.config.length; i < len; i++){
6207                 if(!this.isHidden(i)){
6208                     c++;
6209                 }
6210             }
6211             return c;
6212         }
6213         return this.config.length;
6214     },
6215
6216     /**
6217      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
6218      * @param {Function} fn
6219      * @param {Object} scope (optional)
6220      * @return {Array} result
6221      */
6222     getColumnsBy : function(fn, scope){
6223         var r = [];
6224         for(var i = 0, len = this.config.length; i < len; i++){
6225             var c = this.config[i];
6226             if(fn.call(scope||this, c, i) === true){
6227                 r[r.length] = c;
6228             }
6229         }
6230         return r;
6231     },
6232
6233     /**
6234      * Returns true if the specified column is sortable.
6235      * @param {Number} col The column index
6236      * @return {Boolean}
6237      */
6238     isSortable : function(col){
6239         if(typeof this.config[col].sortable == "undefined"){
6240             return this.defaultSortable;
6241         }
6242         return this.config[col].sortable;
6243     },
6244
6245     /**
6246      * Returns the rendering (formatting) function defined for the column.
6247      * @param {Number} col The column index.
6248      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
6249      */
6250     getRenderer : function(col){
6251         if(!this.config[col].renderer){
6252             return Roo.grid.ColumnModel.defaultRenderer;
6253         }
6254         return this.config[col].renderer;
6255     },
6256
6257     /**
6258      * Sets the rendering (formatting) function for a column.
6259      * @param {Number} col The column index
6260      * @param {Function} fn The function to use to process the cell's raw data
6261      * to return HTML markup for the grid view. The render function is called with
6262      * the following parameters:<ul>
6263      * <li>Data value.</li>
6264      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
6265      * <li>css A CSS style string to apply to the table cell.</li>
6266      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
6267      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
6268      * <li>Row index</li>
6269      * <li>Column index</li>
6270      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
6271      */
6272     setRenderer : function(col, fn){
6273         this.config[col].renderer = fn;
6274     },
6275
6276     /**
6277      * Returns the width for the specified column.
6278      * @param {Number} col The column index
6279      * @return {Number}
6280      */
6281     getColumnWidth : function(col){
6282         return this.config[col].width * 1 || this.defaultWidth;
6283     },
6284
6285     /**
6286      * Sets the width for a column.
6287      * @param {Number} col The column index
6288      * @param {Number} width The new width
6289      */
6290     setColumnWidth : function(col, width, suppressEvent){
6291         this.config[col].width = width;
6292         this.totalWidth = null;
6293         if(!suppressEvent){
6294              this.fireEvent("widthchange", this, col, width);
6295         }
6296     },
6297
6298     /**
6299      * Returns the total width of all columns.
6300      * @param {Boolean} includeHidden True to include hidden column widths
6301      * @return {Number}
6302      */
6303     getTotalWidth : function(includeHidden){
6304         if(!this.totalWidth){
6305             this.totalWidth = 0;
6306             for(var i = 0, len = this.config.length; i < len; i++){
6307                 if(includeHidden || !this.isHidden(i)){
6308                     this.totalWidth += this.getColumnWidth(i);
6309                 }
6310             }
6311         }
6312         return this.totalWidth;
6313     },
6314
6315     /**
6316      * Returns the header for the specified column.
6317      * @param {Number} col The column index
6318      * @return {String}
6319      */
6320     getColumnHeader : function(col){
6321         return this.config[col].header;
6322     },
6323
6324     /**
6325      * Sets the header for a column.
6326      * @param {Number} col The column index
6327      * @param {String} header The new header
6328      */
6329     setColumnHeader : function(col, header){
6330         this.config[col].header = header;
6331         this.fireEvent("headerchange", this, col, header);
6332     },
6333
6334     /**
6335      * Returns the tooltip for the specified column.
6336      * @param {Number} col The column index
6337      * @return {String}
6338      */
6339     getColumnTooltip : function(col){
6340             return this.config[col].tooltip;
6341     },
6342     /**
6343      * Sets the tooltip for a column.
6344      * @param {Number} col The column index
6345      * @param {String} tooltip The new tooltip
6346      */
6347     setColumnTooltip : function(col, tooltip){
6348             this.config[col].tooltip = tooltip;
6349     },
6350
6351     /**
6352      * Returns the dataIndex for the specified column.
6353      * @param {Number} col The column index
6354      * @return {Number}
6355      */
6356     getDataIndex : function(col){
6357         return this.config[col].dataIndex;
6358     },
6359
6360     /**
6361      * Sets the dataIndex for a column.
6362      * @param {Number} col The column index
6363      * @param {Number} dataIndex The new dataIndex
6364      */
6365     setDataIndex : function(col, dataIndex){
6366         this.config[col].dataIndex = dataIndex;
6367     },
6368
6369     
6370     
6371     /**
6372      * Returns true if the cell is editable.
6373      * @param {Number} colIndex The column index
6374      * @param {Number} rowIndex The row index - this is nto actually used..?
6375      * @return {Boolean}
6376      */
6377     isCellEditable : function(colIndex, rowIndex){
6378         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6379     },
6380
6381     /**
6382      * Returns the editor defined for the cell/column.
6383      * return false or null to disable editing.
6384      * @param {Number} colIndex The column index
6385      * @param {Number} rowIndex The row index
6386      * @return {Object}
6387      */
6388     getCellEditor : function(colIndex, rowIndex){
6389         return this.config[colIndex].editor;
6390     },
6391
6392     /**
6393      * Sets if a column is editable.
6394      * @param {Number} col The column index
6395      * @param {Boolean} editable True if the column is editable
6396      */
6397     setEditable : function(col, editable){
6398         this.config[col].editable = editable;
6399     },
6400
6401
6402     /**
6403      * Returns true if the column is hidden.
6404      * @param {Number} colIndex The column index
6405      * @return {Boolean}
6406      */
6407     isHidden : function(colIndex){
6408         return this.config[colIndex].hidden;
6409     },
6410
6411
6412     /**
6413      * Returns true if the column width cannot be changed
6414      */
6415     isFixed : function(colIndex){
6416         return this.config[colIndex].fixed;
6417     },
6418
6419     /**
6420      * Returns true if the column can be resized
6421      * @return {Boolean}
6422      */
6423     isResizable : function(colIndex){
6424         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6425     },
6426     /**
6427      * Sets if a column is hidden.
6428      * @param {Number} colIndex The column index
6429      * @param {Boolean} hidden True if the column is hidden
6430      */
6431     setHidden : function(colIndex, hidden){
6432         this.config[colIndex].hidden = hidden;
6433         this.totalWidth = null;
6434         this.fireEvent("hiddenchange", this, colIndex, hidden);
6435     },
6436
6437     /**
6438      * Sets the editor for a column.
6439      * @param {Number} col The column index
6440      * @param {Object} editor The editor object
6441      */
6442     setEditor : function(col, editor){
6443         this.config[col].editor = editor;
6444     }
6445 });
6446
6447 Roo.grid.ColumnModel.defaultRenderer = function(value)
6448 {
6449     if(typeof value == "object") {
6450         return value;
6451     }
6452         if(typeof value == "string" && value.length < 1){
6453             return "&#160;";
6454         }
6455     
6456         return String.format("{0}", value);
6457 };
6458
6459 // Alias for backwards compatibility
6460 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6461 /*
6462  * Based on:
6463  * Ext JS Library 1.1.1
6464  * Copyright(c) 2006-2007, Ext JS, LLC.
6465  *
6466  * Originally Released Under LGPL - original licence link has changed is not relivant.
6467  *
6468  * Fork - LGPL
6469  * <script type="text/javascript">
6470  */
6471  
6472 /**
6473  * @class Roo.LoadMask
6474  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6475  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6476  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6477  * element's UpdateManager load indicator and will be destroyed after the initial load.
6478  * @constructor
6479  * Create a new LoadMask
6480  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6481  * @param {Object} config The config object
6482  */
6483 Roo.LoadMask = function(el, config){
6484     this.el = Roo.get(el);
6485     Roo.apply(this, config);
6486     if(this.store){
6487         this.store.on('beforeload', this.onBeforeLoad, this);
6488         this.store.on('load', this.onLoad, this);
6489         this.store.on('loadexception', this.onLoadException, this);
6490         this.removeMask = false;
6491     }else{
6492         var um = this.el.getUpdateManager();
6493         um.showLoadIndicator = false; // disable the default indicator
6494         um.on('beforeupdate', this.onBeforeLoad, this);
6495         um.on('update', this.onLoad, this);
6496         um.on('failure', this.onLoad, this);
6497         this.removeMask = true;
6498     }
6499 };
6500
6501 Roo.LoadMask.prototype = {
6502     /**
6503      * @cfg {Boolean} removeMask
6504      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6505      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6506      */
6507     /**
6508      * @cfg {String} msg
6509      * The text to display in a centered loading message box (defaults to 'Loading...')
6510      */
6511     msg : 'Loading...',
6512     /**
6513      * @cfg {String} msgCls
6514      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6515      */
6516     msgCls : 'x-mask-loading',
6517
6518     /**
6519      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6520      * @type Boolean
6521      */
6522     disabled: false,
6523
6524     /**
6525      * Disables the mask to prevent it from being displayed
6526      */
6527     disable : function(){
6528        this.disabled = true;
6529     },
6530
6531     /**
6532      * Enables the mask so that it can be displayed
6533      */
6534     enable : function(){
6535         this.disabled = false;
6536     },
6537     
6538     onLoadException : function()
6539     {
6540         Roo.log(arguments);
6541         
6542         if (typeof(arguments[3]) != 'undefined') {
6543             Roo.MessageBox.alert("Error loading",arguments[3]);
6544         } 
6545         /*
6546         try {
6547             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6548                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6549             }   
6550         } catch(e) {
6551             
6552         }
6553         */
6554     
6555         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6556     },
6557     // private
6558     onLoad : function()
6559     {
6560         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6561     },
6562
6563     // private
6564     onBeforeLoad : function(){
6565         if(!this.disabled){
6566             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6567         }
6568     },
6569
6570     // private
6571     destroy : function(){
6572         if(this.store){
6573             this.store.un('beforeload', this.onBeforeLoad, this);
6574             this.store.un('load', this.onLoad, this);
6575             this.store.un('loadexception', this.onLoadException, this);
6576         }else{
6577             var um = this.el.getUpdateManager();
6578             um.un('beforeupdate', this.onBeforeLoad, this);
6579             um.un('update', this.onLoad, this);
6580             um.un('failure', this.onLoad, this);
6581         }
6582     }
6583 };/*
6584  * - LGPL
6585  *
6586  * table
6587  * 
6588  */
6589
6590 /**
6591  * @class Roo.bootstrap.Table
6592  * @extends Roo.bootstrap.Component
6593  * Bootstrap Table class
6594  * @cfg {String} cls table class
6595  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6596  * @cfg {String} bgcolor Specifies the background color for a table
6597  * @cfg {Number} border Specifies whether the table cells should have borders or not
6598  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6599  * @cfg {Number} cellspacing Specifies the space between cells
6600  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6601  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6602  * @cfg {String} sortable Specifies that the table should be sortable
6603  * @cfg {String} summary Specifies a summary of the content of a table
6604  * @cfg {Number} width Specifies the width of a table
6605  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6606  * 
6607  * @cfg {boolean} striped Should the rows be alternative striped
6608  * @cfg {boolean} bordered Add borders to the table
6609  * @cfg {boolean} hover Add hover highlighting
6610  * @cfg {boolean} condensed Format condensed
6611  * @cfg {boolean} responsive Format condensed
6612  * @cfg {Boolean} loadMask (true|false) default false
6613  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6614  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6615  * @cfg {Boolean} rowSelection (true|false) default false
6616  * @cfg {Boolean} cellSelection (true|false) default false
6617  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6618  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6619  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6620  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6621  
6622  * 
6623  * @constructor
6624  * Create a new Table
6625  * @param {Object} config The config object
6626  */
6627
6628 Roo.bootstrap.Table = function(config){
6629     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6630     
6631   
6632     
6633     // BC...
6634     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6635     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6636     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6637     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6638     
6639     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6640     if (this.sm) {
6641         this.sm.grid = this;
6642         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6643         this.sm = this.selModel;
6644         this.sm.xmodule = this.xmodule || false;
6645     }
6646     
6647     if (this.cm && typeof(this.cm.config) == 'undefined') {
6648         this.colModel = new Roo.grid.ColumnModel(this.cm);
6649         this.cm = this.colModel;
6650         this.cm.xmodule = this.xmodule || false;
6651     }
6652     if (this.store) {
6653         this.store= Roo.factory(this.store, Roo.data);
6654         this.ds = this.store;
6655         this.ds.xmodule = this.xmodule || false;
6656          
6657     }
6658     if (this.footer && this.store) {
6659         this.footer.dataSource = this.ds;
6660         this.footer = Roo.factory(this.footer);
6661     }
6662     
6663     /** @private */
6664     this.addEvents({
6665         /**
6666          * @event cellclick
6667          * Fires when a cell is clicked
6668          * @param {Roo.bootstrap.Table} this
6669          * @param {Roo.Element} el
6670          * @param {Number} rowIndex
6671          * @param {Number} columnIndex
6672          * @param {Roo.EventObject} e
6673          */
6674         "cellclick" : true,
6675         /**
6676          * @event celldblclick
6677          * Fires when a cell is double clicked
6678          * @param {Roo.bootstrap.Table} this
6679          * @param {Roo.Element} el
6680          * @param {Number} rowIndex
6681          * @param {Number} columnIndex
6682          * @param {Roo.EventObject} e
6683          */
6684         "celldblclick" : true,
6685         /**
6686          * @event rowclick
6687          * Fires when a row is clicked
6688          * @param {Roo.bootstrap.Table} this
6689          * @param {Roo.Element} el
6690          * @param {Number} rowIndex
6691          * @param {Roo.EventObject} e
6692          */
6693         "rowclick" : true,
6694         /**
6695          * @event rowdblclick
6696          * Fires when a row is double clicked
6697          * @param {Roo.bootstrap.Table} this
6698          * @param {Roo.Element} el
6699          * @param {Number} rowIndex
6700          * @param {Roo.EventObject} e
6701          */
6702         "rowdblclick" : true,
6703         /**
6704          * @event mouseover
6705          * Fires when a mouseover occur
6706          * @param {Roo.bootstrap.Table} this
6707          * @param {Roo.Element} el
6708          * @param {Number} rowIndex
6709          * @param {Number} columnIndex
6710          * @param {Roo.EventObject} e
6711          */
6712         "mouseover" : true,
6713         /**
6714          * @event mouseout
6715          * Fires when a mouseout occur
6716          * @param {Roo.bootstrap.Table} this
6717          * @param {Roo.Element} el
6718          * @param {Number} rowIndex
6719          * @param {Number} columnIndex
6720          * @param {Roo.EventObject} e
6721          */
6722         "mouseout" : true,
6723         /**
6724          * @event rowclass
6725          * Fires when a row is rendered, so you can change add a style to it.
6726          * @param {Roo.bootstrap.Table} this
6727          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6728          */
6729         'rowclass' : true,
6730           /**
6731          * @event rowsrendered
6732          * Fires when all the  rows have been rendered
6733          * @param {Roo.bootstrap.Table} this
6734          */
6735         'rowsrendered' : true,
6736         /**
6737          * @event contextmenu
6738          * The raw contextmenu event for the entire grid.
6739          * @param {Roo.EventObject} e
6740          */
6741         "contextmenu" : true,
6742         /**
6743          * @event rowcontextmenu
6744          * Fires when a row is right clicked
6745          * @param {Roo.bootstrap.Table} this
6746          * @param {Number} rowIndex
6747          * @param {Roo.EventObject} e
6748          */
6749         "rowcontextmenu" : true,
6750         /**
6751          * @event cellcontextmenu
6752          * Fires when a cell is right clicked
6753          * @param {Roo.bootstrap.Table} this
6754          * @param {Number} rowIndex
6755          * @param {Number} cellIndex
6756          * @param {Roo.EventObject} e
6757          */
6758          "cellcontextmenu" : true,
6759          /**
6760          * @event headercontextmenu
6761          * Fires when a header is right clicked
6762          * @param {Roo.bootstrap.Table} this
6763          * @param {Number} columnIndex
6764          * @param {Roo.EventObject} e
6765          */
6766         "headercontextmenu" : true
6767     });
6768 };
6769
6770 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6771     
6772     cls: false,
6773     align: false,
6774     bgcolor: false,
6775     border: false,
6776     cellpadding: false,
6777     cellspacing: false,
6778     frame: false,
6779     rules: false,
6780     sortable: false,
6781     summary: false,
6782     width: false,
6783     striped : false,
6784     scrollBody : false,
6785     bordered: false,
6786     hover:  false,
6787     condensed : false,
6788     responsive : false,
6789     sm : false,
6790     cm : false,
6791     store : false,
6792     loadMask : false,
6793     footerShow : true,
6794     headerShow : true,
6795   
6796     rowSelection : false,
6797     cellSelection : false,
6798     layout : false,
6799     
6800     // Roo.Element - the tbody
6801     mainBody: false,
6802     // Roo.Element - thead element
6803     mainHead: false,
6804     
6805     container: false, // used by gridpanel...
6806     
6807     lazyLoad : false,
6808     
6809     CSS : Roo.util.CSS,
6810     
6811     auto_hide_footer : false,
6812     
6813     getAutoCreate : function()
6814     {
6815         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6816         
6817         cfg = {
6818             tag: 'table',
6819             cls : 'table',
6820             cn : []
6821         };
6822         if (this.scrollBody) {
6823             cfg.cls += ' table-body-fixed';
6824         }    
6825         if (this.striped) {
6826             cfg.cls += ' table-striped';
6827         }
6828         
6829         if (this.hover) {
6830             cfg.cls += ' table-hover';
6831         }
6832         if (this.bordered) {
6833             cfg.cls += ' table-bordered';
6834         }
6835         if (this.condensed) {
6836             cfg.cls += ' table-condensed';
6837         }
6838         if (this.responsive) {
6839             cfg.cls += ' table-responsive';
6840         }
6841         
6842         if (this.cls) {
6843             cfg.cls+=  ' ' +this.cls;
6844         }
6845         
6846         // this lot should be simplifed...
6847         var _t = this;
6848         var cp = [
6849             'align',
6850             'bgcolor',
6851             'border',
6852             'cellpadding',
6853             'cellspacing',
6854             'frame',
6855             'rules',
6856             'sortable',
6857             'summary',
6858             'width'
6859         ].forEach(function(k) {
6860             if (_t[k]) {
6861                 cfg[k] = _t[k];
6862             }
6863         });
6864         
6865         
6866         if (this.layout) {
6867             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6868         }
6869         
6870         if(this.store || this.cm){
6871             if(this.headerShow){
6872                 cfg.cn.push(this.renderHeader());
6873             }
6874             
6875             cfg.cn.push(this.renderBody());
6876             
6877             if(this.footerShow){
6878                 cfg.cn.push(this.renderFooter());
6879             }
6880             // where does this come from?
6881             //cfg.cls+=  ' TableGrid';
6882         }
6883         
6884         return { cn : [ cfg ] };
6885     },
6886     
6887     initEvents : function()
6888     {   
6889         if(!this.store || !this.cm){
6890             return;
6891         }
6892         if (this.selModel) {
6893             this.selModel.initEvents();
6894         }
6895         
6896         
6897         //Roo.log('initEvents with ds!!!!');
6898         
6899         this.mainBody = this.el.select('tbody', true).first();
6900         this.mainHead = this.el.select('thead', true).first();
6901         this.mainFoot = this.el.select('tfoot', true).first();
6902         
6903         
6904         
6905         var _this = this;
6906         
6907         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6908             e.on('click', _this.sort, _this);
6909         });
6910         
6911         this.mainBody.on("click", this.onClick, this);
6912         this.mainBody.on("dblclick", this.onDblClick, this);
6913         
6914         // why is this done????? = it breaks dialogs??
6915         //this.parent().el.setStyle('position', 'relative');
6916         
6917         
6918         if (this.footer) {
6919             this.footer.parentId = this.id;
6920             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6921             
6922             if(this.lazyLoad){
6923                 this.el.select('tfoot tr td').first().addClass('hide');
6924             }
6925         } 
6926         
6927         if(this.loadMask) {
6928             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6929         }
6930         
6931         this.store.on('load', this.onLoad, this);
6932         this.store.on('beforeload', this.onBeforeLoad, this);
6933         this.store.on('update', this.onUpdate, this);
6934         this.store.on('add', this.onAdd, this);
6935         this.store.on("clear", this.clear, this);
6936         
6937         this.el.on("contextmenu", this.onContextMenu, this);
6938         
6939         this.mainBody.on('scroll', this.onBodyScroll, this);
6940         
6941         this.cm.on("headerchange", this.onHeaderChange, this);
6942         
6943         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6944         
6945     },
6946     
6947     onContextMenu : function(e, t)
6948     {
6949         this.processEvent("contextmenu", e);
6950     },
6951     
6952     processEvent : function(name, e)
6953     {
6954         if (name != 'touchstart' ) {
6955             this.fireEvent(name, e);    
6956         }
6957         
6958         var t = e.getTarget();
6959         
6960         var cell = Roo.get(t);
6961         
6962         if(!cell){
6963             return;
6964         }
6965         
6966         if(cell.findParent('tfoot', false, true)){
6967             return;
6968         }
6969         
6970         if(cell.findParent('thead', false, true)){
6971             
6972             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6973                 cell = Roo.get(t).findParent('th', false, true);
6974                 if (!cell) {
6975                     Roo.log("failed to find th in thead?");
6976                     Roo.log(e.getTarget());
6977                     return;
6978                 }
6979             }
6980             
6981             var cellIndex = cell.dom.cellIndex;
6982             
6983             var ename = name == 'touchstart' ? 'click' : name;
6984             this.fireEvent("header" + ename, this, cellIndex, e);
6985             
6986             return;
6987         }
6988         
6989         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6990             cell = Roo.get(t).findParent('td', false, true);
6991             if (!cell) {
6992                 Roo.log("failed to find th in tbody?");
6993                 Roo.log(e.getTarget());
6994                 return;
6995             }
6996         }
6997         
6998         var row = cell.findParent('tr', false, true);
6999         var cellIndex = cell.dom.cellIndex;
7000         var rowIndex = row.dom.rowIndex - 1;
7001         
7002         if(row !== false){
7003             
7004             this.fireEvent("row" + name, this, rowIndex, e);
7005             
7006             if(cell !== false){
7007             
7008                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7009             }
7010         }
7011         
7012     },
7013     
7014     onMouseover : function(e, el)
7015     {
7016         var cell = Roo.get(el);
7017         
7018         if(!cell){
7019             return;
7020         }
7021         
7022         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7023             cell = cell.findParent('td', false, true);
7024         }
7025         
7026         var row = cell.findParent('tr', false, true);
7027         var cellIndex = cell.dom.cellIndex;
7028         var rowIndex = row.dom.rowIndex - 1; // start from 0
7029         
7030         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7031         
7032     },
7033     
7034     onMouseout : function(e, el)
7035     {
7036         var cell = Roo.get(el);
7037         
7038         if(!cell){
7039             return;
7040         }
7041         
7042         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7043             cell = cell.findParent('td', false, true);
7044         }
7045         
7046         var row = cell.findParent('tr', false, true);
7047         var cellIndex = cell.dom.cellIndex;
7048         var rowIndex = row.dom.rowIndex - 1; // start from 0
7049         
7050         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7051         
7052     },
7053     
7054     onClick : function(e, el)
7055     {
7056         var cell = Roo.get(el);
7057         
7058         if(!cell || (!this.cellSelection && !this.rowSelection)){
7059             return;
7060         }
7061         
7062         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7063             cell = cell.findParent('td', false, true);
7064         }
7065         
7066         if(!cell || typeof(cell) == 'undefined'){
7067             return;
7068         }
7069         
7070         var row = cell.findParent('tr', false, true);
7071         
7072         if(!row || typeof(row) == 'undefined'){
7073             return;
7074         }
7075         
7076         var cellIndex = cell.dom.cellIndex;
7077         var rowIndex = this.getRowIndex(row);
7078         
7079         // why??? - should these not be based on SelectionModel?
7080         if(this.cellSelection){
7081             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7082         }
7083         
7084         if(this.rowSelection){
7085             this.fireEvent('rowclick', this, row, rowIndex, e);
7086         }
7087         
7088         
7089     },
7090         
7091     onDblClick : function(e,el)
7092     {
7093         var cell = Roo.get(el);
7094         
7095         if(!cell || (!this.cellSelection && !this.rowSelection)){
7096             return;
7097         }
7098         
7099         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7100             cell = cell.findParent('td', false, true);
7101         }
7102         
7103         if(!cell || typeof(cell) == 'undefined'){
7104             return;
7105         }
7106         
7107         var row = cell.findParent('tr', false, true);
7108         
7109         if(!row || typeof(row) == 'undefined'){
7110             return;
7111         }
7112         
7113         var cellIndex = cell.dom.cellIndex;
7114         var rowIndex = this.getRowIndex(row);
7115         
7116         if(this.cellSelection){
7117             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7118         }
7119         
7120         if(this.rowSelection){
7121             this.fireEvent('rowdblclick', this, row, rowIndex, e);
7122         }
7123     },
7124     
7125     sort : function(e,el)
7126     {
7127         var col = Roo.get(el);
7128         
7129         if(!col.hasClass('sortable')){
7130             return;
7131         }
7132         
7133         var sort = col.attr('sort');
7134         var dir = 'ASC';
7135         
7136         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7137             dir = 'DESC';
7138         }
7139         
7140         this.store.sortInfo = {field : sort, direction : dir};
7141         
7142         if (this.footer) {
7143             Roo.log("calling footer first");
7144             this.footer.onClick('first');
7145         } else {
7146         
7147             this.store.load({ params : { start : 0 } });
7148         }
7149     },
7150     
7151     renderHeader : function()
7152     {
7153         var header = {
7154             tag: 'thead',
7155             cn : []
7156         };
7157         
7158         var cm = this.cm;
7159         this.totalWidth = 0;
7160         
7161         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7162             
7163             var config = cm.config[i];
7164             
7165             var c = {
7166                 tag: 'th',
7167                 cls : 'x-hcol-' + i,
7168                 style : '',
7169                 html: cm.getColumnHeader(i)
7170             };
7171             
7172             var hh = '';
7173             
7174             if(typeof(config.sortable) != 'undefined' && config.sortable){
7175                 c.cls = 'sortable';
7176                 c.html = '<i class="glyphicon"></i>' + c.html;
7177             }
7178             
7179             // could use BS4 hidden-..-down 
7180             
7181             if(typeof(config.lgHeader) != 'undefined'){
7182                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
7183             }
7184             
7185             if(typeof(config.mdHeader) != 'undefined'){
7186                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
7187             }
7188             
7189             if(typeof(config.smHeader) != 'undefined'){
7190                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
7191             }
7192             
7193             if(typeof(config.xsHeader) != 'undefined'){
7194                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
7195             }
7196             
7197             if(hh.length){
7198                 c.html = hh;
7199             }
7200             
7201             if(typeof(config.tooltip) != 'undefined'){
7202                 c.tooltip = config.tooltip;
7203             }
7204             
7205             if(typeof(config.colspan) != 'undefined'){
7206                 c.colspan = config.colspan;
7207             }
7208             
7209             if(typeof(config.hidden) != 'undefined' && config.hidden){
7210                 c.style += ' display:none;';
7211             }
7212             
7213             if(typeof(config.dataIndex) != 'undefined'){
7214                 c.sort = config.dataIndex;
7215             }
7216             
7217            
7218             
7219             if(typeof(config.align) != 'undefined' && config.align.length){
7220                 c.style += ' text-align:' + config.align + ';';
7221             }
7222             
7223             if(typeof(config.width) != 'undefined'){
7224                 c.style += ' width:' + config.width + 'px;';
7225                 this.totalWidth += config.width;
7226             } else {
7227                 this.totalWidth += 100; // assume minimum of 100 per column?
7228             }
7229             
7230             if(typeof(config.cls) != 'undefined'){
7231                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
7232             }
7233             
7234             ['xs','sm','md','lg'].map(function(size){
7235                 
7236                 if(typeof(config[size]) == 'undefined'){
7237                     return;
7238                 }
7239                  
7240                 if (!config[size]) { // 0 = hidden
7241                     // BS 4 '0' is treated as hide that column and below.
7242                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
7243                     return;
7244                 }
7245                 
7246                 c.cls += ' col-' + size + '-' + config[size] + (
7247                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7248                 );
7249                 
7250                 
7251             });
7252             
7253             header.cn.push(c)
7254         }
7255         
7256         return header;
7257     },
7258     
7259     renderBody : function()
7260     {
7261         var body = {
7262             tag: 'tbody',
7263             cn : [
7264                 {
7265                     tag: 'tr',
7266                     cn : [
7267                         {
7268                             tag : 'td',
7269                             colspan :  this.cm.getColumnCount()
7270                         }
7271                     ]
7272                 }
7273             ]
7274         };
7275         
7276         return body;
7277     },
7278     
7279     renderFooter : function()
7280     {
7281         var footer = {
7282             tag: 'tfoot',
7283             cn : [
7284                 {
7285                     tag: 'tr',
7286                     cn : [
7287                         {
7288                             tag : 'td',
7289                             colspan :  this.cm.getColumnCount()
7290                         }
7291                     ]
7292                 }
7293             ]
7294         };
7295         
7296         return footer;
7297     },
7298     
7299     
7300     
7301     onLoad : function()
7302     {
7303 //        Roo.log('ds onload');
7304         this.clear();
7305         
7306         var _this = this;
7307         var cm = this.cm;
7308         var ds = this.store;
7309         
7310         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7311             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
7312             if (_this.store.sortInfo) {
7313                     
7314                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
7315                     e.select('i', true).addClass(['glyphicon-arrow-up']);
7316                 }
7317                 
7318                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
7319                     e.select('i', true).addClass(['glyphicon-arrow-down']);
7320                 }
7321             }
7322         });
7323         
7324         var tbody =  this.mainBody;
7325               
7326         if(ds.getCount() > 0){
7327             ds.data.each(function(d,rowIndex){
7328                 var row =  this.renderRow(cm, ds, rowIndex);
7329                 
7330                 tbody.createChild(row);
7331                 
7332                 var _this = this;
7333                 
7334                 if(row.cellObjects.length){
7335                     Roo.each(row.cellObjects, function(r){
7336                         _this.renderCellObject(r);
7337                     })
7338                 }
7339                 
7340             }, this);
7341         }
7342         
7343         var tfoot = this.el.select('tfoot', true).first();
7344         
7345         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
7346             
7347             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
7348             
7349             var total = this.ds.getTotalCount();
7350             
7351             if(this.footer.pageSize < total){
7352                 this.mainFoot.show();
7353             }
7354         }
7355         
7356         Roo.each(this.el.select('tbody td', true).elements, function(e){
7357             e.on('mouseover', _this.onMouseover, _this);
7358         });
7359         
7360         Roo.each(this.el.select('tbody td', true).elements, function(e){
7361             e.on('mouseout', _this.onMouseout, _this);
7362         });
7363         this.fireEvent('rowsrendered', this);
7364         
7365         this.autoSize();
7366     },
7367     
7368     
7369     onUpdate : function(ds,record)
7370     {
7371         this.refreshRow(record);
7372         this.autoSize();
7373     },
7374     
7375     onRemove : function(ds, record, index, isUpdate){
7376         if(isUpdate !== true){
7377             this.fireEvent("beforerowremoved", this, index, record);
7378         }
7379         var bt = this.mainBody.dom;
7380         
7381         var rows = this.el.select('tbody > tr', true).elements;
7382         
7383         if(typeof(rows[index]) != 'undefined'){
7384             bt.removeChild(rows[index].dom);
7385         }
7386         
7387 //        if(bt.rows[index]){
7388 //            bt.removeChild(bt.rows[index]);
7389 //        }
7390         
7391         if(isUpdate !== true){
7392             //this.stripeRows(index);
7393             //this.syncRowHeights(index, index);
7394             //this.layout();
7395             this.fireEvent("rowremoved", this, index, record);
7396         }
7397     },
7398     
7399     onAdd : function(ds, records, rowIndex)
7400     {
7401         //Roo.log('on Add called');
7402         // - note this does not handle multiple adding very well..
7403         var bt = this.mainBody.dom;
7404         for (var i =0 ; i < records.length;i++) {
7405             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7406             //Roo.log(records[i]);
7407             //Roo.log(this.store.getAt(rowIndex+i));
7408             this.insertRow(this.store, rowIndex + i, false);
7409             return;
7410         }
7411         
7412     },
7413     
7414     
7415     refreshRow : function(record){
7416         var ds = this.store, index;
7417         if(typeof record == 'number'){
7418             index = record;
7419             record = ds.getAt(index);
7420         }else{
7421             index = ds.indexOf(record);
7422         }
7423         this.insertRow(ds, index, true);
7424         this.autoSize();
7425         this.onRemove(ds, record, index+1, true);
7426         this.autoSize();
7427         //this.syncRowHeights(index, index);
7428         //this.layout();
7429         this.fireEvent("rowupdated", this, index, record);
7430     },
7431     
7432     insertRow : function(dm, rowIndex, isUpdate){
7433         
7434         if(!isUpdate){
7435             this.fireEvent("beforerowsinserted", this, rowIndex);
7436         }
7437             //var s = this.getScrollState();
7438         var row = this.renderRow(this.cm, this.store, rowIndex);
7439         // insert before rowIndex..
7440         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7441         
7442         var _this = this;
7443                 
7444         if(row.cellObjects.length){
7445             Roo.each(row.cellObjects, function(r){
7446                 _this.renderCellObject(r);
7447             })
7448         }
7449             
7450         if(!isUpdate){
7451             this.fireEvent("rowsinserted", this, rowIndex);
7452             //this.syncRowHeights(firstRow, lastRow);
7453             //this.stripeRows(firstRow);
7454             //this.layout();
7455         }
7456         
7457     },
7458     
7459     
7460     getRowDom : function(rowIndex)
7461     {
7462         var rows = this.el.select('tbody > tr', true).elements;
7463         
7464         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7465         
7466     },
7467     // returns the object tree for a tr..
7468   
7469     
7470     renderRow : function(cm, ds, rowIndex) 
7471     {
7472         var d = ds.getAt(rowIndex);
7473         
7474         var row = {
7475             tag : 'tr',
7476             cls : 'x-row-' + rowIndex,
7477             cn : []
7478         };
7479             
7480         var cellObjects = [];
7481         
7482         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7483             var config = cm.config[i];
7484             
7485             var renderer = cm.getRenderer(i);
7486             var value = '';
7487             var id = false;
7488             
7489             if(typeof(renderer) !== 'undefined'){
7490                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7491             }
7492             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7493             // and are rendered into the cells after the row is rendered - using the id for the element.
7494             
7495             if(typeof(value) === 'object'){
7496                 id = Roo.id();
7497                 cellObjects.push({
7498                     container : id,
7499                     cfg : value 
7500                 })
7501             }
7502             
7503             var rowcfg = {
7504                 record: d,
7505                 rowIndex : rowIndex,
7506                 colIndex : i,
7507                 rowClass : ''
7508             };
7509
7510             this.fireEvent('rowclass', this, rowcfg);
7511             
7512             var td = {
7513                 tag: 'td',
7514                 cls : rowcfg.rowClass + ' x-col-' + i,
7515                 style: '',
7516                 html: (typeof(value) === 'object') ? '' : value
7517             };
7518             
7519             if (id) {
7520                 td.id = id;
7521             }
7522             
7523             if(typeof(config.colspan) != 'undefined'){
7524                 td.colspan = config.colspan;
7525             }
7526             
7527             if(typeof(config.hidden) != 'undefined' && config.hidden){
7528                 td.style += ' display:none;';
7529             }
7530             
7531             if(typeof(config.align) != 'undefined' && config.align.length){
7532                 td.style += ' text-align:' + config.align + ';';
7533             }
7534             if(typeof(config.valign) != 'undefined' && config.valign.length){
7535                 td.style += ' vertical-align:' + config.valign + ';';
7536             }
7537             
7538             if(typeof(config.width) != 'undefined'){
7539                 td.style += ' width:' +  config.width + 'px;';
7540             }
7541             
7542             if(typeof(config.cursor) != 'undefined'){
7543                 td.style += ' cursor:' +  config.cursor + ';';
7544             }
7545             
7546             if(typeof(config.cls) != 'undefined'){
7547                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7548             }
7549             
7550             ['xs','sm','md','lg'].map(function(size){
7551                 
7552                 if(typeof(config[size]) == 'undefined'){
7553                     return;
7554                 }
7555                 
7556                 
7557                   
7558                 if (!config[size]) { // 0 = hidden
7559                     // BS 4 '0' is treated as hide that column and below.
7560                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
7561                     return;
7562                 }
7563                 
7564                 td.cls += ' col-' + size + '-' + config[size] + (
7565                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
7566                 );
7567                  
7568
7569             });
7570             
7571             row.cn.push(td);
7572            
7573         }
7574         
7575         row.cellObjects = cellObjects;
7576         
7577         return row;
7578           
7579     },
7580     
7581     
7582     
7583     onBeforeLoad : function()
7584     {
7585         
7586     },
7587      /**
7588      * Remove all rows
7589      */
7590     clear : function()
7591     {
7592         this.el.select('tbody', true).first().dom.innerHTML = '';
7593     },
7594     /**
7595      * Show or hide a row.
7596      * @param {Number} rowIndex to show or hide
7597      * @param {Boolean} state hide
7598      */
7599     setRowVisibility : function(rowIndex, state)
7600     {
7601         var bt = this.mainBody.dom;
7602         
7603         var rows = this.el.select('tbody > tr', true).elements;
7604         
7605         if(typeof(rows[rowIndex]) == 'undefined'){
7606             return;
7607         }
7608         rows[rowIndex].dom.style.display = state ? '' : 'none';
7609     },
7610     
7611     
7612     getSelectionModel : function(){
7613         if(!this.selModel){
7614             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7615         }
7616         return this.selModel;
7617     },
7618     /*
7619      * Render the Roo.bootstrap object from renderder
7620      */
7621     renderCellObject : function(r)
7622     {
7623         var _this = this;
7624         
7625         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7626         
7627         var t = r.cfg.render(r.container);
7628         
7629         if(r.cfg.cn){
7630             Roo.each(r.cfg.cn, function(c){
7631                 var child = {
7632                     container: t.getChildContainer(),
7633                     cfg: c
7634                 };
7635                 _this.renderCellObject(child);
7636             })
7637         }
7638     },
7639     
7640     getRowIndex : function(row)
7641     {
7642         var rowIndex = -1;
7643         
7644         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7645             if(el != row){
7646                 return;
7647             }
7648             
7649             rowIndex = index;
7650         });
7651         
7652         return rowIndex;
7653     },
7654      /**
7655      * Returns the grid's underlying element = used by panel.Grid
7656      * @return {Element} The element
7657      */
7658     getGridEl : function(){
7659         return this.el;
7660     },
7661      /**
7662      * Forces a resize - used by panel.Grid
7663      * @return {Element} The element
7664      */
7665     autoSize : function()
7666     {
7667         //var ctr = Roo.get(this.container.dom.parentElement);
7668         var ctr = Roo.get(this.el.dom);
7669         
7670         var thd = this.getGridEl().select('thead',true).first();
7671         var tbd = this.getGridEl().select('tbody', true).first();
7672         var tfd = this.getGridEl().select('tfoot', true).first();
7673         
7674         var cw = ctr.getWidth();
7675         
7676         if (tbd) {
7677             
7678             tbd.setWidth(ctr.getWidth());
7679             // if the body has a max height - and then scrolls - we should perhaps set up the height here
7680             // this needs fixing for various usage - currently only hydra job advers I think..
7681             //tdb.setHeight(
7682             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7683             //); 
7684             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7685             cw -= barsize;
7686         }
7687         cw = Math.max(cw, this.totalWidth);
7688         this.getGridEl().select('tr',true).setWidth(cw);
7689         // resize 'expandable coloumn?
7690         
7691         return; // we doe not have a view in this design..
7692         
7693     },
7694     onBodyScroll: function()
7695     {
7696         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7697         if(this.mainHead){
7698             this.mainHead.setStyle({
7699                 'position' : 'relative',
7700                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7701             });
7702         }
7703         
7704         if(this.lazyLoad){
7705             
7706             var scrollHeight = this.mainBody.dom.scrollHeight;
7707             
7708             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7709             
7710             var height = this.mainBody.getHeight();
7711             
7712             if(scrollHeight - height == scrollTop) {
7713                 
7714                 var total = this.ds.getTotalCount();
7715                 
7716                 if(this.footer.cursor + this.footer.pageSize < total){
7717                     
7718                     this.footer.ds.load({
7719                         params : {
7720                             start : this.footer.cursor + this.footer.pageSize,
7721                             limit : this.footer.pageSize
7722                         },
7723                         add : true
7724                     });
7725                 }
7726             }
7727             
7728         }
7729     },
7730     
7731     onHeaderChange : function()
7732     {
7733         var header = this.renderHeader();
7734         var table = this.el.select('table', true).first();
7735         
7736         this.mainHead.remove();
7737         this.mainHead = table.createChild(header, this.mainBody, false);
7738     },
7739     
7740     onHiddenChange : function(colModel, colIndex, hidden)
7741     {
7742         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7743         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7744         
7745         this.CSS.updateRule(thSelector, "display", "");
7746         this.CSS.updateRule(tdSelector, "display", "");
7747         
7748         if(hidden){
7749             this.CSS.updateRule(thSelector, "display", "none");
7750             this.CSS.updateRule(tdSelector, "display", "none");
7751         }
7752         
7753         this.onHeaderChange();
7754         this.onLoad();
7755     },
7756     
7757     setColumnWidth: function(col_index, width)
7758     {
7759         // width = "md-2 xs-2..."
7760         if(!this.colModel.config[col_index]) {
7761             return;
7762         }
7763         
7764         var w = width.split(" ");
7765         
7766         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7767         
7768         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7769         
7770         
7771         for(var j = 0; j < w.length; j++) {
7772             
7773             if(!w[j]) {
7774                 continue;
7775             }
7776             
7777             var size_cls = w[j].split("-");
7778             
7779             if(!Number.isInteger(size_cls[1] * 1)) {
7780                 continue;
7781             }
7782             
7783             if(!this.colModel.config[col_index][size_cls[0]]) {
7784                 continue;
7785             }
7786             
7787             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7788                 continue;
7789             }
7790             
7791             h_row[0].classList.replace(
7792                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7793                 "col-"+size_cls[0]+"-"+size_cls[1]
7794             );
7795             
7796             for(var i = 0; i < rows.length; i++) {
7797                 
7798                 var size_cls = w[j].split("-");
7799                 
7800                 if(!Number.isInteger(size_cls[1] * 1)) {
7801                     continue;
7802                 }
7803                 
7804                 if(!this.colModel.config[col_index][size_cls[0]]) {
7805                     continue;
7806                 }
7807                 
7808                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7809                     continue;
7810                 }
7811                 
7812                 rows[i].classList.replace(
7813                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7814                     "col-"+size_cls[0]+"-"+size_cls[1]
7815                 );
7816             }
7817             
7818             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7819         }
7820     }
7821 });
7822
7823  
7824
7825  /*
7826  * - LGPL
7827  *
7828  * table cell
7829  * 
7830  */
7831
7832 /**
7833  * @class Roo.bootstrap.TableCell
7834  * @extends Roo.bootstrap.Component
7835  * Bootstrap TableCell class
7836  * @cfg {String} html cell contain text
7837  * @cfg {String} cls cell class
7838  * @cfg {String} tag cell tag (td|th) default td
7839  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7840  * @cfg {String} align Aligns the content in a cell
7841  * @cfg {String} axis Categorizes cells
7842  * @cfg {String} bgcolor Specifies the background color of a cell
7843  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7844  * @cfg {Number} colspan Specifies the number of columns a cell should span
7845  * @cfg {String} headers Specifies one or more header cells a cell is related to
7846  * @cfg {Number} height Sets the height of a cell
7847  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7848  * @cfg {Number} rowspan Sets the number of rows a cell should span
7849  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7850  * @cfg {String} valign Vertical aligns the content in a cell
7851  * @cfg {Number} width Specifies the width of a cell
7852  * 
7853  * @constructor
7854  * Create a new TableCell
7855  * @param {Object} config The config object
7856  */
7857
7858 Roo.bootstrap.TableCell = function(config){
7859     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7860 };
7861
7862 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7863     
7864     html: false,
7865     cls: false,
7866     tag: false,
7867     abbr: false,
7868     align: false,
7869     axis: false,
7870     bgcolor: false,
7871     charoff: false,
7872     colspan: false,
7873     headers: false,
7874     height: false,
7875     nowrap: false,
7876     rowspan: false,
7877     scope: false,
7878     valign: false,
7879     width: false,
7880     
7881     
7882     getAutoCreate : function(){
7883         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7884         
7885         cfg = {
7886             tag: 'td'
7887         };
7888         
7889         if(this.tag){
7890             cfg.tag = this.tag;
7891         }
7892         
7893         if (this.html) {
7894             cfg.html=this.html
7895         }
7896         if (this.cls) {
7897             cfg.cls=this.cls
7898         }
7899         if (this.abbr) {
7900             cfg.abbr=this.abbr
7901         }
7902         if (this.align) {
7903             cfg.align=this.align
7904         }
7905         if (this.axis) {
7906             cfg.axis=this.axis
7907         }
7908         if (this.bgcolor) {
7909             cfg.bgcolor=this.bgcolor
7910         }
7911         if (this.charoff) {
7912             cfg.charoff=this.charoff
7913         }
7914         if (this.colspan) {
7915             cfg.colspan=this.colspan
7916         }
7917         if (this.headers) {
7918             cfg.headers=this.headers
7919         }
7920         if (this.height) {
7921             cfg.height=this.height
7922         }
7923         if (this.nowrap) {
7924             cfg.nowrap=this.nowrap
7925         }
7926         if (this.rowspan) {
7927             cfg.rowspan=this.rowspan
7928         }
7929         if (this.scope) {
7930             cfg.scope=this.scope
7931         }
7932         if (this.valign) {
7933             cfg.valign=this.valign
7934         }
7935         if (this.width) {
7936             cfg.width=this.width
7937         }
7938         
7939         
7940         return cfg;
7941     }
7942    
7943 });
7944
7945  
7946
7947  /*
7948  * - LGPL
7949  *
7950  * table row
7951  * 
7952  */
7953
7954 /**
7955  * @class Roo.bootstrap.TableRow
7956  * @extends Roo.bootstrap.Component
7957  * Bootstrap TableRow class
7958  * @cfg {String} cls row class
7959  * @cfg {String} align Aligns the content in a table row
7960  * @cfg {String} bgcolor Specifies a background color for a table row
7961  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7962  * @cfg {String} valign Vertical aligns the content in a table row
7963  * 
7964  * @constructor
7965  * Create a new TableRow
7966  * @param {Object} config The config object
7967  */
7968
7969 Roo.bootstrap.TableRow = function(config){
7970     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7971 };
7972
7973 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7974     
7975     cls: false,
7976     align: false,
7977     bgcolor: false,
7978     charoff: false,
7979     valign: false,
7980     
7981     getAutoCreate : function(){
7982         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7983         
7984         cfg = {
7985             tag: 'tr'
7986         };
7987             
7988         if(this.cls){
7989             cfg.cls = this.cls;
7990         }
7991         if(this.align){
7992             cfg.align = this.align;
7993         }
7994         if(this.bgcolor){
7995             cfg.bgcolor = this.bgcolor;
7996         }
7997         if(this.charoff){
7998             cfg.charoff = this.charoff;
7999         }
8000         if(this.valign){
8001             cfg.valign = this.valign;
8002         }
8003         
8004         return cfg;
8005     }
8006    
8007 });
8008
8009  
8010
8011  /*
8012  * - LGPL
8013  *
8014  * table body
8015  * 
8016  */
8017
8018 /**
8019  * @class Roo.bootstrap.TableBody
8020  * @extends Roo.bootstrap.Component
8021  * Bootstrap TableBody class
8022  * @cfg {String} cls element class
8023  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8024  * @cfg {String} align Aligns the content inside the element
8025  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8026  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8027  * 
8028  * @constructor
8029  * Create a new TableBody
8030  * @param {Object} config The config object
8031  */
8032
8033 Roo.bootstrap.TableBody = function(config){
8034     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8035 };
8036
8037 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
8038     
8039     cls: false,
8040     tag: false,
8041     align: false,
8042     charoff: false,
8043     valign: false,
8044     
8045     getAutoCreate : function(){
8046         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8047         
8048         cfg = {
8049             tag: 'tbody'
8050         };
8051             
8052         if (this.cls) {
8053             cfg.cls=this.cls
8054         }
8055         if(this.tag){
8056             cfg.tag = this.tag;
8057         }
8058         
8059         if(this.align){
8060             cfg.align = this.align;
8061         }
8062         if(this.charoff){
8063             cfg.charoff = this.charoff;
8064         }
8065         if(this.valign){
8066             cfg.valign = this.valign;
8067         }
8068         
8069         return cfg;
8070     }
8071     
8072     
8073 //    initEvents : function()
8074 //    {
8075 //        
8076 //        if(!this.store){
8077 //            return;
8078 //        }
8079 //        
8080 //        this.store = Roo.factory(this.store, Roo.data);
8081 //        this.store.on('load', this.onLoad, this);
8082 //        
8083 //        this.store.load();
8084 //        
8085 //    },
8086 //    
8087 //    onLoad: function () 
8088 //    {   
8089 //        this.fireEvent('load', this);
8090 //    }
8091 //    
8092 //   
8093 });
8094
8095  
8096
8097  /*
8098  * Based on:
8099  * Ext JS Library 1.1.1
8100  * Copyright(c) 2006-2007, Ext JS, LLC.
8101  *
8102  * Originally Released Under LGPL - original licence link has changed is not relivant.
8103  *
8104  * Fork - LGPL
8105  * <script type="text/javascript">
8106  */
8107
8108 // as we use this in bootstrap.
8109 Roo.namespace('Roo.form');
8110  /**
8111  * @class Roo.form.Action
8112  * Internal Class used to handle form actions
8113  * @constructor
8114  * @param {Roo.form.BasicForm} el The form element or its id
8115  * @param {Object} config Configuration options
8116  */
8117
8118  
8119  
8120 // define the action interface
8121 Roo.form.Action = function(form, options){
8122     this.form = form;
8123     this.options = options || {};
8124 };
8125 /**
8126  * Client Validation Failed
8127  * @const 
8128  */
8129 Roo.form.Action.CLIENT_INVALID = 'client';
8130 /**
8131  * Server Validation Failed
8132  * @const 
8133  */
8134 Roo.form.Action.SERVER_INVALID = 'server';
8135  /**
8136  * Connect to Server Failed
8137  * @const 
8138  */
8139 Roo.form.Action.CONNECT_FAILURE = 'connect';
8140 /**
8141  * Reading Data from Server Failed
8142  * @const 
8143  */
8144 Roo.form.Action.LOAD_FAILURE = 'load';
8145
8146 Roo.form.Action.prototype = {
8147     type : 'default',
8148     failureType : undefined,
8149     response : undefined,
8150     result : undefined,
8151
8152     // interface method
8153     run : function(options){
8154
8155     },
8156
8157     // interface method
8158     success : function(response){
8159
8160     },
8161
8162     // interface method
8163     handleResponse : function(response){
8164
8165     },
8166
8167     // default connection failure
8168     failure : function(response){
8169         
8170         this.response = response;
8171         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8172         this.form.afterAction(this, false);
8173     },
8174
8175     processResponse : function(response){
8176         this.response = response;
8177         if(!response.responseText){
8178             return true;
8179         }
8180         this.result = this.handleResponse(response);
8181         return this.result;
8182     },
8183
8184     // utility functions used internally
8185     getUrl : function(appendParams){
8186         var url = this.options.url || this.form.url || this.form.el.dom.action;
8187         if(appendParams){
8188             var p = this.getParams();
8189             if(p){
8190                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
8191             }
8192         }
8193         return url;
8194     },
8195
8196     getMethod : function(){
8197         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
8198     },
8199
8200     getParams : function(){
8201         var bp = this.form.baseParams;
8202         var p = this.options.params;
8203         if(p){
8204             if(typeof p == "object"){
8205                 p = Roo.urlEncode(Roo.applyIf(p, bp));
8206             }else if(typeof p == 'string' && bp){
8207                 p += '&' + Roo.urlEncode(bp);
8208             }
8209         }else if(bp){
8210             p = Roo.urlEncode(bp);
8211         }
8212         return p;
8213     },
8214
8215     createCallback : function(){
8216         return {
8217             success: this.success,
8218             failure: this.failure,
8219             scope: this,
8220             timeout: (this.form.timeout*1000),
8221             upload: this.form.fileUpload ? this.success : undefined
8222         };
8223     }
8224 };
8225
8226 Roo.form.Action.Submit = function(form, options){
8227     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
8228 };
8229
8230 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
8231     type : 'submit',
8232
8233     haveProgress : false,
8234     uploadComplete : false,
8235     
8236     // uploadProgress indicator.
8237     uploadProgress : function()
8238     {
8239         if (!this.form.progressUrl) {
8240             return;
8241         }
8242         
8243         if (!this.haveProgress) {
8244             Roo.MessageBox.progress("Uploading", "Uploading");
8245         }
8246         if (this.uploadComplete) {
8247            Roo.MessageBox.hide();
8248            return;
8249         }
8250         
8251         this.haveProgress = true;
8252    
8253         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
8254         
8255         var c = new Roo.data.Connection();
8256         c.request({
8257             url : this.form.progressUrl,
8258             params: {
8259                 id : uid
8260             },
8261             method: 'GET',
8262             success : function(req){
8263                //console.log(data);
8264                 var rdata = false;
8265                 var edata;
8266                 try  {
8267                    rdata = Roo.decode(req.responseText)
8268                 } catch (e) {
8269                     Roo.log("Invalid data from server..");
8270                     Roo.log(edata);
8271                     return;
8272                 }
8273                 if (!rdata || !rdata.success) {
8274                     Roo.log(rdata);
8275                     Roo.MessageBox.alert(Roo.encode(rdata));
8276                     return;
8277                 }
8278                 var data = rdata.data;
8279                 
8280                 if (this.uploadComplete) {
8281                    Roo.MessageBox.hide();
8282                    return;
8283                 }
8284                    
8285                 if (data){
8286                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
8287                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
8288                     );
8289                 }
8290                 this.uploadProgress.defer(2000,this);
8291             },
8292        
8293             failure: function(data) {
8294                 Roo.log('progress url failed ');
8295                 Roo.log(data);
8296             },
8297             scope : this
8298         });
8299            
8300     },
8301     
8302     
8303     run : function()
8304     {
8305         // run get Values on the form, so it syncs any secondary forms.
8306         this.form.getValues();
8307         
8308         var o = this.options;
8309         var method = this.getMethod();
8310         var isPost = method == 'POST';
8311         if(o.clientValidation === false || this.form.isValid()){
8312             
8313             if (this.form.progressUrl) {
8314                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
8315                     (new Date() * 1) + '' + Math.random());
8316                     
8317             } 
8318             
8319             
8320             Roo.Ajax.request(Roo.apply(this.createCallback(), {
8321                 form:this.form.el.dom,
8322                 url:this.getUrl(!isPost),
8323                 method: method,
8324                 params:isPost ? this.getParams() : null,
8325                 isUpload: this.form.fileUpload,
8326                 formData : this.form.formData
8327             }));
8328             
8329             this.uploadProgress();
8330
8331         }else if (o.clientValidation !== false){ // client validation failed
8332             this.failureType = Roo.form.Action.CLIENT_INVALID;
8333             this.form.afterAction(this, false);
8334         }
8335     },
8336
8337     success : function(response)
8338     {
8339         this.uploadComplete= true;
8340         if (this.haveProgress) {
8341             Roo.MessageBox.hide();
8342         }
8343         
8344         
8345         var result = this.processResponse(response);
8346         if(result === true || result.success){
8347             this.form.afterAction(this, true);
8348             return;
8349         }
8350         if(result.errors){
8351             this.form.markInvalid(result.errors);
8352             this.failureType = Roo.form.Action.SERVER_INVALID;
8353         }
8354         this.form.afterAction(this, false);
8355     },
8356     failure : function(response)
8357     {
8358         this.uploadComplete= true;
8359         if (this.haveProgress) {
8360             Roo.MessageBox.hide();
8361         }
8362         
8363         this.response = response;
8364         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8365         this.form.afterAction(this, false);
8366     },
8367     
8368     handleResponse : function(response){
8369         if(this.form.errorReader){
8370             var rs = this.form.errorReader.read(response);
8371             var errors = [];
8372             if(rs.records){
8373                 for(var i = 0, len = rs.records.length; i < len; i++) {
8374                     var r = rs.records[i];
8375                     errors[i] = r.data;
8376                 }
8377             }
8378             if(errors.length < 1){
8379                 errors = null;
8380             }
8381             return {
8382                 success : rs.success,
8383                 errors : errors
8384             };
8385         }
8386         var ret = false;
8387         try {
8388             ret = Roo.decode(response.responseText);
8389         } catch (e) {
8390             ret = {
8391                 success: false,
8392                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8393                 errors : []
8394             };
8395         }
8396         return ret;
8397         
8398     }
8399 });
8400
8401
8402 Roo.form.Action.Load = function(form, options){
8403     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8404     this.reader = this.form.reader;
8405 };
8406
8407 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8408     type : 'load',
8409
8410     run : function(){
8411         
8412         Roo.Ajax.request(Roo.apply(
8413                 this.createCallback(), {
8414                     method:this.getMethod(),
8415                     url:this.getUrl(false),
8416                     params:this.getParams()
8417         }));
8418     },
8419
8420     success : function(response){
8421         
8422         var result = this.processResponse(response);
8423         if(result === true || !result.success || !result.data){
8424             this.failureType = Roo.form.Action.LOAD_FAILURE;
8425             this.form.afterAction(this, false);
8426             return;
8427         }
8428         this.form.clearInvalid();
8429         this.form.setValues(result.data);
8430         this.form.afterAction(this, true);
8431     },
8432
8433     handleResponse : function(response){
8434         if(this.form.reader){
8435             var rs = this.form.reader.read(response);
8436             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8437             return {
8438                 success : rs.success,
8439                 data : data
8440             };
8441         }
8442         return Roo.decode(response.responseText);
8443     }
8444 });
8445
8446 Roo.form.Action.ACTION_TYPES = {
8447     'load' : Roo.form.Action.Load,
8448     'submit' : Roo.form.Action.Submit
8449 };/*
8450  * - LGPL
8451  *
8452  * form
8453  *
8454  */
8455
8456 /**
8457  * @class Roo.bootstrap.Form
8458  * @extends Roo.bootstrap.Component
8459  * Bootstrap Form class
8460  * @cfg {String} method  GET | POST (default POST)
8461  * @cfg {String} labelAlign top | left (default top)
8462  * @cfg {String} align left  | right - for navbars
8463  * @cfg {Boolean} loadMask load mask when submit (default true)
8464
8465  *
8466  * @constructor
8467  * Create a new Form
8468  * @param {Object} config The config object
8469  */
8470
8471
8472 Roo.bootstrap.Form = function(config){
8473     
8474     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8475     
8476     Roo.bootstrap.Form.popover.apply();
8477     
8478     this.addEvents({
8479         /**
8480          * @event clientvalidation
8481          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8482          * @param {Form} this
8483          * @param {Boolean} valid true if the form has passed client-side validation
8484          */
8485         clientvalidation: true,
8486         /**
8487          * @event beforeaction
8488          * Fires before any action is performed. Return false to cancel the action.
8489          * @param {Form} this
8490          * @param {Action} action The action to be performed
8491          */
8492         beforeaction: true,
8493         /**
8494          * @event actionfailed
8495          * Fires when an action fails.
8496          * @param {Form} this
8497          * @param {Action} action The action that failed
8498          */
8499         actionfailed : true,
8500         /**
8501          * @event actioncomplete
8502          * Fires when an action is completed.
8503          * @param {Form} this
8504          * @param {Action} action The action that completed
8505          */
8506         actioncomplete : true
8507     });
8508 };
8509
8510 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8511
8512      /**
8513      * @cfg {String} method
8514      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8515      */
8516     method : 'POST',
8517     /**
8518      * @cfg {String} url
8519      * The URL to use for form actions if one isn't supplied in the action options.
8520      */
8521     /**
8522      * @cfg {Boolean} fileUpload
8523      * Set to true if this form is a file upload.
8524      */
8525
8526     /**
8527      * @cfg {Object} baseParams
8528      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8529      */
8530
8531     /**
8532      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8533      */
8534     timeout: 30,
8535     /**
8536      * @cfg {Sting} align (left|right) for navbar forms
8537      */
8538     align : 'left',
8539
8540     // private
8541     activeAction : null,
8542
8543     /**
8544      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8545      * element by passing it or its id or mask the form itself by passing in true.
8546      * @type Mixed
8547      */
8548     waitMsgTarget : false,
8549
8550     loadMask : true,
8551     
8552     /**
8553      * @cfg {Boolean} errorMask (true|false) default false
8554      */
8555     errorMask : false,
8556     
8557     /**
8558      * @cfg {Number} maskOffset Default 100
8559      */
8560     maskOffset : 100,
8561     
8562     /**
8563      * @cfg {Boolean} maskBody
8564      */
8565     maskBody : false,
8566
8567     getAutoCreate : function(){
8568
8569         var cfg = {
8570             tag: 'form',
8571             method : this.method || 'POST',
8572             id : this.id || Roo.id(),
8573             cls : ''
8574         };
8575         if (this.parent().xtype.match(/^Nav/)) {
8576             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8577
8578         }
8579
8580         if (this.labelAlign == 'left' ) {
8581             cfg.cls += ' form-horizontal';
8582         }
8583
8584
8585         return cfg;
8586     },
8587     initEvents : function()
8588     {
8589         this.el.on('submit', this.onSubmit, this);
8590         // this was added as random key presses on the form where triggering form submit.
8591         this.el.on('keypress', function(e) {
8592             if (e.getCharCode() != 13) {
8593                 return true;
8594             }
8595             // we might need to allow it for textareas.. and some other items.
8596             // check e.getTarget().
8597
8598             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8599                 return true;
8600             }
8601
8602             Roo.log("keypress blocked");
8603
8604             e.preventDefault();
8605             return false;
8606         });
8607         
8608     },
8609     // private
8610     onSubmit : function(e){
8611         e.stopEvent();
8612     },
8613
8614      /**
8615      * Returns true if client-side validation on the form is successful.
8616      * @return Boolean
8617      */
8618     isValid : function(){
8619         var items = this.getItems();
8620         var valid = true;
8621         var target = false;
8622         
8623         items.each(function(f){
8624             
8625             if(f.validate()){
8626                 return;
8627             }
8628             
8629             Roo.log('invalid field: ' + f.name);
8630             
8631             valid = false;
8632
8633             if(!target && f.el.isVisible(true)){
8634                 target = f;
8635             }
8636            
8637         });
8638         
8639         if(this.errorMask && !valid){
8640             Roo.bootstrap.Form.popover.mask(this, target);
8641         }
8642         
8643         return valid;
8644     },
8645     
8646     /**
8647      * Returns true if any fields in this form have changed since their original load.
8648      * @return Boolean
8649      */
8650     isDirty : function(){
8651         var dirty = false;
8652         var items = this.getItems();
8653         items.each(function(f){
8654            if(f.isDirty()){
8655                dirty = true;
8656                return false;
8657            }
8658            return true;
8659         });
8660         return dirty;
8661     },
8662      /**
8663      * Performs a predefined action (submit or load) or custom actions you define on this form.
8664      * @param {String} actionName The name of the action type
8665      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8666      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8667      * accept other config options):
8668      * <pre>
8669 Property          Type             Description
8670 ----------------  ---------------  ----------------------------------------------------------------------------------
8671 url               String           The url for the action (defaults to the form's url)
8672 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8673 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8674 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8675                                    validate the form on the client (defaults to false)
8676      * </pre>
8677      * @return {BasicForm} this
8678      */
8679     doAction : function(action, options){
8680         if(typeof action == 'string'){
8681             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8682         }
8683         if(this.fireEvent('beforeaction', this, action) !== false){
8684             this.beforeAction(action);
8685             action.run.defer(100, action);
8686         }
8687         return this;
8688     },
8689
8690     // private
8691     beforeAction : function(action){
8692         var o = action.options;
8693         
8694         if(this.loadMask){
8695             
8696             if(this.maskBody){
8697                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8698             } else {
8699                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8700             }
8701         }
8702         // not really supported yet.. ??
8703
8704         //if(this.waitMsgTarget === true){
8705         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8706         //}else if(this.waitMsgTarget){
8707         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8708         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8709         //}else {
8710         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8711        // }
8712
8713     },
8714
8715     // private
8716     afterAction : function(action, success){
8717         this.activeAction = null;
8718         var o = action.options;
8719
8720         if(this.loadMask){
8721             
8722             if(this.maskBody){
8723                 Roo.get(document.body).unmask();
8724             } else {
8725                 this.el.unmask();
8726             }
8727         }
8728         
8729         //if(this.waitMsgTarget === true){
8730 //            this.el.unmask();
8731         //}else if(this.waitMsgTarget){
8732         //    this.waitMsgTarget.unmask();
8733         //}else{
8734         //    Roo.MessageBox.updateProgress(1);
8735         //    Roo.MessageBox.hide();
8736        // }
8737         //
8738         if(success){
8739             if(o.reset){
8740                 this.reset();
8741             }
8742             Roo.callback(o.success, o.scope, [this, action]);
8743             this.fireEvent('actioncomplete', this, action);
8744
8745         }else{
8746
8747             // failure condition..
8748             // we have a scenario where updates need confirming.
8749             // eg. if a locking scenario exists..
8750             // we look for { errors : { needs_confirm : true }} in the response.
8751             if (
8752                 (typeof(action.result) != 'undefined')  &&
8753                 (typeof(action.result.errors) != 'undefined')  &&
8754                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8755            ){
8756                 var _t = this;
8757                 Roo.log("not supported yet");
8758                  /*
8759
8760                 Roo.MessageBox.confirm(
8761                     "Change requires confirmation",
8762                     action.result.errorMsg,
8763                     function(r) {
8764                         if (r != 'yes') {
8765                             return;
8766                         }
8767                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8768                     }
8769
8770                 );
8771                 */
8772
8773
8774                 return;
8775             }
8776
8777             Roo.callback(o.failure, o.scope, [this, action]);
8778             // show an error message if no failed handler is set..
8779             if (!this.hasListener('actionfailed')) {
8780                 Roo.log("need to add dialog support");
8781                 /*
8782                 Roo.MessageBox.alert("Error",
8783                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8784                         action.result.errorMsg :
8785                         "Saving Failed, please check your entries or try again"
8786                 );
8787                 */
8788             }
8789
8790             this.fireEvent('actionfailed', this, action);
8791         }
8792
8793     },
8794     /**
8795      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8796      * @param {String} id The value to search for
8797      * @return Field
8798      */
8799     findField : function(id){
8800         var items = this.getItems();
8801         var field = items.get(id);
8802         if(!field){
8803              items.each(function(f){
8804                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8805                     field = f;
8806                     return false;
8807                 }
8808                 return true;
8809             });
8810         }
8811         return field || null;
8812     },
8813      /**
8814      * Mark fields in this form invalid in bulk.
8815      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8816      * @return {BasicForm} this
8817      */
8818     markInvalid : function(errors){
8819         if(errors instanceof Array){
8820             for(var i = 0, len = errors.length; i < len; i++){
8821                 var fieldError = errors[i];
8822                 var f = this.findField(fieldError.id);
8823                 if(f){
8824                     f.markInvalid(fieldError.msg);
8825                 }
8826             }
8827         }else{
8828             var field, id;
8829             for(id in errors){
8830                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8831                     field.markInvalid(errors[id]);
8832                 }
8833             }
8834         }
8835         //Roo.each(this.childForms || [], function (f) {
8836         //    f.markInvalid(errors);
8837         //});
8838
8839         return this;
8840     },
8841
8842     /**
8843      * Set values for fields in this form in bulk.
8844      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8845      * @return {BasicForm} this
8846      */
8847     setValues : function(values){
8848         if(values instanceof Array){ // array of objects
8849             for(var i = 0, len = values.length; i < len; i++){
8850                 var v = values[i];
8851                 var f = this.findField(v.id);
8852                 if(f){
8853                     f.setValue(v.value);
8854                     if(this.trackResetOnLoad){
8855                         f.originalValue = f.getValue();
8856                     }
8857                 }
8858             }
8859         }else{ // object hash
8860             var field, id;
8861             for(id in values){
8862                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8863
8864                     if (field.setFromData &&
8865                         field.valueField &&
8866                         field.displayField &&
8867                         // combos' with local stores can
8868                         // be queried via setValue()
8869                         // to set their value..
8870                         (field.store && !field.store.isLocal)
8871                         ) {
8872                         // it's a combo
8873                         var sd = { };
8874                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8875                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8876                         field.setFromData(sd);
8877
8878                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8879                         
8880                         field.setFromData(values);
8881                         
8882                     } else {
8883                         field.setValue(values[id]);
8884                     }
8885
8886
8887                     if(this.trackResetOnLoad){
8888                         field.originalValue = field.getValue();
8889                     }
8890                 }
8891             }
8892         }
8893
8894         //Roo.each(this.childForms || [], function (f) {
8895         //    f.setValues(values);
8896         //});
8897
8898         return this;
8899     },
8900
8901     /**
8902      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8903      * they are returned as an array.
8904      * @param {Boolean} asString
8905      * @return {Object}
8906      */
8907     getValues : function(asString){
8908         //if (this.childForms) {
8909             // copy values from the child forms
8910         //    Roo.each(this.childForms, function (f) {
8911         //        this.setValues(f.getValues());
8912         //    }, this);
8913         //}
8914
8915
8916
8917         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8918         if(asString === true){
8919             return fs;
8920         }
8921         return Roo.urlDecode(fs);
8922     },
8923
8924     /**
8925      * Returns the fields in this form as an object with key/value pairs.
8926      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8927      * @return {Object}
8928      */
8929     getFieldValues : function(with_hidden)
8930     {
8931         var items = this.getItems();
8932         var ret = {};
8933         items.each(function(f){
8934             
8935             if (!f.getName()) {
8936                 return;
8937             }
8938             
8939             var v = f.getValue();
8940             
8941             if (f.inputType =='radio') {
8942                 if (typeof(ret[f.getName()]) == 'undefined') {
8943                     ret[f.getName()] = ''; // empty..
8944                 }
8945
8946                 if (!f.el.dom.checked) {
8947                     return;
8948
8949                 }
8950                 v = f.el.dom.value;
8951
8952             }
8953             
8954             if(f.xtype == 'MoneyField'){
8955                 ret[f.currencyName] = f.getCurrency();
8956             }
8957
8958             // not sure if this supported any more..
8959             if ((typeof(v) == 'object') && f.getRawValue) {
8960                 v = f.getRawValue() ; // dates..
8961             }
8962             // combo boxes where name != hiddenName...
8963             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8964                 ret[f.name] = f.getRawValue();
8965             }
8966             ret[f.getName()] = v;
8967         });
8968
8969         return ret;
8970     },
8971
8972     /**
8973      * Clears all invalid messages in this form.
8974      * @return {BasicForm} this
8975      */
8976     clearInvalid : function(){
8977         var items = this.getItems();
8978
8979         items.each(function(f){
8980            f.clearInvalid();
8981         });
8982
8983         return this;
8984     },
8985
8986     /**
8987      * Resets this form.
8988      * @return {BasicForm} this
8989      */
8990     reset : function(){
8991         var items = this.getItems();
8992         items.each(function(f){
8993             f.reset();
8994         });
8995
8996         Roo.each(this.childForms || [], function (f) {
8997             f.reset();
8998         });
8999
9000
9001         return this;
9002     },
9003     
9004     getItems : function()
9005     {
9006         var r=new Roo.util.MixedCollection(false, function(o){
9007             return o.id || (o.id = Roo.id());
9008         });
9009         var iter = function(el) {
9010             if (el.inputEl) {
9011                 r.add(el);
9012             }
9013             if (!el.items) {
9014                 return;
9015             }
9016             Roo.each(el.items,function(e) {
9017                 iter(e);
9018             });
9019         };
9020
9021         iter(this);
9022         return r;
9023     },
9024     
9025     hideFields : function(items)
9026     {
9027         Roo.each(items, function(i){
9028             
9029             var f = this.findField(i);
9030             
9031             if(!f){
9032                 return;
9033             }
9034             
9035             f.hide();
9036             
9037         }, this);
9038     },
9039     
9040     showFields : function(items)
9041     {
9042         Roo.each(items, function(i){
9043             
9044             var f = this.findField(i);
9045             
9046             if(!f){
9047                 return;
9048             }
9049             
9050             f.show();
9051             
9052         }, this);
9053     }
9054
9055 });
9056
9057 Roo.apply(Roo.bootstrap.Form, {
9058     
9059     popover : {
9060         
9061         padding : 5,
9062         
9063         isApplied : false,
9064         
9065         isMasked : false,
9066         
9067         form : false,
9068         
9069         target : false,
9070         
9071         toolTip : false,
9072         
9073         intervalID : false,
9074         
9075         maskEl : false,
9076         
9077         apply : function()
9078         {
9079             if(this.isApplied){
9080                 return;
9081             }
9082             
9083             this.maskEl = {
9084                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9085                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9086                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9087                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9088             };
9089             
9090             this.maskEl.top.enableDisplayMode("block");
9091             this.maskEl.left.enableDisplayMode("block");
9092             this.maskEl.bottom.enableDisplayMode("block");
9093             this.maskEl.right.enableDisplayMode("block");
9094             
9095             this.toolTip = new Roo.bootstrap.Tooltip({
9096                 cls : 'roo-form-error-popover',
9097                 alignment : {
9098                     'left' : ['r-l', [-2,0], 'right'],
9099                     'right' : ['l-r', [2,0], 'left'],
9100                     'bottom' : ['tl-bl', [0,2], 'top'],
9101                     'top' : [ 'bl-tl', [0,-2], 'bottom']
9102                 }
9103             });
9104             
9105             this.toolTip.render(Roo.get(document.body));
9106
9107             this.toolTip.el.enableDisplayMode("block");
9108             
9109             Roo.get(document.body).on('click', function(){
9110                 this.unmask();
9111             }, this);
9112             
9113             Roo.get(document.body).on('touchstart', function(){
9114                 this.unmask();
9115             }, this);
9116             
9117             this.isApplied = true
9118         },
9119         
9120         mask : function(form, target)
9121         {
9122             this.form = form;
9123             
9124             this.target = target;
9125             
9126             if(!this.form.errorMask || !target.el){
9127                 return;
9128             }
9129             
9130             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9131             
9132             Roo.log(scrollable);
9133             
9134             var ot = this.target.el.calcOffsetsTo(scrollable);
9135             
9136             var scrollTo = ot[1] - this.form.maskOffset;
9137             
9138             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9139             
9140             scrollable.scrollTo('top', scrollTo);
9141             
9142             var box = this.target.el.getBox();
9143             Roo.log(box);
9144             var zIndex = Roo.bootstrap.Modal.zIndex++;
9145
9146             
9147             this.maskEl.top.setStyle('position', 'absolute');
9148             this.maskEl.top.setStyle('z-index', zIndex);
9149             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
9150             this.maskEl.top.setLeft(0);
9151             this.maskEl.top.setTop(0);
9152             this.maskEl.top.show();
9153             
9154             this.maskEl.left.setStyle('position', 'absolute');
9155             this.maskEl.left.setStyle('z-index', zIndex);
9156             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
9157             this.maskEl.left.setLeft(0);
9158             this.maskEl.left.setTop(box.y - this.padding);
9159             this.maskEl.left.show();
9160
9161             this.maskEl.bottom.setStyle('position', 'absolute');
9162             this.maskEl.bottom.setStyle('z-index', zIndex);
9163             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
9164             this.maskEl.bottom.setLeft(0);
9165             this.maskEl.bottom.setTop(box.bottom + this.padding);
9166             this.maskEl.bottom.show();
9167
9168             this.maskEl.right.setStyle('position', 'absolute');
9169             this.maskEl.right.setStyle('z-index', zIndex);
9170             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
9171             this.maskEl.right.setLeft(box.right + this.padding);
9172             this.maskEl.right.setTop(box.y - this.padding);
9173             this.maskEl.right.show();
9174
9175             this.toolTip.bindEl = this.target.el;
9176
9177             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
9178
9179             var tip = this.target.blankText;
9180
9181             if(this.target.getValue() !== '' ) {
9182                 
9183                 if (this.target.invalidText.length) {
9184                     tip = this.target.invalidText;
9185                 } else if (this.target.regexText.length){
9186                     tip = this.target.regexText;
9187                 }
9188             }
9189
9190             this.toolTip.show(tip);
9191
9192             this.intervalID = window.setInterval(function() {
9193                 Roo.bootstrap.Form.popover.unmask();
9194             }, 10000);
9195
9196             window.onwheel = function(){ return false;};
9197             
9198             (function(){ this.isMasked = true; }).defer(500, this);
9199             
9200         },
9201         
9202         unmask : function()
9203         {
9204             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
9205                 return;
9206             }
9207             
9208             this.maskEl.top.setStyle('position', 'absolute');
9209             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
9210             this.maskEl.top.hide();
9211
9212             this.maskEl.left.setStyle('position', 'absolute');
9213             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
9214             this.maskEl.left.hide();
9215
9216             this.maskEl.bottom.setStyle('position', 'absolute');
9217             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
9218             this.maskEl.bottom.hide();
9219
9220             this.maskEl.right.setStyle('position', 'absolute');
9221             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
9222             this.maskEl.right.hide();
9223             
9224             this.toolTip.hide();
9225             
9226             this.toolTip.el.hide();
9227             
9228             window.onwheel = function(){ return true;};
9229             
9230             if(this.intervalID){
9231                 window.clearInterval(this.intervalID);
9232                 this.intervalID = false;
9233             }
9234             
9235             this.isMasked = false;
9236             
9237         }
9238         
9239     }
9240     
9241 });
9242
9243 /*
9244  * Based on:
9245  * Ext JS Library 1.1.1
9246  * Copyright(c) 2006-2007, Ext JS, LLC.
9247  *
9248  * Originally Released Under LGPL - original licence link has changed is not relivant.
9249  *
9250  * Fork - LGPL
9251  * <script type="text/javascript">
9252  */
9253 /**
9254  * @class Roo.form.VTypes
9255  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
9256  * @singleton
9257  */
9258 Roo.form.VTypes = function(){
9259     // closure these in so they are only created once.
9260     var alpha = /^[a-zA-Z_]+$/;
9261     var alphanum = /^[a-zA-Z0-9_]+$/;
9262     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
9263     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
9264
9265     // All these messages and functions are configurable
9266     return {
9267         /**
9268          * The function used to validate email addresses
9269          * @param {String} value The email address
9270          */
9271         'email' : function(v){
9272             return email.test(v);
9273         },
9274         /**
9275          * The error text to display when the email validation function returns false
9276          * @type String
9277          */
9278         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
9279         /**
9280          * The keystroke filter mask to be applied on email input
9281          * @type RegExp
9282          */
9283         'emailMask' : /[a-z0-9_\.\-@]/i,
9284
9285         /**
9286          * The function used to validate URLs
9287          * @param {String} value The URL
9288          */
9289         'url' : function(v){
9290             return url.test(v);
9291         },
9292         /**
9293          * The error text to display when the url validation function returns false
9294          * @type String
9295          */
9296         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
9297         
9298         /**
9299          * The function used to validate alpha values
9300          * @param {String} value The value
9301          */
9302         'alpha' : function(v){
9303             return alpha.test(v);
9304         },
9305         /**
9306          * The error text to display when the alpha validation function returns false
9307          * @type String
9308          */
9309         'alphaText' : 'This field should only contain letters and _',
9310         /**
9311          * The keystroke filter mask to be applied on alpha input
9312          * @type RegExp
9313          */
9314         'alphaMask' : /[a-z_]/i,
9315
9316         /**
9317          * The function used to validate alphanumeric values
9318          * @param {String} value The value
9319          */
9320         'alphanum' : function(v){
9321             return alphanum.test(v);
9322         },
9323         /**
9324          * The error text to display when the alphanumeric validation function returns false
9325          * @type String
9326          */
9327         'alphanumText' : 'This field should only contain letters, numbers and _',
9328         /**
9329          * The keystroke filter mask to be applied on alphanumeric input
9330          * @type RegExp
9331          */
9332         'alphanumMask' : /[a-z0-9_]/i
9333     };
9334 }();/*
9335  * - LGPL
9336  *
9337  * Input
9338  * 
9339  */
9340
9341 /**
9342  * @class Roo.bootstrap.Input
9343  * @extends Roo.bootstrap.Component
9344  * Bootstrap Input class
9345  * @cfg {Boolean} disabled is it disabled
9346  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
9347  * @cfg {String} name name of the input
9348  * @cfg {string} fieldLabel - the label associated
9349  * @cfg {string} placeholder - placeholder to put in text.
9350  * @cfg {string}  before - input group add on before
9351  * @cfg {string} after - input group add on after
9352  * @cfg {string} size - (lg|sm) or leave empty..
9353  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
9354  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
9355  * @cfg {Number} md colspan out of 12 for computer-sized screens
9356  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
9357  * @cfg {string} value default value of the input
9358  * @cfg {Number} labelWidth set the width of label 
9359  * @cfg {Number} labellg set the width of label (1-12)
9360  * @cfg {Number} labelmd set the width of label (1-12)
9361  * @cfg {Number} labelsm set the width of label (1-12)
9362  * @cfg {Number} labelxs set the width of label (1-12)
9363  * @cfg {String} labelAlign (top|left)
9364  * @cfg {Boolean} readOnly Specifies that the field should be read-only
9365  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9366  * @cfg {String} indicatorpos (left|right) default left
9367  * @cfg {String} capture (user|camera) use for file input only. (default empty)
9368  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9369
9370  * @cfg {String} align (left|center|right) Default left
9371  * @cfg {Boolean} forceFeedback (true|false) Default false
9372  * 
9373  * @constructor
9374  * Create a new Input
9375  * @param {Object} config The config object
9376  */
9377
9378 Roo.bootstrap.Input = function(config){
9379     
9380     Roo.bootstrap.Input.superclass.constructor.call(this, config);
9381     
9382     this.addEvents({
9383         /**
9384          * @event focus
9385          * Fires when this field receives input focus.
9386          * @param {Roo.form.Field} this
9387          */
9388         focus : true,
9389         /**
9390          * @event blur
9391          * Fires when this field loses input focus.
9392          * @param {Roo.form.Field} this
9393          */
9394         blur : true,
9395         /**
9396          * @event specialkey
9397          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9398          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9399          * @param {Roo.form.Field} this
9400          * @param {Roo.EventObject} e The event object
9401          */
9402         specialkey : true,
9403         /**
9404          * @event change
9405          * Fires just before the field blurs if the field value has changed.
9406          * @param {Roo.form.Field} this
9407          * @param {Mixed} newValue The new value
9408          * @param {Mixed} oldValue The original value
9409          */
9410         change : true,
9411         /**
9412          * @event invalid
9413          * Fires after the field has been marked as invalid.
9414          * @param {Roo.form.Field} this
9415          * @param {String} msg The validation message
9416          */
9417         invalid : true,
9418         /**
9419          * @event valid
9420          * Fires after the field has been validated with no errors.
9421          * @param {Roo.form.Field} this
9422          */
9423         valid : true,
9424          /**
9425          * @event keyup
9426          * Fires after the key up
9427          * @param {Roo.form.Field} this
9428          * @param {Roo.EventObject}  e The event Object
9429          */
9430         keyup : true
9431     });
9432 };
9433
9434 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9435      /**
9436      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9437       automatic validation (defaults to "keyup").
9438      */
9439     validationEvent : "keyup",
9440      /**
9441      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9442      */
9443     validateOnBlur : true,
9444     /**
9445      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9446      */
9447     validationDelay : 250,
9448      /**
9449      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9450      */
9451     focusClass : "x-form-focus",  // not needed???
9452     
9453        
9454     /**
9455      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9456      */
9457     invalidClass : "has-warning",
9458     
9459     /**
9460      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9461      */
9462     validClass : "has-success",
9463     
9464     /**
9465      * @cfg {Boolean} hasFeedback (true|false) default true
9466      */
9467     hasFeedback : true,
9468     
9469     /**
9470      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9471      */
9472     invalidFeedbackClass : "glyphicon-warning-sign",
9473     
9474     /**
9475      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9476      */
9477     validFeedbackClass : "glyphicon-ok",
9478     
9479     /**
9480      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9481      */
9482     selectOnFocus : false,
9483     
9484      /**
9485      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9486      */
9487     maskRe : null,
9488        /**
9489      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9490      */
9491     vtype : null,
9492     
9493       /**
9494      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9495      */
9496     disableKeyFilter : false,
9497     
9498        /**
9499      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9500      */
9501     disabled : false,
9502      /**
9503      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9504      */
9505     allowBlank : true,
9506     /**
9507      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9508      */
9509     blankText : "Please complete this mandatory field",
9510     
9511      /**
9512      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9513      */
9514     minLength : 0,
9515     /**
9516      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9517      */
9518     maxLength : Number.MAX_VALUE,
9519     /**
9520      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9521      */
9522     minLengthText : "The minimum length for this field is {0}",
9523     /**
9524      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9525      */
9526     maxLengthText : "The maximum length for this field is {0}",
9527   
9528     
9529     /**
9530      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9531      * If available, this function will be called only after the basic validators all return true, and will be passed the
9532      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9533      */
9534     validator : null,
9535     /**
9536      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9537      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9538      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9539      */
9540     regex : null,
9541     /**
9542      * @cfg {String} regexText -- Depricated - use Invalid Text
9543      */
9544     regexText : "",
9545     
9546     /**
9547      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9548      */
9549     invalidText : "",
9550     
9551     
9552     
9553     autocomplete: false,
9554     
9555     
9556     fieldLabel : '',
9557     inputType : 'text',
9558     
9559     name : false,
9560     placeholder: false,
9561     before : false,
9562     after : false,
9563     size : false,
9564     hasFocus : false,
9565     preventMark: false,
9566     isFormField : true,
9567     value : '',
9568     labelWidth : 2,
9569     labelAlign : false,
9570     readOnly : false,
9571     align : false,
9572     formatedValue : false,
9573     forceFeedback : false,
9574     
9575     indicatorpos : 'left',
9576     
9577     labellg : 0,
9578     labelmd : 0,
9579     labelsm : 0,
9580     labelxs : 0,
9581     
9582     capture : '',
9583     accept : '',
9584     
9585     parentLabelAlign : function()
9586     {
9587         var parent = this;
9588         while (parent.parent()) {
9589             parent = parent.parent();
9590             if (typeof(parent.labelAlign) !='undefined') {
9591                 return parent.labelAlign;
9592             }
9593         }
9594         return 'left';
9595         
9596     },
9597     
9598     getAutoCreate : function()
9599     {
9600         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9601         
9602         var id = Roo.id();
9603         
9604         var cfg = {};
9605         
9606         if(this.inputType != 'hidden'){
9607             cfg.cls = 'form-group' //input-group
9608         }
9609         
9610         var input =  {
9611             tag: 'input',
9612             id : id,
9613             type : this.inputType,
9614             value : this.value,
9615             cls : 'form-control',
9616             placeholder : this.placeholder || '',
9617             autocomplete : this.autocomplete || 'new-password'
9618         };
9619         
9620         if(this.capture.length){
9621             input.capture = this.capture;
9622         }
9623         
9624         if(this.accept.length){
9625             input.accept = this.accept + "/*";
9626         }
9627         
9628         if(this.align){
9629             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9630         }
9631         
9632         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9633             input.maxLength = this.maxLength;
9634         }
9635         
9636         if (this.disabled) {
9637             input.disabled=true;
9638         }
9639         
9640         if (this.readOnly) {
9641             input.readonly=true;
9642         }
9643         
9644         if (this.name) {
9645             input.name = this.name;
9646         }
9647         
9648         if (this.size) {
9649             input.cls += ' input-' + this.size;
9650         }
9651         
9652         var settings=this;
9653         ['xs','sm','md','lg'].map(function(size){
9654             if (settings[size]) {
9655                 cfg.cls += ' col-' + size + '-' + settings[size];
9656             }
9657         });
9658         
9659         var inputblock = input;
9660         
9661         var feedback = {
9662             tag: 'span',
9663             cls: 'glyphicon form-control-feedback'
9664         };
9665             
9666         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9667             
9668             inputblock = {
9669                 cls : 'has-feedback',
9670                 cn :  [
9671                     input,
9672                     feedback
9673                 ] 
9674             };  
9675         }
9676         
9677         if (this.before || this.after) {
9678             
9679             inputblock = {
9680                 cls : 'input-group',
9681                 cn :  [] 
9682             };
9683             
9684             if (this.before && typeof(this.before) == 'string') {
9685                 
9686                 inputblock.cn.push({
9687                     tag :'span',
9688                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9689                     html : this.before
9690                 });
9691             }
9692             if (this.before && typeof(this.before) == 'object') {
9693                 this.before = Roo.factory(this.before);
9694                 
9695                 inputblock.cn.push({
9696                     tag :'span',
9697                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9698                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9699                 });
9700             }
9701             
9702             inputblock.cn.push(input);
9703             
9704             if (this.after && typeof(this.after) == 'string') {
9705                 inputblock.cn.push({
9706                     tag :'span',
9707                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9708                     html : this.after
9709                 });
9710             }
9711             if (this.after && typeof(this.after) == 'object') {
9712                 this.after = Roo.factory(this.after);
9713                 
9714                 inputblock.cn.push({
9715                     tag :'span',
9716                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9717                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9718                 });
9719             }
9720             
9721             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9722                 inputblock.cls += ' has-feedback';
9723                 inputblock.cn.push(feedback);
9724             }
9725         };
9726         var indicator = {
9727             tag : 'i',
9728             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9729             tooltip : 'This field is required'
9730         };
9731         if (Roo.bootstrap.version == 4) {
9732             indicator = {
9733                 tag : 'i',
9734                 style : 'display-none'
9735             };
9736         }
9737         if (align ==='left' && this.fieldLabel.length) {
9738             
9739             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9740             
9741             cfg.cn = [
9742                 indicator,
9743                 {
9744                     tag: 'label',
9745                     'for' :  id,
9746                     cls : 'control-label col-form-label',
9747                     html : this.fieldLabel
9748
9749                 },
9750                 {
9751                     cls : "", 
9752                     cn: [
9753                         inputblock
9754                     ]
9755                 }
9756             ];
9757             
9758             var labelCfg = cfg.cn[1];
9759             var contentCfg = cfg.cn[2];
9760             
9761             if(this.indicatorpos == 'right'){
9762                 cfg.cn = [
9763                     {
9764                         tag: 'label',
9765                         'for' :  id,
9766                         cls : 'control-label col-form-label',
9767                         cn : [
9768                             {
9769                                 tag : 'span',
9770                                 html : this.fieldLabel
9771                             },
9772                             indicator
9773                         ]
9774                     },
9775                     {
9776                         cls : "",
9777                         cn: [
9778                             inputblock
9779                         ]
9780                     }
9781
9782                 ];
9783                 
9784                 labelCfg = cfg.cn[0];
9785                 contentCfg = cfg.cn[1];
9786             
9787             }
9788             
9789             if(this.labelWidth > 12){
9790                 labelCfg.style = "width: " + this.labelWidth + 'px';
9791             }
9792             
9793             if(this.labelWidth < 13 && this.labelmd == 0){
9794                 this.labelmd = this.labelWidth;
9795             }
9796             
9797             if(this.labellg > 0){
9798                 labelCfg.cls += ' col-lg-' + this.labellg;
9799                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9800             }
9801             
9802             if(this.labelmd > 0){
9803                 labelCfg.cls += ' col-md-' + this.labelmd;
9804                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9805             }
9806             
9807             if(this.labelsm > 0){
9808                 labelCfg.cls += ' col-sm-' + this.labelsm;
9809                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9810             }
9811             
9812             if(this.labelxs > 0){
9813                 labelCfg.cls += ' col-xs-' + this.labelxs;
9814                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9815             }
9816             
9817             
9818         } else if ( this.fieldLabel.length) {
9819                 
9820             cfg.cn = [
9821                 {
9822                     tag : 'i',
9823                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9824                     tooltip : 'This field is required'
9825                 },
9826                 {
9827                     tag: 'label',
9828                    //cls : 'input-group-addon',
9829                     html : this.fieldLabel
9830
9831                 },
9832
9833                inputblock
9834
9835            ];
9836            
9837            if(this.indicatorpos == 'right'){
9838                 
9839                 cfg.cn = [
9840                     {
9841                         tag: 'label',
9842                        //cls : 'input-group-addon',
9843                         html : this.fieldLabel
9844
9845                     },
9846                     {
9847                         tag : 'i',
9848                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9849                         tooltip : 'This field is required'
9850                     },
9851
9852                    inputblock
9853
9854                ];
9855
9856             }
9857
9858         } else {
9859             
9860             cfg.cn = [
9861
9862                     inputblock
9863
9864             ];
9865                 
9866                 
9867         };
9868         
9869         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9870            cfg.cls += ' navbar-form';
9871         }
9872         
9873         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9874             // on BS4 we do this only if not form 
9875             cfg.cls += ' navbar-form';
9876             cfg.tag = 'li';
9877         }
9878         
9879         return cfg;
9880         
9881     },
9882     /**
9883      * return the real input element.
9884      */
9885     inputEl: function ()
9886     {
9887         return this.el.select('input.form-control',true).first();
9888     },
9889     
9890     tooltipEl : function()
9891     {
9892         return this.inputEl();
9893     },
9894     
9895     indicatorEl : function()
9896     {
9897         if (Roo.bootstrap.version == 4) {
9898             return false; // not enabled in v4 yet.
9899         }
9900         
9901         var indicator = this.el.select('i.roo-required-indicator',true).first();
9902         
9903         if(!indicator){
9904             return false;
9905         }
9906         
9907         return indicator;
9908         
9909     },
9910     
9911     setDisabled : function(v)
9912     {
9913         var i  = this.inputEl().dom;
9914         if (!v) {
9915             i.removeAttribute('disabled');
9916             return;
9917             
9918         }
9919         i.setAttribute('disabled','true');
9920     },
9921     initEvents : function()
9922     {
9923           
9924         this.inputEl().on("keydown" , this.fireKey,  this);
9925         this.inputEl().on("focus", this.onFocus,  this);
9926         this.inputEl().on("blur", this.onBlur,  this);
9927         
9928         this.inputEl().relayEvent('keyup', this);
9929         
9930         this.indicator = this.indicatorEl();
9931         
9932         if(this.indicator){
9933             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9934         }
9935  
9936         // reference to original value for reset
9937         this.originalValue = this.getValue();
9938         //Roo.form.TextField.superclass.initEvents.call(this);
9939         if(this.validationEvent == 'keyup'){
9940             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9941             this.inputEl().on('keyup', this.filterValidation, this);
9942         }
9943         else if(this.validationEvent !== false){
9944             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9945         }
9946         
9947         if(this.selectOnFocus){
9948             this.on("focus", this.preFocus, this);
9949             
9950         }
9951         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9952             this.inputEl().on("keypress", this.filterKeys, this);
9953         } else {
9954             this.inputEl().relayEvent('keypress', this);
9955         }
9956        /* if(this.grow){
9957             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9958             this.el.on("click", this.autoSize,  this);
9959         }
9960         */
9961         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9962             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9963         }
9964         
9965         if (typeof(this.before) == 'object') {
9966             this.before.render(this.el.select('.roo-input-before',true).first());
9967         }
9968         if (typeof(this.after) == 'object') {
9969             this.after.render(this.el.select('.roo-input-after',true).first());
9970         }
9971         
9972         this.inputEl().on('change', this.onChange, this);
9973         
9974     },
9975     filterValidation : function(e){
9976         if(!e.isNavKeyPress()){
9977             this.validationTask.delay(this.validationDelay);
9978         }
9979     },
9980      /**
9981      * Validates the field value
9982      * @return {Boolean} True if the value is valid, else false
9983      */
9984     validate : function(){
9985         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9986         if(this.disabled || this.validateValue(this.getRawValue())){
9987             this.markValid();
9988             return true;
9989         }
9990         
9991         this.markInvalid();
9992         return false;
9993     },
9994     
9995     
9996     /**
9997      * Validates a value according to the field's validation rules and marks the field as invalid
9998      * if the validation fails
9999      * @param {Mixed} value The value to validate
10000      * @return {Boolean} True if the value is valid, else false
10001      */
10002     validateValue : function(value)
10003     {
10004         if(this.getVisibilityEl().hasClass('hidden')){
10005             return true;
10006         }
10007         
10008         if(value.length < 1)  { // if it's blank
10009             if(this.allowBlank){
10010                 return true;
10011             }
10012             return false;
10013         }
10014         
10015         if(value.length < this.minLength){
10016             return false;
10017         }
10018         if(value.length > this.maxLength){
10019             return false;
10020         }
10021         if(this.vtype){
10022             var vt = Roo.form.VTypes;
10023             if(!vt[this.vtype](value, this)){
10024                 return false;
10025             }
10026         }
10027         if(typeof this.validator == "function"){
10028             var msg = this.validator(value);
10029             if(msg !== true){
10030                 return false;
10031             }
10032             if (typeof(msg) == 'string') {
10033                 this.invalidText = msg;
10034             }
10035         }
10036         
10037         if(this.regex && !this.regex.test(value)){
10038             return false;
10039         }
10040         
10041         return true;
10042     },
10043     
10044      // private
10045     fireKey : function(e){
10046         //Roo.log('field ' + e.getKey());
10047         if(e.isNavKeyPress()){
10048             this.fireEvent("specialkey", this, e);
10049         }
10050     },
10051     focus : function (selectText){
10052         if(this.rendered){
10053             this.inputEl().focus();
10054             if(selectText === true){
10055                 this.inputEl().dom.select();
10056             }
10057         }
10058         return this;
10059     } ,
10060     
10061     onFocus : function(){
10062         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10063            // this.el.addClass(this.focusClass);
10064         }
10065         if(!this.hasFocus){
10066             this.hasFocus = true;
10067             this.startValue = this.getValue();
10068             this.fireEvent("focus", this);
10069         }
10070     },
10071     
10072     beforeBlur : Roo.emptyFn,
10073
10074     
10075     // private
10076     onBlur : function(){
10077         this.beforeBlur();
10078         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10079             //this.el.removeClass(this.focusClass);
10080         }
10081         this.hasFocus = false;
10082         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10083             this.validate();
10084         }
10085         var v = this.getValue();
10086         if(String(v) !== String(this.startValue)){
10087             this.fireEvent('change', this, v, this.startValue);
10088         }
10089         this.fireEvent("blur", this);
10090     },
10091     
10092     onChange : function(e)
10093     {
10094         var v = this.getValue();
10095         if(String(v) !== String(this.startValue)){
10096             this.fireEvent('change', this, v, this.startValue);
10097         }
10098         
10099     },
10100     
10101     /**
10102      * Resets the current field value to the originally loaded value and clears any validation messages
10103      */
10104     reset : function(){
10105         this.setValue(this.originalValue);
10106         this.validate();
10107     },
10108      /**
10109      * Returns the name of the field
10110      * @return {Mixed} name The name field
10111      */
10112     getName: function(){
10113         return this.name;
10114     },
10115      /**
10116      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
10117      * @return {Mixed} value The field value
10118      */
10119     getValue : function(){
10120         
10121         var v = this.inputEl().getValue();
10122         
10123         return v;
10124     },
10125     /**
10126      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
10127      * @return {Mixed} value The field value
10128      */
10129     getRawValue : function(){
10130         var v = this.inputEl().getValue();
10131         
10132         return v;
10133     },
10134     
10135     /**
10136      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
10137      * @param {Mixed} value The value to set
10138      */
10139     setRawValue : function(v){
10140         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10141     },
10142     
10143     selectText : function(start, end){
10144         var v = this.getRawValue();
10145         if(v.length > 0){
10146             start = start === undefined ? 0 : start;
10147             end = end === undefined ? v.length : end;
10148             var d = this.inputEl().dom;
10149             if(d.setSelectionRange){
10150                 d.setSelectionRange(start, end);
10151             }else if(d.createTextRange){
10152                 var range = d.createTextRange();
10153                 range.moveStart("character", start);
10154                 range.moveEnd("character", v.length-end);
10155                 range.select();
10156             }
10157         }
10158     },
10159     
10160     /**
10161      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
10162      * @param {Mixed} value The value to set
10163      */
10164     setValue : function(v){
10165         this.value = v;
10166         if(this.rendered){
10167             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
10168             this.validate();
10169         }
10170     },
10171     
10172     /*
10173     processValue : function(value){
10174         if(this.stripCharsRe){
10175             var newValue = value.replace(this.stripCharsRe, '');
10176             if(newValue !== value){
10177                 this.setRawValue(newValue);
10178                 return newValue;
10179             }
10180         }
10181         return value;
10182     },
10183   */
10184     preFocus : function(){
10185         
10186         if(this.selectOnFocus){
10187             this.inputEl().dom.select();
10188         }
10189     },
10190     filterKeys : function(e){
10191         var k = e.getKey();
10192         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
10193             return;
10194         }
10195         var c = e.getCharCode(), cc = String.fromCharCode(c);
10196         if(Roo.isIE && (e.isSpecialKey() || !cc)){
10197             return;
10198         }
10199         if(!this.maskRe.test(cc)){
10200             e.stopEvent();
10201         }
10202     },
10203      /**
10204      * Clear any invalid styles/messages for this field
10205      */
10206     clearInvalid : function(){
10207         
10208         if(!this.el || this.preventMark){ // not rendered
10209             return;
10210         }
10211         
10212         
10213         this.el.removeClass([this.invalidClass, 'is-invalid']);
10214         
10215         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10216             
10217             var feedback = this.el.select('.form-control-feedback', true).first();
10218             
10219             if(feedback){
10220                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10221             }
10222             
10223         }
10224         
10225         if(this.indicator){
10226             this.indicator.removeClass('visible');
10227             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10228         }
10229         
10230         this.fireEvent('valid', this);
10231     },
10232     
10233      /**
10234      * Mark this field as valid
10235      */
10236     markValid : function()
10237     {
10238         if(!this.el  || this.preventMark){ // not rendered...
10239             return;
10240         }
10241         
10242         this.el.removeClass([this.invalidClass, this.validClass]);
10243         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10244
10245         var feedback = this.el.select('.form-control-feedback', true).first();
10246             
10247         if(feedback){
10248             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10249         }
10250         
10251         if(this.indicator){
10252             this.indicator.removeClass('visible');
10253             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10254         }
10255         
10256         if(this.disabled){
10257             return;
10258         }
10259         
10260         if(this.allowBlank && !this.getRawValue().length){
10261             return;
10262         }
10263         if (Roo.bootstrap.version == 3) {
10264             this.el.addClass(this.validClass);
10265         } else {
10266             this.inputEl().addClass('is-valid');
10267         }
10268
10269         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10270             
10271             var feedback = this.el.select('.form-control-feedback', true).first();
10272             
10273             if(feedback){
10274                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10275                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10276             }
10277             
10278         }
10279         
10280         this.fireEvent('valid', this);
10281     },
10282     
10283      /**
10284      * Mark this field as invalid
10285      * @param {String} msg The validation message
10286      */
10287     markInvalid : function(msg)
10288     {
10289         if(!this.el  || this.preventMark){ // not rendered
10290             return;
10291         }
10292         
10293         this.el.removeClass([this.invalidClass, this.validClass]);
10294         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10295         
10296         var feedback = this.el.select('.form-control-feedback', true).first();
10297             
10298         if(feedback){
10299             this.el.select('.form-control-feedback', true).first().removeClass(
10300                     [this.invalidFeedbackClass, this.validFeedbackClass]);
10301         }
10302
10303         if(this.disabled){
10304             return;
10305         }
10306         
10307         if(this.allowBlank && !this.getRawValue().length){
10308             return;
10309         }
10310         
10311         if(this.indicator){
10312             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10313             this.indicator.addClass('visible');
10314         }
10315         if (Roo.bootstrap.version == 3) {
10316             this.el.addClass(this.invalidClass);
10317         } else {
10318             this.inputEl().addClass('is-invalid');
10319         }
10320         
10321         
10322         
10323         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10324             
10325             var feedback = this.el.select('.form-control-feedback', true).first();
10326             
10327             if(feedback){
10328                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10329                 
10330                 if(this.getValue().length || this.forceFeedback){
10331                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10332                 }
10333                 
10334             }
10335             
10336         }
10337         
10338         this.fireEvent('invalid', this, msg);
10339     },
10340     // private
10341     SafariOnKeyDown : function(event)
10342     {
10343         // this is a workaround for a password hang bug on chrome/ webkit.
10344         if (this.inputEl().dom.type != 'password') {
10345             return;
10346         }
10347         
10348         var isSelectAll = false;
10349         
10350         if(this.inputEl().dom.selectionEnd > 0){
10351             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
10352         }
10353         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
10354             event.preventDefault();
10355             this.setValue('');
10356             return;
10357         }
10358         
10359         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10360             
10361             event.preventDefault();
10362             // this is very hacky as keydown always get's upper case.
10363             //
10364             var cc = String.fromCharCode(event.getCharCode());
10365             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
10366             
10367         }
10368     },
10369     adjustWidth : function(tag, w){
10370         tag = tag.toLowerCase();
10371         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10372             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10373                 if(tag == 'input'){
10374                     return w + 2;
10375                 }
10376                 if(tag == 'textarea'){
10377                     return w-2;
10378                 }
10379             }else if(Roo.isOpera){
10380                 if(tag == 'input'){
10381                     return w + 2;
10382                 }
10383                 if(tag == 'textarea'){
10384                     return w-2;
10385                 }
10386             }
10387         }
10388         return w;
10389     },
10390     
10391     setFieldLabel : function(v)
10392     {
10393         if(!this.rendered){
10394             return;
10395         }
10396         
10397         if(this.indicatorEl()){
10398             var ar = this.el.select('label > span',true);
10399             
10400             if (ar.elements.length) {
10401                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10402                 this.fieldLabel = v;
10403                 return;
10404             }
10405             
10406             var br = this.el.select('label',true);
10407             
10408             if(br.elements.length) {
10409                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10410                 this.fieldLabel = v;
10411                 return;
10412             }
10413             
10414             Roo.log('Cannot Found any of label > span || label in input');
10415             return;
10416         }
10417         
10418         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10419         this.fieldLabel = v;
10420         
10421         
10422     }
10423 });
10424
10425  
10426 /*
10427  * - LGPL
10428  *
10429  * Input
10430  * 
10431  */
10432
10433 /**
10434  * @class Roo.bootstrap.TextArea
10435  * @extends Roo.bootstrap.Input
10436  * Bootstrap TextArea class
10437  * @cfg {Number} cols Specifies the visible width of a text area
10438  * @cfg {Number} rows Specifies the visible number of lines in a text area
10439  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10440  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10441  * @cfg {string} html text
10442  * 
10443  * @constructor
10444  * Create a new TextArea
10445  * @param {Object} config The config object
10446  */
10447
10448 Roo.bootstrap.TextArea = function(config){
10449     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10450    
10451 };
10452
10453 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10454      
10455     cols : false,
10456     rows : 5,
10457     readOnly : false,
10458     warp : 'soft',
10459     resize : false,
10460     value: false,
10461     html: false,
10462     
10463     getAutoCreate : function(){
10464         
10465         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10466         
10467         var id = Roo.id();
10468         
10469         var cfg = {};
10470         
10471         if(this.inputType != 'hidden'){
10472             cfg.cls = 'form-group' //input-group
10473         }
10474         
10475         var input =  {
10476             tag: 'textarea',
10477             id : id,
10478             warp : this.warp,
10479             rows : this.rows,
10480             value : this.value || '',
10481             html: this.html || '',
10482             cls : 'form-control',
10483             placeholder : this.placeholder || '' 
10484             
10485         };
10486         
10487         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10488             input.maxLength = this.maxLength;
10489         }
10490         
10491         if(this.resize){
10492             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10493         }
10494         
10495         if(this.cols){
10496             input.cols = this.cols;
10497         }
10498         
10499         if (this.readOnly) {
10500             input.readonly = true;
10501         }
10502         
10503         if (this.name) {
10504             input.name = this.name;
10505         }
10506         
10507         if (this.size) {
10508             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10509         }
10510         
10511         var settings=this;
10512         ['xs','sm','md','lg'].map(function(size){
10513             if (settings[size]) {
10514                 cfg.cls += ' col-' + size + '-' + settings[size];
10515             }
10516         });
10517         
10518         var inputblock = input;
10519         
10520         if(this.hasFeedback && !this.allowBlank){
10521             
10522             var feedback = {
10523                 tag: 'span',
10524                 cls: 'glyphicon form-control-feedback'
10525             };
10526
10527             inputblock = {
10528                 cls : 'has-feedback',
10529                 cn :  [
10530                     input,
10531                     feedback
10532                 ] 
10533             };  
10534         }
10535         
10536         
10537         if (this.before || this.after) {
10538             
10539             inputblock = {
10540                 cls : 'input-group',
10541                 cn :  [] 
10542             };
10543             if (this.before) {
10544                 inputblock.cn.push({
10545                     tag :'span',
10546                     cls : 'input-group-addon',
10547                     html : this.before
10548                 });
10549             }
10550             
10551             inputblock.cn.push(input);
10552             
10553             if(this.hasFeedback && !this.allowBlank){
10554                 inputblock.cls += ' has-feedback';
10555                 inputblock.cn.push(feedback);
10556             }
10557             
10558             if (this.after) {
10559                 inputblock.cn.push({
10560                     tag :'span',
10561                     cls : 'input-group-addon',
10562                     html : this.after
10563                 });
10564             }
10565             
10566         }
10567         
10568         if (align ==='left' && this.fieldLabel.length) {
10569             cfg.cn = [
10570                 {
10571                     tag: 'label',
10572                     'for' :  id,
10573                     cls : 'control-label',
10574                     html : this.fieldLabel
10575                 },
10576                 {
10577                     cls : "",
10578                     cn: [
10579                         inputblock
10580                     ]
10581                 }
10582
10583             ];
10584             
10585             if(this.labelWidth > 12){
10586                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10587             }
10588
10589             if(this.labelWidth < 13 && this.labelmd == 0){
10590                 this.labelmd = this.labelWidth;
10591             }
10592
10593             if(this.labellg > 0){
10594                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10595                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10596             }
10597
10598             if(this.labelmd > 0){
10599                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10600                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10601             }
10602
10603             if(this.labelsm > 0){
10604                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10605                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10606             }
10607
10608             if(this.labelxs > 0){
10609                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10610                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10611             }
10612             
10613         } else if ( this.fieldLabel.length) {
10614             cfg.cn = [
10615
10616                {
10617                    tag: 'label',
10618                    //cls : 'input-group-addon',
10619                    html : this.fieldLabel
10620
10621                },
10622
10623                inputblock
10624
10625            ];
10626
10627         } else {
10628
10629             cfg.cn = [
10630
10631                 inputblock
10632
10633             ];
10634                 
10635         }
10636         
10637         if (this.disabled) {
10638             input.disabled=true;
10639         }
10640         
10641         return cfg;
10642         
10643     },
10644     /**
10645      * return the real textarea element.
10646      */
10647     inputEl: function ()
10648     {
10649         return this.el.select('textarea.form-control',true).first();
10650     },
10651     
10652     /**
10653      * Clear any invalid styles/messages for this field
10654      */
10655     clearInvalid : function()
10656     {
10657         
10658         if(!this.el || this.preventMark){ // not rendered
10659             return;
10660         }
10661         
10662         var label = this.el.select('label', true).first();
10663         var icon = this.el.select('i.fa-star', true).first();
10664         
10665         if(label && icon){
10666             icon.remove();
10667         }
10668         this.el.removeClass( this.validClass);
10669         this.inputEl().removeClass('is-invalid');
10670          
10671         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10672             
10673             var feedback = this.el.select('.form-control-feedback', true).first();
10674             
10675             if(feedback){
10676                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10677             }
10678             
10679         }
10680         
10681         this.fireEvent('valid', this);
10682     },
10683     
10684      /**
10685      * Mark this field as valid
10686      */
10687     markValid : function()
10688     {
10689         if(!this.el  || this.preventMark){ // not rendered
10690             return;
10691         }
10692         
10693         this.el.removeClass([this.invalidClass, this.validClass]);
10694         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10695         
10696         var feedback = this.el.select('.form-control-feedback', true).first();
10697             
10698         if(feedback){
10699             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10700         }
10701
10702         if(this.disabled || this.allowBlank){
10703             return;
10704         }
10705         
10706         var label = this.el.select('label', true).first();
10707         var icon = this.el.select('i.fa-star', true).first();
10708         
10709         if(label && icon){
10710             icon.remove();
10711         }
10712         if (Roo.bootstrap.version == 3) {
10713             this.el.addClass(this.validClass);
10714         } else {
10715             this.inputEl().addClass('is-valid');
10716         }
10717         
10718         
10719         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10720             
10721             var feedback = this.el.select('.form-control-feedback', true).first();
10722             
10723             if(feedback){
10724                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10725                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10726             }
10727             
10728         }
10729         
10730         this.fireEvent('valid', this);
10731     },
10732     
10733      /**
10734      * Mark this field as invalid
10735      * @param {String} msg The validation message
10736      */
10737     markInvalid : function(msg)
10738     {
10739         if(!this.el  || this.preventMark){ // not rendered
10740             return;
10741         }
10742         
10743         this.el.removeClass([this.invalidClass, this.validClass]);
10744         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10745         
10746         var feedback = this.el.select('.form-control-feedback', true).first();
10747             
10748         if(feedback){
10749             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10750         }
10751
10752         if(this.disabled || this.allowBlank){
10753             return;
10754         }
10755         
10756         var label = this.el.select('label', true).first();
10757         var icon = this.el.select('i.fa-star', true).first();
10758         
10759         if(!this.getValue().length && label && !icon){
10760             this.el.createChild({
10761                 tag : 'i',
10762                 cls : 'text-danger fa fa-lg fa-star',
10763                 tooltip : 'This field is required',
10764                 style : 'margin-right:5px;'
10765             }, label, true);
10766         }
10767         
10768         if (Roo.bootstrap.version == 3) {
10769             this.el.addClass(this.invalidClass);
10770         } else {
10771             this.inputEl().addClass('is-invalid');
10772         }
10773         
10774         // fixme ... this may be depricated need to test..
10775         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10776             
10777             var feedback = this.el.select('.form-control-feedback', true).first();
10778             
10779             if(feedback){
10780                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10781                 
10782                 if(this.getValue().length || this.forceFeedback){
10783                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10784                 }
10785                 
10786             }
10787             
10788         }
10789         
10790         this.fireEvent('invalid', this, msg);
10791     }
10792 });
10793
10794  
10795 /*
10796  * - LGPL
10797  *
10798  * trigger field - base class for combo..
10799  * 
10800  */
10801  
10802 /**
10803  * @class Roo.bootstrap.TriggerField
10804  * @extends Roo.bootstrap.Input
10805  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10806  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10807  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10808  * for which you can provide a custom implementation.  For example:
10809  * <pre><code>
10810 var trigger = new Roo.bootstrap.TriggerField();
10811 trigger.onTriggerClick = myTriggerFn;
10812 trigger.applyTo('my-field');
10813 </code></pre>
10814  *
10815  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10816  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10817  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10818  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10819  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
10820
10821  * @constructor
10822  * Create a new TriggerField.
10823  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10824  * to the base TextField)
10825  */
10826 Roo.bootstrap.TriggerField = function(config){
10827     this.mimicing = false;
10828     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10829 };
10830
10831 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10832     /**
10833      * @cfg {String} triggerClass A CSS class to apply to the trigger
10834      */
10835      /**
10836      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10837      */
10838     hideTrigger:false,
10839
10840     /**
10841      * @cfg {Boolean} removable (true|false) special filter default false
10842      */
10843     removable : false,
10844     
10845     /** @cfg {Boolean} grow @hide */
10846     /** @cfg {Number} growMin @hide */
10847     /** @cfg {Number} growMax @hide */
10848
10849     /**
10850      * @hide 
10851      * @method
10852      */
10853     autoSize: Roo.emptyFn,
10854     // private
10855     monitorTab : true,
10856     // private
10857     deferHeight : true,
10858
10859     
10860     actionMode : 'wrap',
10861     
10862     caret : false,
10863     
10864     
10865     getAutoCreate : function(){
10866        
10867         var align = this.labelAlign || this.parentLabelAlign();
10868         
10869         var id = Roo.id();
10870         
10871         var cfg = {
10872             cls: 'form-group' //input-group
10873         };
10874         
10875         
10876         var input =  {
10877             tag: 'input',
10878             id : id,
10879             type : this.inputType,
10880             cls : 'form-control',
10881             autocomplete: 'new-password',
10882             placeholder : this.placeholder || '' 
10883             
10884         };
10885         if (this.name) {
10886             input.name = this.name;
10887         }
10888         if (this.size) {
10889             input.cls += ' input-' + this.size;
10890         }
10891         
10892         if (this.disabled) {
10893             input.disabled=true;
10894         }
10895         
10896         var inputblock = input;
10897         
10898         if(this.hasFeedback && !this.allowBlank){
10899             
10900             var feedback = {
10901                 tag: 'span',
10902                 cls: 'glyphicon form-control-feedback'
10903             };
10904             
10905             if(this.removable && !this.editable && !this.tickable){
10906                 inputblock = {
10907                     cls : 'has-feedback',
10908                     cn :  [
10909                         inputblock,
10910                         {
10911                             tag: 'button',
10912                             html : 'x',
10913                             cls : 'roo-combo-removable-btn close'
10914                         },
10915                         feedback
10916                     ] 
10917                 };
10918             } else {
10919                 inputblock = {
10920                     cls : 'has-feedback',
10921                     cn :  [
10922                         inputblock,
10923                         feedback
10924                     ] 
10925                 };
10926             }
10927
10928         } else {
10929             if(this.removable && !this.editable && !this.tickable){
10930                 inputblock = {
10931                     cls : 'roo-removable',
10932                     cn :  [
10933                         inputblock,
10934                         {
10935                             tag: 'button',
10936                             html : 'x',
10937                             cls : 'roo-combo-removable-btn close'
10938                         }
10939                     ] 
10940                 };
10941             }
10942         }
10943         
10944         if (this.before || this.after) {
10945             
10946             inputblock = {
10947                 cls : 'input-group',
10948                 cn :  [] 
10949             };
10950             if (this.before) {
10951                 inputblock.cn.push({
10952                     tag :'span',
10953                     cls : 'input-group-addon input-group-prepend input-group-text',
10954                     html : this.before
10955                 });
10956             }
10957             
10958             inputblock.cn.push(input);
10959             
10960             if(this.hasFeedback && !this.allowBlank){
10961                 inputblock.cls += ' has-feedback';
10962                 inputblock.cn.push(feedback);
10963             }
10964             
10965             if (this.after) {
10966                 inputblock.cn.push({
10967                     tag :'span',
10968                     cls : 'input-group-addon input-group-append input-group-text',
10969                     html : this.after
10970                 });
10971             }
10972             
10973         };
10974         
10975       
10976         
10977         var ibwrap = inputblock;
10978         
10979         if(this.multiple){
10980             ibwrap = {
10981                 tag: 'ul',
10982                 cls: 'roo-select2-choices',
10983                 cn:[
10984                     {
10985                         tag: 'li',
10986                         cls: 'roo-select2-search-field',
10987                         cn: [
10988
10989                             inputblock
10990                         ]
10991                     }
10992                 ]
10993             };
10994                 
10995         }
10996         
10997         var combobox = {
10998             cls: 'roo-select2-container input-group',
10999             cn: [
11000                  {
11001                     tag: 'input',
11002                     type : 'hidden',
11003                     cls: 'form-hidden-field'
11004                 },
11005                 ibwrap
11006             ]
11007         };
11008         
11009         if(!this.multiple && this.showToggleBtn){
11010             
11011             var caret = {
11012                         tag: 'span',
11013                         cls: 'caret'
11014              };
11015             if (this.caret != false) {
11016                 caret = {
11017                      tag: 'i',
11018                      cls: 'fa fa-' + this.caret
11019                 };
11020                 
11021             }
11022             
11023             combobox.cn.push({
11024                 tag :'span',
11025                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11026                 cn : [
11027                     Roo.bootstrap.version == 3 ? caret : '',
11028                     {
11029                         tag: 'span',
11030                         cls: 'combobox-clear',
11031                         cn  : [
11032                             {
11033                                 tag : 'i',
11034                                 cls: 'icon-remove'
11035                             }
11036                         ]
11037                     }
11038                 ]
11039
11040             })
11041         }
11042         
11043         if(this.multiple){
11044             combobox.cls += ' roo-select2-container-multi';
11045         }
11046          var indicator = {
11047             tag : 'i',
11048             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11049             tooltip : 'This field is required'
11050         };
11051         if (Roo.bootstrap.version == 4) {
11052             indicator = {
11053                 tag : 'i',
11054                 style : 'display:none'
11055             };
11056         }
11057         
11058         
11059         if (align ==='left' && this.fieldLabel.length) {
11060             
11061             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11062
11063             cfg.cn = [
11064                 indicator,
11065                 {
11066                     tag: 'label',
11067                     'for' :  id,
11068                     cls : 'control-label',
11069                     html : this.fieldLabel
11070
11071                 },
11072                 {
11073                     cls : "", 
11074                     cn: [
11075                         combobox
11076                     ]
11077                 }
11078
11079             ];
11080             
11081             var labelCfg = cfg.cn[1];
11082             var contentCfg = cfg.cn[2];
11083             
11084             if(this.indicatorpos == 'right'){
11085                 cfg.cn = [
11086                     {
11087                         tag: 'label',
11088                         'for' :  id,
11089                         cls : 'control-label',
11090                         cn : [
11091                             {
11092                                 tag : 'span',
11093                                 html : this.fieldLabel
11094                             },
11095                             indicator
11096                         ]
11097                     },
11098                     {
11099                         cls : "", 
11100                         cn: [
11101                             combobox
11102                         ]
11103                     }
11104
11105                 ];
11106                 
11107                 labelCfg = cfg.cn[0];
11108                 contentCfg = cfg.cn[1];
11109             }
11110             
11111             if(this.labelWidth > 12){
11112                 labelCfg.style = "width: " + this.labelWidth + 'px';
11113             }
11114             
11115             if(this.labelWidth < 13 && this.labelmd == 0){
11116                 this.labelmd = this.labelWidth;
11117             }
11118             
11119             if(this.labellg > 0){
11120                 labelCfg.cls += ' col-lg-' + this.labellg;
11121                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11122             }
11123             
11124             if(this.labelmd > 0){
11125                 labelCfg.cls += ' col-md-' + this.labelmd;
11126                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11127             }
11128             
11129             if(this.labelsm > 0){
11130                 labelCfg.cls += ' col-sm-' + this.labelsm;
11131                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11132             }
11133             
11134             if(this.labelxs > 0){
11135                 labelCfg.cls += ' col-xs-' + this.labelxs;
11136                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11137             }
11138             
11139         } else if ( this.fieldLabel.length) {
11140 //                Roo.log(" label");
11141             cfg.cn = [
11142                 indicator,
11143                {
11144                    tag: 'label',
11145                    //cls : 'input-group-addon',
11146                    html : this.fieldLabel
11147
11148                },
11149
11150                combobox
11151
11152             ];
11153             
11154             if(this.indicatorpos == 'right'){
11155                 
11156                 cfg.cn = [
11157                     {
11158                        tag: 'label',
11159                        cn : [
11160                            {
11161                                tag : 'span',
11162                                html : this.fieldLabel
11163                            },
11164                            indicator
11165                        ]
11166
11167                     },
11168                     combobox
11169
11170                 ];
11171
11172             }
11173
11174         } else {
11175             
11176 //                Roo.log(" no label && no align");
11177                 cfg = combobox
11178                      
11179                 
11180         }
11181         
11182         var settings=this;
11183         ['xs','sm','md','lg'].map(function(size){
11184             if (settings[size]) {
11185                 cfg.cls += ' col-' + size + '-' + settings[size];
11186             }
11187         });
11188         
11189         return cfg;
11190         
11191     },
11192     
11193     
11194     
11195     // private
11196     onResize : function(w, h){
11197 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
11198 //        if(typeof w == 'number'){
11199 //            var x = w - this.trigger.getWidth();
11200 //            this.inputEl().setWidth(this.adjustWidth('input', x));
11201 //            this.trigger.setStyle('left', x+'px');
11202 //        }
11203     },
11204
11205     // private
11206     adjustSize : Roo.BoxComponent.prototype.adjustSize,
11207
11208     // private
11209     getResizeEl : function(){
11210         return this.inputEl();
11211     },
11212
11213     // private
11214     getPositionEl : function(){
11215         return this.inputEl();
11216     },
11217
11218     // private
11219     alignErrorIcon : function(){
11220         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
11221     },
11222
11223     // private
11224     initEvents : function(){
11225         
11226         this.createList();
11227         
11228         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
11229         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
11230         if(!this.multiple && this.showToggleBtn){
11231             this.trigger = this.el.select('span.dropdown-toggle',true).first();
11232             if(this.hideTrigger){
11233                 this.trigger.setDisplayed(false);
11234             }
11235             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
11236         }
11237         
11238         if(this.multiple){
11239             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
11240         }
11241         
11242         if(this.removable && !this.editable && !this.tickable){
11243             var close = this.closeTriggerEl();
11244             
11245             if(close){
11246                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
11247                 close.on('click', this.removeBtnClick, this, close);
11248             }
11249         }
11250         
11251         //this.trigger.addClassOnOver('x-form-trigger-over');
11252         //this.trigger.addClassOnClick('x-form-trigger-click');
11253         
11254         //if(!this.width){
11255         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
11256         //}
11257     },
11258     
11259     closeTriggerEl : function()
11260     {
11261         var close = this.el.select('.roo-combo-removable-btn', true).first();
11262         return close ? close : false;
11263     },
11264     
11265     removeBtnClick : function(e, h, el)
11266     {
11267         e.preventDefault();
11268         
11269         if(this.fireEvent("remove", this) !== false){
11270             this.reset();
11271             this.fireEvent("afterremove", this)
11272         }
11273     },
11274     
11275     createList : function()
11276     {
11277         this.list = Roo.get(document.body).createChild({
11278             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
11279             cls: 'typeahead typeahead-long dropdown-menu',
11280             style: 'display:none'
11281         });
11282         
11283         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
11284         
11285     },
11286
11287     // private
11288     initTrigger : function(){
11289        
11290     },
11291
11292     // private
11293     onDestroy : function(){
11294         if(this.trigger){
11295             this.trigger.removeAllListeners();
11296           //  this.trigger.remove();
11297         }
11298         //if(this.wrap){
11299         //    this.wrap.remove();
11300         //}
11301         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
11302     },
11303
11304     // private
11305     onFocus : function(){
11306         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
11307         /*
11308         if(!this.mimicing){
11309             this.wrap.addClass('x-trigger-wrap-focus');
11310             this.mimicing = true;
11311             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
11312             if(this.monitorTab){
11313                 this.el.on("keydown", this.checkTab, this);
11314             }
11315         }
11316         */
11317     },
11318
11319     // private
11320     checkTab : function(e){
11321         if(e.getKey() == e.TAB){
11322             this.triggerBlur();
11323         }
11324     },
11325
11326     // private
11327     onBlur : function(){
11328         // do nothing
11329     },
11330
11331     // private
11332     mimicBlur : function(e, t){
11333         /*
11334         if(!this.wrap.contains(t) && this.validateBlur()){
11335             this.triggerBlur();
11336         }
11337         */
11338     },
11339
11340     // private
11341     triggerBlur : function(){
11342         this.mimicing = false;
11343         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
11344         if(this.monitorTab){
11345             this.el.un("keydown", this.checkTab, this);
11346         }
11347         //this.wrap.removeClass('x-trigger-wrap-focus');
11348         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
11349     },
11350
11351     // private
11352     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
11353     validateBlur : function(e, t){
11354         return true;
11355     },
11356
11357     // private
11358     onDisable : function(){
11359         this.inputEl().dom.disabled = true;
11360         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11361         //if(this.wrap){
11362         //    this.wrap.addClass('x-item-disabled');
11363         //}
11364     },
11365
11366     // private
11367     onEnable : function(){
11368         this.inputEl().dom.disabled = false;
11369         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11370         //if(this.wrap){
11371         //    this.el.removeClass('x-item-disabled');
11372         //}
11373     },
11374
11375     // private
11376     onShow : function(){
11377         var ae = this.getActionEl();
11378         
11379         if(ae){
11380             ae.dom.style.display = '';
11381             ae.dom.style.visibility = 'visible';
11382         }
11383     },
11384
11385     // private
11386     
11387     onHide : function(){
11388         var ae = this.getActionEl();
11389         ae.dom.style.display = 'none';
11390     },
11391
11392     /**
11393      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
11394      * by an implementing function.
11395      * @method
11396      * @param {EventObject} e
11397      */
11398     onTriggerClick : Roo.emptyFn
11399 });
11400  /*
11401  * Based on:
11402  * Ext JS Library 1.1.1
11403  * Copyright(c) 2006-2007, Ext JS, LLC.
11404  *
11405  * Originally Released Under LGPL - original licence link has changed is not relivant.
11406  *
11407  * Fork - LGPL
11408  * <script type="text/javascript">
11409  */
11410
11411
11412 /**
11413  * @class Roo.data.SortTypes
11414  * @singleton
11415  * Defines the default sorting (casting?) comparison functions used when sorting data.
11416  */
11417 Roo.data.SortTypes = {
11418     /**
11419      * Default sort that does nothing
11420      * @param {Mixed} s The value being converted
11421      * @return {Mixed} The comparison value
11422      */
11423     none : function(s){
11424         return s;
11425     },
11426     
11427     /**
11428      * The regular expression used to strip tags
11429      * @type {RegExp}
11430      * @property
11431      */
11432     stripTagsRE : /<\/?[^>]+>/gi,
11433     
11434     /**
11435      * Strips all HTML tags to sort on text only
11436      * @param {Mixed} s The value being converted
11437      * @return {String} The comparison value
11438      */
11439     asText : function(s){
11440         return String(s).replace(this.stripTagsRE, "");
11441     },
11442     
11443     /**
11444      * Strips all HTML tags to sort on text only - Case insensitive
11445      * @param {Mixed} s The value being converted
11446      * @return {String} The comparison value
11447      */
11448     asUCText : function(s){
11449         return String(s).toUpperCase().replace(this.stripTagsRE, "");
11450     },
11451     
11452     /**
11453      * Case insensitive string
11454      * @param {Mixed} s The value being converted
11455      * @return {String} The comparison value
11456      */
11457     asUCString : function(s) {
11458         return String(s).toUpperCase();
11459     },
11460     
11461     /**
11462      * Date sorting
11463      * @param {Mixed} s The value being converted
11464      * @return {Number} The comparison value
11465      */
11466     asDate : function(s) {
11467         if(!s){
11468             return 0;
11469         }
11470         if(s instanceof Date){
11471             return s.getTime();
11472         }
11473         return Date.parse(String(s));
11474     },
11475     
11476     /**
11477      * Float sorting
11478      * @param {Mixed} s The value being converted
11479      * @return {Float} The comparison value
11480      */
11481     asFloat : function(s) {
11482         var val = parseFloat(String(s).replace(/,/g, ""));
11483         if(isNaN(val)) {
11484             val = 0;
11485         }
11486         return val;
11487     },
11488     
11489     /**
11490      * Integer sorting
11491      * @param {Mixed} s The value being converted
11492      * @return {Number} The comparison value
11493      */
11494     asInt : function(s) {
11495         var val = parseInt(String(s).replace(/,/g, ""));
11496         if(isNaN(val)) {
11497             val = 0;
11498         }
11499         return val;
11500     }
11501 };/*
11502  * Based on:
11503  * Ext JS Library 1.1.1
11504  * Copyright(c) 2006-2007, Ext JS, LLC.
11505  *
11506  * Originally Released Under LGPL - original licence link has changed is not relivant.
11507  *
11508  * Fork - LGPL
11509  * <script type="text/javascript">
11510  */
11511
11512 /**
11513 * @class Roo.data.Record
11514  * Instances of this class encapsulate both record <em>definition</em> information, and record
11515  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11516  * to access Records cached in an {@link Roo.data.Store} object.<br>
11517  * <p>
11518  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11519  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11520  * objects.<br>
11521  * <p>
11522  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11523  * @constructor
11524  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11525  * {@link #create}. The parameters are the same.
11526  * @param {Array} data An associative Array of data values keyed by the field name.
11527  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11528  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11529  * not specified an integer id is generated.
11530  */
11531 Roo.data.Record = function(data, id){
11532     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11533     this.data = data;
11534 };
11535
11536 /**
11537  * Generate a constructor for a specific record layout.
11538  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11539  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11540  * Each field definition object may contain the following properties: <ul>
11541  * <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,
11542  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11543  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11544  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11545  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11546  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11547  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11548  * this may be omitted.</p></li>
11549  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11550  * <ul><li>auto (Default, implies no conversion)</li>
11551  * <li>string</li>
11552  * <li>int</li>
11553  * <li>float</li>
11554  * <li>boolean</li>
11555  * <li>date</li></ul></p></li>
11556  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11557  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11558  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11559  * by the Reader into an object that will be stored in the Record. It is passed the
11560  * following parameters:<ul>
11561  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11562  * </ul></p></li>
11563  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11564  * </ul>
11565  * <br>usage:<br><pre><code>
11566 var TopicRecord = Roo.data.Record.create(
11567     {name: 'title', mapping: 'topic_title'},
11568     {name: 'author', mapping: 'username'},
11569     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11570     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11571     {name: 'lastPoster', mapping: 'user2'},
11572     {name: 'excerpt', mapping: 'post_text'}
11573 );
11574
11575 var myNewRecord = new TopicRecord({
11576     title: 'Do my job please',
11577     author: 'noobie',
11578     totalPosts: 1,
11579     lastPost: new Date(),
11580     lastPoster: 'Animal',
11581     excerpt: 'No way dude!'
11582 });
11583 myStore.add(myNewRecord);
11584 </code></pre>
11585  * @method create
11586  * @static
11587  */
11588 Roo.data.Record.create = function(o){
11589     var f = function(){
11590         f.superclass.constructor.apply(this, arguments);
11591     };
11592     Roo.extend(f, Roo.data.Record);
11593     var p = f.prototype;
11594     p.fields = new Roo.util.MixedCollection(false, function(field){
11595         return field.name;
11596     });
11597     for(var i = 0, len = o.length; i < len; i++){
11598         p.fields.add(new Roo.data.Field(o[i]));
11599     }
11600     f.getField = function(name){
11601         return p.fields.get(name);  
11602     };
11603     return f;
11604 };
11605
11606 Roo.data.Record.AUTO_ID = 1000;
11607 Roo.data.Record.EDIT = 'edit';
11608 Roo.data.Record.REJECT = 'reject';
11609 Roo.data.Record.COMMIT = 'commit';
11610
11611 Roo.data.Record.prototype = {
11612     /**
11613      * Readonly flag - true if this record has been modified.
11614      * @type Boolean
11615      */
11616     dirty : false,
11617     editing : false,
11618     error: null,
11619     modified: null,
11620
11621     // private
11622     join : function(store){
11623         this.store = store;
11624     },
11625
11626     /**
11627      * Set the named field to the specified value.
11628      * @param {String} name The name of the field to set.
11629      * @param {Object} value The value to set the field to.
11630      */
11631     set : function(name, value){
11632         if(this.data[name] == value){
11633             return;
11634         }
11635         this.dirty = true;
11636         if(!this.modified){
11637             this.modified = {};
11638         }
11639         if(typeof this.modified[name] == 'undefined'){
11640             this.modified[name] = this.data[name];
11641         }
11642         this.data[name] = value;
11643         if(!this.editing && this.store){
11644             this.store.afterEdit(this);
11645         }       
11646     },
11647
11648     /**
11649      * Get the value of the named field.
11650      * @param {String} name The name of the field to get the value of.
11651      * @return {Object} The value of the field.
11652      */
11653     get : function(name){
11654         return this.data[name]; 
11655     },
11656
11657     // private
11658     beginEdit : function(){
11659         this.editing = true;
11660         this.modified = {}; 
11661     },
11662
11663     // private
11664     cancelEdit : function(){
11665         this.editing = false;
11666         delete this.modified;
11667     },
11668
11669     // private
11670     endEdit : function(){
11671         this.editing = false;
11672         if(this.dirty && this.store){
11673             this.store.afterEdit(this);
11674         }
11675     },
11676
11677     /**
11678      * Usually called by the {@link Roo.data.Store} which owns the Record.
11679      * Rejects all changes made to the Record since either creation, or the last commit operation.
11680      * Modified fields are reverted to their original values.
11681      * <p>
11682      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11683      * of reject operations.
11684      */
11685     reject : function(){
11686         var m = this.modified;
11687         for(var n in m){
11688             if(typeof m[n] != "function"){
11689                 this.data[n] = m[n];
11690             }
11691         }
11692         this.dirty = false;
11693         delete this.modified;
11694         this.editing = false;
11695         if(this.store){
11696             this.store.afterReject(this);
11697         }
11698     },
11699
11700     /**
11701      * Usually called by the {@link Roo.data.Store} which owns the Record.
11702      * Commits all changes made to the Record since either creation, or the last commit operation.
11703      * <p>
11704      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11705      * of commit operations.
11706      */
11707     commit : function(){
11708         this.dirty = false;
11709         delete this.modified;
11710         this.editing = false;
11711         if(this.store){
11712             this.store.afterCommit(this);
11713         }
11714     },
11715
11716     // private
11717     hasError : function(){
11718         return this.error != null;
11719     },
11720
11721     // private
11722     clearError : function(){
11723         this.error = null;
11724     },
11725
11726     /**
11727      * Creates a copy of this record.
11728      * @param {String} id (optional) A new record id if you don't want to use this record's id
11729      * @return {Record}
11730      */
11731     copy : function(newId) {
11732         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11733     }
11734 };/*
11735  * Based on:
11736  * Ext JS Library 1.1.1
11737  * Copyright(c) 2006-2007, Ext JS, LLC.
11738  *
11739  * Originally Released Under LGPL - original licence link has changed is not relivant.
11740  *
11741  * Fork - LGPL
11742  * <script type="text/javascript">
11743  */
11744
11745
11746
11747 /**
11748  * @class Roo.data.Store
11749  * @extends Roo.util.Observable
11750  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11751  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11752  * <p>
11753  * 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
11754  * has no knowledge of the format of the data returned by the Proxy.<br>
11755  * <p>
11756  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11757  * instances from the data object. These records are cached and made available through accessor functions.
11758  * @constructor
11759  * Creates a new Store.
11760  * @param {Object} config A config object containing the objects needed for the Store to access data,
11761  * and read the data into Records.
11762  */
11763 Roo.data.Store = function(config){
11764     this.data = new Roo.util.MixedCollection(false);
11765     this.data.getKey = function(o){
11766         return o.id;
11767     };
11768     this.baseParams = {};
11769     // private
11770     this.paramNames = {
11771         "start" : "start",
11772         "limit" : "limit",
11773         "sort" : "sort",
11774         "dir" : "dir",
11775         "multisort" : "_multisort"
11776     };
11777
11778     if(config && config.data){
11779         this.inlineData = config.data;
11780         delete config.data;
11781     }
11782
11783     Roo.apply(this, config);
11784     
11785     if(this.reader){ // reader passed
11786         this.reader = Roo.factory(this.reader, Roo.data);
11787         this.reader.xmodule = this.xmodule || false;
11788         if(!this.recordType){
11789             this.recordType = this.reader.recordType;
11790         }
11791         if(this.reader.onMetaChange){
11792             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11793         }
11794     }
11795
11796     if(this.recordType){
11797         this.fields = this.recordType.prototype.fields;
11798     }
11799     this.modified = [];
11800
11801     this.addEvents({
11802         /**
11803          * @event datachanged
11804          * Fires when the data cache has changed, and a widget which is using this Store
11805          * as a Record cache should refresh its view.
11806          * @param {Store} this
11807          */
11808         datachanged : true,
11809         /**
11810          * @event metachange
11811          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11812          * @param {Store} this
11813          * @param {Object} meta The JSON metadata
11814          */
11815         metachange : true,
11816         /**
11817          * @event add
11818          * Fires when Records have been added to the Store
11819          * @param {Store} this
11820          * @param {Roo.data.Record[]} records The array of Records added
11821          * @param {Number} index The index at which the record(s) were added
11822          */
11823         add : true,
11824         /**
11825          * @event remove
11826          * Fires when a Record has been removed from the Store
11827          * @param {Store} this
11828          * @param {Roo.data.Record} record The Record that was removed
11829          * @param {Number} index The index at which the record was removed
11830          */
11831         remove : true,
11832         /**
11833          * @event update
11834          * Fires when a Record has been updated
11835          * @param {Store} this
11836          * @param {Roo.data.Record} record The Record that was updated
11837          * @param {String} operation The update operation being performed.  Value may be one of:
11838          * <pre><code>
11839  Roo.data.Record.EDIT
11840  Roo.data.Record.REJECT
11841  Roo.data.Record.COMMIT
11842          * </code></pre>
11843          */
11844         update : true,
11845         /**
11846          * @event clear
11847          * Fires when the data cache has been cleared.
11848          * @param {Store} this
11849          */
11850         clear : true,
11851         /**
11852          * @event beforeload
11853          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11854          * the load action will be canceled.
11855          * @param {Store} this
11856          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11857          */
11858         beforeload : true,
11859         /**
11860          * @event beforeloadadd
11861          * Fires after a new set of Records has been loaded.
11862          * @param {Store} this
11863          * @param {Roo.data.Record[]} records The Records that were loaded
11864          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11865          */
11866         beforeloadadd : true,
11867         /**
11868          * @event load
11869          * Fires after a new set of Records has been loaded, before they are added to the store.
11870          * @param {Store} this
11871          * @param {Roo.data.Record[]} records The Records that were loaded
11872          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11873          * @params {Object} return from reader
11874          */
11875         load : true,
11876         /**
11877          * @event loadexception
11878          * Fires if an exception occurs in the Proxy during loading.
11879          * Called with the signature of the Proxy's "loadexception" event.
11880          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11881          * 
11882          * @param {Proxy} 
11883          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11884          * @param {Object} load options 
11885          * @param {Object} jsonData from your request (normally this contains the Exception)
11886          */
11887         loadexception : true
11888     });
11889     
11890     if(this.proxy){
11891         this.proxy = Roo.factory(this.proxy, Roo.data);
11892         this.proxy.xmodule = this.xmodule || false;
11893         this.relayEvents(this.proxy,  ["loadexception"]);
11894     }
11895     this.sortToggle = {};
11896     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11897
11898     Roo.data.Store.superclass.constructor.call(this);
11899
11900     if(this.inlineData){
11901         this.loadData(this.inlineData);
11902         delete this.inlineData;
11903     }
11904 };
11905
11906 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11907      /**
11908     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11909     * without a remote query - used by combo/forms at present.
11910     */
11911     
11912     /**
11913     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11914     */
11915     /**
11916     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11917     */
11918     /**
11919     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11920     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11921     */
11922     /**
11923     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11924     * on any HTTP request
11925     */
11926     /**
11927     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11928     */
11929     /**
11930     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11931     */
11932     multiSort: false,
11933     /**
11934     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11935     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11936     */
11937     remoteSort : false,
11938
11939     /**
11940     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11941      * loaded or when a record is removed. (defaults to false).
11942     */
11943     pruneModifiedRecords : false,
11944
11945     // private
11946     lastOptions : null,
11947
11948     /**
11949      * Add Records to the Store and fires the add event.
11950      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11951      */
11952     add : function(records){
11953         records = [].concat(records);
11954         for(var i = 0, len = records.length; i < len; i++){
11955             records[i].join(this);
11956         }
11957         var index = this.data.length;
11958         this.data.addAll(records);
11959         this.fireEvent("add", this, records, index);
11960     },
11961
11962     /**
11963      * Remove a Record from the Store and fires the remove event.
11964      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11965      */
11966     remove : function(record){
11967         var index = this.data.indexOf(record);
11968         this.data.removeAt(index);
11969  
11970         if(this.pruneModifiedRecords){
11971             this.modified.remove(record);
11972         }
11973         this.fireEvent("remove", this, record, index);
11974     },
11975
11976     /**
11977      * Remove all Records from the Store and fires the clear event.
11978      */
11979     removeAll : function(){
11980         this.data.clear();
11981         if(this.pruneModifiedRecords){
11982             this.modified = [];
11983         }
11984         this.fireEvent("clear", this);
11985     },
11986
11987     /**
11988      * Inserts Records to the Store at the given index and fires the add event.
11989      * @param {Number} index The start index at which to insert the passed Records.
11990      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11991      */
11992     insert : function(index, records){
11993         records = [].concat(records);
11994         for(var i = 0, len = records.length; i < len; i++){
11995             this.data.insert(index, records[i]);
11996             records[i].join(this);
11997         }
11998         this.fireEvent("add", this, records, index);
11999     },
12000
12001     /**
12002      * Get the index within the cache of the passed Record.
12003      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
12004      * @return {Number} The index of the passed Record. Returns -1 if not found.
12005      */
12006     indexOf : function(record){
12007         return this.data.indexOf(record);
12008     },
12009
12010     /**
12011      * Get the index within the cache of the Record with the passed id.
12012      * @param {String} id The id of the Record to find.
12013      * @return {Number} The index of the Record. Returns -1 if not found.
12014      */
12015     indexOfId : function(id){
12016         return this.data.indexOfKey(id);
12017     },
12018
12019     /**
12020      * Get the Record with the specified id.
12021      * @param {String} id The id of the Record to find.
12022      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
12023      */
12024     getById : function(id){
12025         return this.data.key(id);
12026     },
12027
12028     /**
12029      * Get the Record at the specified index.
12030      * @param {Number} index The index of the Record to find.
12031      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
12032      */
12033     getAt : function(index){
12034         return this.data.itemAt(index);
12035     },
12036
12037     /**
12038      * Returns a range of Records between specified indices.
12039      * @param {Number} startIndex (optional) The starting index (defaults to 0)
12040      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
12041      * @return {Roo.data.Record[]} An array of Records
12042      */
12043     getRange : function(start, end){
12044         return this.data.getRange(start, end);
12045     },
12046
12047     // private
12048     storeOptions : function(o){
12049         o = Roo.apply({}, o);
12050         delete o.callback;
12051         delete o.scope;
12052         this.lastOptions = o;
12053     },
12054
12055     /**
12056      * Loads the Record cache from the configured Proxy using the configured Reader.
12057      * <p>
12058      * If using remote paging, then the first load call must specify the <em>start</em>
12059      * and <em>limit</em> properties in the options.params property to establish the initial
12060      * position within the dataset, and the number of Records to cache on each read from the Proxy.
12061      * <p>
12062      * <strong>It is important to note that for remote data sources, loading is asynchronous,
12063      * and this call will return before the new data has been loaded. Perform any post-processing
12064      * in a callback function, or in a "load" event handler.</strong>
12065      * <p>
12066      * @param {Object} options An object containing properties which control loading options:<ul>
12067      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
12068      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
12069      * passed the following arguments:<ul>
12070      * <li>r : Roo.data.Record[]</li>
12071      * <li>options: Options object from the load call</li>
12072      * <li>success: Boolean success indicator</li></ul></li>
12073      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
12074      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
12075      * </ul>
12076      */
12077     load : function(options){
12078         options = options || {};
12079         if(this.fireEvent("beforeload", this, options) !== false){
12080             this.storeOptions(options);
12081             var p = Roo.apply(options.params || {}, this.baseParams);
12082             // if meta was not loaded from remote source.. try requesting it.
12083             if (!this.reader.metaFromRemote) {
12084                 p._requestMeta = 1;
12085             }
12086             if(this.sortInfo && this.remoteSort){
12087                 var pn = this.paramNames;
12088                 p[pn["sort"]] = this.sortInfo.field;
12089                 p[pn["dir"]] = this.sortInfo.direction;
12090             }
12091             if (this.multiSort) {
12092                 var pn = this.paramNames;
12093                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
12094             }
12095             
12096             this.proxy.load(p, this.reader, this.loadRecords, this, options);
12097         }
12098     },
12099
12100     /**
12101      * Reloads the Record cache from the configured Proxy using the configured Reader and
12102      * the options from the last load operation performed.
12103      * @param {Object} options (optional) An object containing properties which may override the options
12104      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
12105      * the most recently used options are reused).
12106      */
12107     reload : function(options){
12108         this.load(Roo.applyIf(options||{}, this.lastOptions));
12109     },
12110
12111     // private
12112     // Called as a callback by the Reader during a load operation.
12113     loadRecords : function(o, options, success){
12114         if(!o || success === false){
12115             if(success !== false){
12116                 this.fireEvent("load", this, [], options, o);
12117             }
12118             if(options.callback){
12119                 options.callback.call(options.scope || this, [], options, false);
12120             }
12121             return;
12122         }
12123         // if data returned failure - throw an exception.
12124         if (o.success === false) {
12125             // show a message if no listener is registered.
12126             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
12127                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
12128             }
12129             // loadmask wil be hooked into this..
12130             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
12131             return;
12132         }
12133         var r = o.records, t = o.totalRecords || r.length;
12134         
12135         this.fireEvent("beforeloadadd", this, r, options, o);
12136         
12137         if(!options || options.add !== true){
12138             if(this.pruneModifiedRecords){
12139                 this.modified = [];
12140             }
12141             for(var i = 0, len = r.length; i < len; i++){
12142                 r[i].join(this);
12143             }
12144             if(this.snapshot){
12145                 this.data = this.snapshot;
12146                 delete this.snapshot;
12147             }
12148             this.data.clear();
12149             this.data.addAll(r);
12150             this.totalLength = t;
12151             this.applySort();
12152             this.fireEvent("datachanged", this);
12153         }else{
12154             this.totalLength = Math.max(t, this.data.length+r.length);
12155             this.add(r);
12156         }
12157         
12158         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
12159                 
12160             var e = new Roo.data.Record({});
12161
12162             e.set(this.parent.displayField, this.parent.emptyTitle);
12163             e.set(this.parent.valueField, '');
12164
12165             this.insert(0, e);
12166         }
12167             
12168         this.fireEvent("load", this, r, options, o);
12169         if(options.callback){
12170             options.callback.call(options.scope || this, r, options, true);
12171         }
12172     },
12173
12174
12175     /**
12176      * Loads data from a passed data block. A Reader which understands the format of the data
12177      * must have been configured in the constructor.
12178      * @param {Object} data The data block from which to read the Records.  The format of the data expected
12179      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
12180      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
12181      */
12182     loadData : function(o, append){
12183         var r = this.reader.readRecords(o);
12184         this.loadRecords(r, {add: append}, true);
12185     },
12186     
12187      /**
12188      * using 'cn' the nested child reader read the child array into it's child stores.
12189      * @param {Object} rec The record with a 'children array
12190      */
12191     loadDataFromChildren : function(rec)
12192     {
12193         this.loadData(this.reader.toLoadData(rec));
12194     },
12195     
12196
12197     /**
12198      * Gets the number of cached records.
12199      * <p>
12200      * <em>If using paging, this may not be the total size of the dataset. If the data object
12201      * used by the Reader contains the dataset size, then the getTotalCount() function returns
12202      * the data set size</em>
12203      */
12204     getCount : function(){
12205         return this.data.length || 0;
12206     },
12207
12208     /**
12209      * Gets the total number of records in the dataset as returned by the server.
12210      * <p>
12211      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
12212      * the dataset size</em>
12213      */
12214     getTotalCount : function(){
12215         return this.totalLength || 0;
12216     },
12217
12218     /**
12219      * Returns the sort state of the Store as an object with two properties:
12220      * <pre><code>
12221  field {String} The name of the field by which the Records are sorted
12222  direction {String} The sort order, "ASC" or "DESC"
12223      * </code></pre>
12224      */
12225     getSortState : function(){
12226         return this.sortInfo;
12227     },
12228
12229     // private
12230     applySort : function(){
12231         if(this.sortInfo && !this.remoteSort){
12232             var s = this.sortInfo, f = s.field;
12233             var st = this.fields.get(f).sortType;
12234             var fn = function(r1, r2){
12235                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
12236                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12237             };
12238             this.data.sort(s.direction, fn);
12239             if(this.snapshot && this.snapshot != this.data){
12240                 this.snapshot.sort(s.direction, fn);
12241             }
12242         }
12243     },
12244
12245     /**
12246      * Sets the default sort column and order to be used by the next load operation.
12247      * @param {String} fieldName The name of the field to sort by.
12248      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12249      */
12250     setDefaultSort : function(field, dir){
12251         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
12252     },
12253
12254     /**
12255      * Sort the Records.
12256      * If remote sorting is used, the sort is performed on the server, and the cache is
12257      * reloaded. If local sorting is used, the cache is sorted internally.
12258      * @param {String} fieldName The name of the field to sort by.
12259      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
12260      */
12261     sort : function(fieldName, dir){
12262         var f = this.fields.get(fieldName);
12263         if(!dir){
12264             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
12265             
12266             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
12267                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
12268             }else{
12269                 dir = f.sortDir;
12270             }
12271         }
12272         this.sortToggle[f.name] = dir;
12273         this.sortInfo = {field: f.name, direction: dir};
12274         if(!this.remoteSort){
12275             this.applySort();
12276             this.fireEvent("datachanged", this);
12277         }else{
12278             this.load(this.lastOptions);
12279         }
12280     },
12281
12282     /**
12283      * Calls the specified function for each of the Records in the cache.
12284      * @param {Function} fn The function to call. The Record is passed as the first parameter.
12285      * Returning <em>false</em> aborts and exits the iteration.
12286      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
12287      */
12288     each : function(fn, scope){
12289         this.data.each(fn, scope);
12290     },
12291
12292     /**
12293      * Gets all records modified since the last commit.  Modified records are persisted across load operations
12294      * (e.g., during paging).
12295      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
12296      */
12297     getModifiedRecords : function(){
12298         return this.modified;
12299     },
12300
12301     // private
12302     createFilterFn : function(property, value, anyMatch){
12303         if(!value.exec){ // not a regex
12304             value = String(value);
12305             if(value.length == 0){
12306                 return false;
12307             }
12308             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
12309         }
12310         return function(r){
12311             return value.test(r.data[property]);
12312         };
12313     },
12314
12315     /**
12316      * Sums the value of <i>property</i> for each record between start and end and returns the result.
12317      * @param {String} property A field on your records
12318      * @param {Number} start The record index to start at (defaults to 0)
12319      * @param {Number} end The last record index to include (defaults to length - 1)
12320      * @return {Number} The sum
12321      */
12322     sum : function(property, start, end){
12323         var rs = this.data.items, v = 0;
12324         start = start || 0;
12325         end = (end || end === 0) ? end : rs.length-1;
12326
12327         for(var i = start; i <= end; i++){
12328             v += (rs[i].data[property] || 0);
12329         }
12330         return v;
12331     },
12332
12333     /**
12334      * Filter the records by a specified property.
12335      * @param {String} field A field on your records
12336      * @param {String/RegExp} value Either a string that the field
12337      * should start with or a RegExp to test against the field
12338      * @param {Boolean} anyMatch True to match any part not just the beginning
12339      */
12340     filter : function(property, value, anyMatch){
12341         var fn = this.createFilterFn(property, value, anyMatch);
12342         return fn ? this.filterBy(fn) : this.clearFilter();
12343     },
12344
12345     /**
12346      * Filter by a function. The specified function will be called with each
12347      * record in this data source. If the function returns true the record is included,
12348      * otherwise it is filtered.
12349      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12350      * @param {Object} scope (optional) The scope of the function (defaults to this)
12351      */
12352     filterBy : function(fn, scope){
12353         this.snapshot = this.snapshot || this.data;
12354         this.data = this.queryBy(fn, scope||this);
12355         this.fireEvent("datachanged", this);
12356     },
12357
12358     /**
12359      * Query the records by a specified property.
12360      * @param {String} field A field on your records
12361      * @param {String/RegExp} value Either a string that the field
12362      * should start with or a RegExp to test against the field
12363      * @param {Boolean} anyMatch True to match any part not just the beginning
12364      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12365      */
12366     query : function(property, value, anyMatch){
12367         var fn = this.createFilterFn(property, value, anyMatch);
12368         return fn ? this.queryBy(fn) : this.data.clone();
12369     },
12370
12371     /**
12372      * Query by a function. The specified function will be called with each
12373      * record in this data source. If the function returns true the record is included
12374      * in the results.
12375      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12376      * @param {Object} scope (optional) The scope of the function (defaults to this)
12377       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12378      **/
12379     queryBy : function(fn, scope){
12380         var data = this.snapshot || this.data;
12381         return data.filterBy(fn, scope||this);
12382     },
12383
12384     /**
12385      * Collects unique values for a particular dataIndex from this store.
12386      * @param {String} dataIndex The property to collect
12387      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12388      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12389      * @return {Array} An array of the unique values
12390      **/
12391     collect : function(dataIndex, allowNull, bypassFilter){
12392         var d = (bypassFilter === true && this.snapshot) ?
12393                 this.snapshot.items : this.data.items;
12394         var v, sv, r = [], l = {};
12395         for(var i = 0, len = d.length; i < len; i++){
12396             v = d[i].data[dataIndex];
12397             sv = String(v);
12398             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12399                 l[sv] = true;
12400                 r[r.length] = v;
12401             }
12402         }
12403         return r;
12404     },
12405
12406     /**
12407      * Revert to a view of the Record cache with no filtering applied.
12408      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12409      */
12410     clearFilter : function(suppressEvent){
12411         if(this.snapshot && this.snapshot != this.data){
12412             this.data = this.snapshot;
12413             delete this.snapshot;
12414             if(suppressEvent !== true){
12415                 this.fireEvent("datachanged", this);
12416             }
12417         }
12418     },
12419
12420     // private
12421     afterEdit : function(record){
12422         if(this.modified.indexOf(record) == -1){
12423             this.modified.push(record);
12424         }
12425         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12426     },
12427     
12428     // private
12429     afterReject : function(record){
12430         this.modified.remove(record);
12431         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12432     },
12433
12434     // private
12435     afterCommit : function(record){
12436         this.modified.remove(record);
12437         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12438     },
12439
12440     /**
12441      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12442      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12443      */
12444     commitChanges : function(){
12445         var m = this.modified.slice(0);
12446         this.modified = [];
12447         for(var i = 0, len = m.length; i < len; i++){
12448             m[i].commit();
12449         }
12450     },
12451
12452     /**
12453      * Cancel outstanding changes on all changed records.
12454      */
12455     rejectChanges : function(){
12456         var m = this.modified.slice(0);
12457         this.modified = [];
12458         for(var i = 0, len = m.length; i < len; i++){
12459             m[i].reject();
12460         }
12461     },
12462
12463     onMetaChange : function(meta, rtype, o){
12464         this.recordType = rtype;
12465         this.fields = rtype.prototype.fields;
12466         delete this.snapshot;
12467         this.sortInfo = meta.sortInfo || this.sortInfo;
12468         this.modified = [];
12469         this.fireEvent('metachange', this, this.reader.meta);
12470     },
12471     
12472     moveIndex : function(data, type)
12473     {
12474         var index = this.indexOf(data);
12475         
12476         var newIndex = index + type;
12477         
12478         this.remove(data);
12479         
12480         this.insert(newIndex, data);
12481         
12482     }
12483 });/*
12484  * Based on:
12485  * Ext JS Library 1.1.1
12486  * Copyright(c) 2006-2007, Ext JS, LLC.
12487  *
12488  * Originally Released Under LGPL - original licence link has changed is not relivant.
12489  *
12490  * Fork - LGPL
12491  * <script type="text/javascript">
12492  */
12493
12494 /**
12495  * @class Roo.data.SimpleStore
12496  * @extends Roo.data.Store
12497  * Small helper class to make creating Stores from Array data easier.
12498  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12499  * @cfg {Array} fields An array of field definition objects, or field name strings.
12500  * @cfg {Object} an existing reader (eg. copied from another store)
12501  * @cfg {Array} data The multi-dimensional array of data
12502  * @constructor
12503  * @param {Object} config
12504  */
12505 Roo.data.SimpleStore = function(config)
12506 {
12507     Roo.data.SimpleStore.superclass.constructor.call(this, {
12508         isLocal : true,
12509         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
12510                 id: config.id
12511             },
12512             Roo.data.Record.create(config.fields)
12513         ),
12514         proxy : new Roo.data.MemoryProxy(config.data)
12515     });
12516     this.load();
12517 };
12518 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12519  * Based on:
12520  * Ext JS Library 1.1.1
12521  * Copyright(c) 2006-2007, Ext JS, LLC.
12522  *
12523  * Originally Released Under LGPL - original licence link has changed is not relivant.
12524  *
12525  * Fork - LGPL
12526  * <script type="text/javascript">
12527  */
12528
12529 /**
12530 /**
12531  * @extends Roo.data.Store
12532  * @class Roo.data.JsonStore
12533  * Small helper class to make creating Stores for JSON data easier. <br/>
12534 <pre><code>
12535 var store = new Roo.data.JsonStore({
12536     url: 'get-images.php',
12537     root: 'images',
12538     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12539 });
12540 </code></pre>
12541  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12542  * JsonReader and HttpProxy (unless inline data is provided).</b>
12543  * @cfg {Array} fields An array of field definition objects, or field name strings.
12544  * @constructor
12545  * @param {Object} config
12546  */
12547 Roo.data.JsonStore = function(c){
12548     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12549         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12550         reader: new Roo.data.JsonReader(c, c.fields)
12551     }));
12552 };
12553 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12554  * Based on:
12555  * Ext JS Library 1.1.1
12556  * Copyright(c) 2006-2007, Ext JS, LLC.
12557  *
12558  * Originally Released Under LGPL - original licence link has changed is not relivant.
12559  *
12560  * Fork - LGPL
12561  * <script type="text/javascript">
12562  */
12563
12564  
12565 Roo.data.Field = function(config){
12566     if(typeof config == "string"){
12567         config = {name: config};
12568     }
12569     Roo.apply(this, config);
12570     
12571     if(!this.type){
12572         this.type = "auto";
12573     }
12574     
12575     var st = Roo.data.SortTypes;
12576     // named sortTypes are supported, here we look them up
12577     if(typeof this.sortType == "string"){
12578         this.sortType = st[this.sortType];
12579     }
12580     
12581     // set default sortType for strings and dates
12582     if(!this.sortType){
12583         switch(this.type){
12584             case "string":
12585                 this.sortType = st.asUCString;
12586                 break;
12587             case "date":
12588                 this.sortType = st.asDate;
12589                 break;
12590             default:
12591                 this.sortType = st.none;
12592         }
12593     }
12594
12595     // define once
12596     var stripRe = /[\$,%]/g;
12597
12598     // prebuilt conversion function for this field, instead of
12599     // switching every time we're reading a value
12600     if(!this.convert){
12601         var cv, dateFormat = this.dateFormat;
12602         switch(this.type){
12603             case "":
12604             case "auto":
12605             case undefined:
12606                 cv = function(v){ return v; };
12607                 break;
12608             case "string":
12609                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12610                 break;
12611             case "int":
12612                 cv = function(v){
12613                     return v !== undefined && v !== null && v !== '' ?
12614                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12615                     };
12616                 break;
12617             case "float":
12618                 cv = function(v){
12619                     return v !== undefined && v !== null && v !== '' ?
12620                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12621                     };
12622                 break;
12623             case "bool":
12624             case "boolean":
12625                 cv = function(v){ return v === true || v === "true" || v == 1; };
12626                 break;
12627             case "date":
12628                 cv = function(v){
12629                     if(!v){
12630                         return '';
12631                     }
12632                     if(v instanceof Date){
12633                         return v;
12634                     }
12635                     if(dateFormat){
12636                         if(dateFormat == "timestamp"){
12637                             return new Date(v*1000);
12638                         }
12639                         return Date.parseDate(v, dateFormat);
12640                     }
12641                     var parsed = Date.parse(v);
12642                     return parsed ? new Date(parsed) : null;
12643                 };
12644              break;
12645             
12646         }
12647         this.convert = cv;
12648     }
12649 };
12650
12651 Roo.data.Field.prototype = {
12652     dateFormat: null,
12653     defaultValue: "",
12654     mapping: null,
12655     sortType : null,
12656     sortDir : "ASC"
12657 };/*
12658  * Based on:
12659  * Ext JS Library 1.1.1
12660  * Copyright(c) 2006-2007, Ext JS, LLC.
12661  *
12662  * Originally Released Under LGPL - original licence link has changed is not relivant.
12663  *
12664  * Fork - LGPL
12665  * <script type="text/javascript">
12666  */
12667  
12668 // Base class for reading structured data from a data source.  This class is intended to be
12669 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12670
12671 /**
12672  * @class Roo.data.DataReader
12673  * Base class for reading structured data from a data source.  This class is intended to be
12674  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12675  */
12676
12677 Roo.data.DataReader = function(meta, recordType){
12678     
12679     this.meta = meta;
12680     
12681     this.recordType = recordType instanceof Array ? 
12682         Roo.data.Record.create(recordType) : recordType;
12683 };
12684
12685 Roo.data.DataReader.prototype = {
12686     
12687     
12688     readerType : 'Data',
12689      /**
12690      * Create an empty record
12691      * @param {Object} data (optional) - overlay some values
12692      * @return {Roo.data.Record} record created.
12693      */
12694     newRow :  function(d) {
12695         var da =  {};
12696         this.recordType.prototype.fields.each(function(c) {
12697             switch( c.type) {
12698                 case 'int' : da[c.name] = 0; break;
12699                 case 'date' : da[c.name] = new Date(); break;
12700                 case 'float' : da[c.name] = 0.0; break;
12701                 case 'boolean' : da[c.name] = false; break;
12702                 default : da[c.name] = ""; break;
12703             }
12704             
12705         });
12706         return new this.recordType(Roo.apply(da, d));
12707     }
12708     
12709     
12710 };/*
12711  * Based on:
12712  * Ext JS Library 1.1.1
12713  * Copyright(c) 2006-2007, Ext JS, LLC.
12714  *
12715  * Originally Released Under LGPL - original licence link has changed is not relivant.
12716  *
12717  * Fork - LGPL
12718  * <script type="text/javascript">
12719  */
12720
12721 /**
12722  * @class Roo.data.DataProxy
12723  * @extends Roo.data.Observable
12724  * This class is an abstract base class for implementations which provide retrieval of
12725  * unformatted data objects.<br>
12726  * <p>
12727  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12728  * (of the appropriate type which knows how to parse the data object) to provide a block of
12729  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12730  * <p>
12731  * Custom implementations must implement the load method as described in
12732  * {@link Roo.data.HttpProxy#load}.
12733  */
12734 Roo.data.DataProxy = function(){
12735     this.addEvents({
12736         /**
12737          * @event beforeload
12738          * Fires before a network request is made to retrieve a data object.
12739          * @param {Object} This DataProxy object.
12740          * @param {Object} params The params parameter to the load function.
12741          */
12742         beforeload : true,
12743         /**
12744          * @event load
12745          * Fires before the load method's callback is called.
12746          * @param {Object} This DataProxy object.
12747          * @param {Object} o The data object.
12748          * @param {Object} arg The callback argument object passed to the load function.
12749          */
12750         load : true,
12751         /**
12752          * @event loadexception
12753          * Fires if an Exception occurs during data retrieval.
12754          * @param {Object} This DataProxy object.
12755          * @param {Object} o The data object.
12756          * @param {Object} arg The callback argument object passed to the load function.
12757          * @param {Object} e The Exception.
12758          */
12759         loadexception : true
12760     });
12761     Roo.data.DataProxy.superclass.constructor.call(this);
12762 };
12763
12764 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12765
12766     /**
12767      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12768      */
12769 /*
12770  * Based on:
12771  * Ext JS Library 1.1.1
12772  * Copyright(c) 2006-2007, Ext JS, LLC.
12773  *
12774  * Originally Released Under LGPL - original licence link has changed is not relivant.
12775  *
12776  * Fork - LGPL
12777  * <script type="text/javascript">
12778  */
12779 /**
12780  * @class Roo.data.MemoryProxy
12781  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12782  * to the Reader when its load method is called.
12783  * @constructor
12784  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12785  */
12786 Roo.data.MemoryProxy = function(data){
12787     if (data.data) {
12788         data = data.data;
12789     }
12790     Roo.data.MemoryProxy.superclass.constructor.call(this);
12791     this.data = data;
12792 };
12793
12794 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12795     
12796     /**
12797      * Load data from the requested source (in this case an in-memory
12798      * data object passed to the constructor), read the data object into
12799      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12800      * process that block using the passed callback.
12801      * @param {Object} params This parameter is not used by the MemoryProxy class.
12802      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12803      * object into a block of Roo.data.Records.
12804      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12805      * The function must be passed <ul>
12806      * <li>The Record block object</li>
12807      * <li>The "arg" argument from the load function</li>
12808      * <li>A boolean success indicator</li>
12809      * </ul>
12810      * @param {Object} scope The scope in which to call the callback
12811      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12812      */
12813     load : function(params, reader, callback, scope, arg){
12814         params = params || {};
12815         var result;
12816         try {
12817             result = reader.readRecords(params.data ? params.data :this.data);
12818         }catch(e){
12819             this.fireEvent("loadexception", this, arg, null, e);
12820             callback.call(scope, null, arg, false);
12821             return;
12822         }
12823         callback.call(scope, result, arg, true);
12824     },
12825     
12826     // private
12827     update : function(params, records){
12828         
12829     }
12830 });/*
12831  * Based on:
12832  * Ext JS Library 1.1.1
12833  * Copyright(c) 2006-2007, Ext JS, LLC.
12834  *
12835  * Originally Released Under LGPL - original licence link has changed is not relivant.
12836  *
12837  * Fork - LGPL
12838  * <script type="text/javascript">
12839  */
12840 /**
12841  * @class Roo.data.HttpProxy
12842  * @extends Roo.data.DataProxy
12843  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12844  * configured to reference a certain URL.<br><br>
12845  * <p>
12846  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12847  * from which the running page was served.<br><br>
12848  * <p>
12849  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12850  * <p>
12851  * Be aware that to enable the browser to parse an XML document, the server must set
12852  * the Content-Type header in the HTTP response to "text/xml".
12853  * @constructor
12854  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12855  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12856  * will be used to make the request.
12857  */
12858 Roo.data.HttpProxy = function(conn){
12859     Roo.data.HttpProxy.superclass.constructor.call(this);
12860     // is conn a conn config or a real conn?
12861     this.conn = conn;
12862     this.useAjax = !conn || !conn.events;
12863   
12864 };
12865
12866 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12867     // thse are take from connection...
12868     
12869     /**
12870      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12871      */
12872     /**
12873      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12874      * extra parameters to each request made by this object. (defaults to undefined)
12875      */
12876     /**
12877      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12878      *  to each request made by this object. (defaults to undefined)
12879      */
12880     /**
12881      * @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)
12882      */
12883     /**
12884      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12885      */
12886      /**
12887      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12888      * @type Boolean
12889      */
12890   
12891
12892     /**
12893      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12894      * @type Boolean
12895      */
12896     /**
12897      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12898      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12899      * a finer-grained basis than the DataProxy events.
12900      */
12901     getConnection : function(){
12902         return this.useAjax ? Roo.Ajax : this.conn;
12903     },
12904
12905     /**
12906      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12907      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12908      * process that block using the passed callback.
12909      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12910      * for the request to the remote server.
12911      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12912      * object into a block of Roo.data.Records.
12913      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12914      * The function must be passed <ul>
12915      * <li>The Record block object</li>
12916      * <li>The "arg" argument from the load function</li>
12917      * <li>A boolean success indicator</li>
12918      * </ul>
12919      * @param {Object} scope The scope in which to call the callback
12920      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12921      */
12922     load : function(params, reader, callback, scope, arg){
12923         if(this.fireEvent("beforeload", this, params) !== false){
12924             var  o = {
12925                 params : params || {},
12926                 request: {
12927                     callback : callback,
12928                     scope : scope,
12929                     arg : arg
12930                 },
12931                 reader: reader,
12932                 callback : this.loadResponse,
12933                 scope: this
12934             };
12935             if(this.useAjax){
12936                 Roo.applyIf(o, this.conn);
12937                 if(this.activeRequest){
12938                     Roo.Ajax.abort(this.activeRequest);
12939                 }
12940                 this.activeRequest = Roo.Ajax.request(o);
12941             }else{
12942                 this.conn.request(o);
12943             }
12944         }else{
12945             callback.call(scope||this, null, arg, false);
12946         }
12947     },
12948
12949     // private
12950     loadResponse : function(o, success, response){
12951         delete this.activeRequest;
12952         if(!success){
12953             this.fireEvent("loadexception", this, o, response);
12954             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12955             return;
12956         }
12957         var result;
12958         try {
12959             result = o.reader.read(response);
12960         }catch(e){
12961             this.fireEvent("loadexception", this, o, response, e);
12962             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12963             return;
12964         }
12965         
12966         this.fireEvent("load", this, o, o.request.arg);
12967         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12968     },
12969
12970     // private
12971     update : function(dataSet){
12972
12973     },
12974
12975     // private
12976     updateResponse : function(dataSet){
12977
12978     }
12979 });/*
12980  * Based on:
12981  * Ext JS Library 1.1.1
12982  * Copyright(c) 2006-2007, Ext JS, LLC.
12983  *
12984  * Originally Released Under LGPL - original licence link has changed is not relivant.
12985  *
12986  * Fork - LGPL
12987  * <script type="text/javascript">
12988  */
12989
12990 /**
12991  * @class Roo.data.ScriptTagProxy
12992  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12993  * other than the originating domain of the running page.<br><br>
12994  * <p>
12995  * <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
12996  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12997  * <p>
12998  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12999  * source code that is used as the source inside a &lt;script> tag.<br><br>
13000  * <p>
13001  * In order for the browser to process the returned data, the server must wrap the data object
13002  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
13003  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
13004  * depending on whether the callback name was passed:
13005  * <p>
13006  * <pre><code>
13007 boolean scriptTag = false;
13008 String cb = request.getParameter("callback");
13009 if (cb != null) {
13010     scriptTag = true;
13011     response.setContentType("text/javascript");
13012 } else {
13013     response.setContentType("application/x-json");
13014 }
13015 Writer out = response.getWriter();
13016 if (scriptTag) {
13017     out.write(cb + "(");
13018 }
13019 out.print(dataBlock.toJsonString());
13020 if (scriptTag) {
13021     out.write(");");
13022 }
13023 </pre></code>
13024  *
13025  * @constructor
13026  * @param {Object} config A configuration object.
13027  */
13028 Roo.data.ScriptTagProxy = function(config){
13029     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
13030     Roo.apply(this, config);
13031     this.head = document.getElementsByTagName("head")[0];
13032 };
13033
13034 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
13035
13036 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
13037     /**
13038      * @cfg {String} url The URL from which to request the data object.
13039      */
13040     /**
13041      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
13042      */
13043     timeout : 30000,
13044     /**
13045      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
13046      * the server the name of the callback function set up by the load call to process the returned data object.
13047      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
13048      * javascript output which calls this named function passing the data object as its only parameter.
13049      */
13050     callbackParam : "callback",
13051     /**
13052      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
13053      * name to the request.
13054      */
13055     nocache : true,
13056
13057     /**
13058      * Load data from the configured URL, read the data object into
13059      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13060      * process that block using the passed callback.
13061      * @param {Object} params An object containing properties which are to be used as HTTP parameters
13062      * for the request to the remote server.
13063      * @param {Roo.data.DataReader} reader The Reader object which converts the data
13064      * object into a block of Roo.data.Records.
13065      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
13066      * The function must be passed <ul>
13067      * <li>The Record block object</li>
13068      * <li>The "arg" argument from the load function</li>
13069      * <li>A boolean success indicator</li>
13070      * </ul>
13071      * @param {Object} scope The scope in which to call the callback
13072      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13073      */
13074     load : function(params, reader, callback, scope, arg){
13075         if(this.fireEvent("beforeload", this, params) !== false){
13076
13077             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
13078
13079             var url = this.url;
13080             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
13081             if(this.nocache){
13082                 url += "&_dc=" + (new Date().getTime());
13083             }
13084             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
13085             var trans = {
13086                 id : transId,
13087                 cb : "stcCallback"+transId,
13088                 scriptId : "stcScript"+transId,
13089                 params : params,
13090                 arg : arg,
13091                 url : url,
13092                 callback : callback,
13093                 scope : scope,
13094                 reader : reader
13095             };
13096             var conn = this;
13097
13098             window[trans.cb] = function(o){
13099                 conn.handleResponse(o, trans);
13100             };
13101
13102             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
13103
13104             if(this.autoAbort !== false){
13105                 this.abort();
13106             }
13107
13108             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
13109
13110             var script = document.createElement("script");
13111             script.setAttribute("src", url);
13112             script.setAttribute("type", "text/javascript");
13113             script.setAttribute("id", trans.scriptId);
13114             this.head.appendChild(script);
13115
13116             this.trans = trans;
13117         }else{
13118             callback.call(scope||this, null, arg, false);
13119         }
13120     },
13121
13122     // private
13123     isLoading : function(){
13124         return this.trans ? true : false;
13125     },
13126
13127     /**
13128      * Abort the current server request.
13129      */
13130     abort : function(){
13131         if(this.isLoading()){
13132             this.destroyTrans(this.trans);
13133         }
13134     },
13135
13136     // private
13137     destroyTrans : function(trans, isLoaded){
13138         this.head.removeChild(document.getElementById(trans.scriptId));
13139         clearTimeout(trans.timeoutId);
13140         if(isLoaded){
13141             window[trans.cb] = undefined;
13142             try{
13143                 delete window[trans.cb];
13144             }catch(e){}
13145         }else{
13146             // if hasn't been loaded, wait for load to remove it to prevent script error
13147             window[trans.cb] = function(){
13148                 window[trans.cb] = undefined;
13149                 try{
13150                     delete window[trans.cb];
13151                 }catch(e){}
13152             };
13153         }
13154     },
13155
13156     // private
13157     handleResponse : function(o, trans){
13158         this.trans = false;
13159         this.destroyTrans(trans, true);
13160         var result;
13161         try {
13162             result = trans.reader.readRecords(o);
13163         }catch(e){
13164             this.fireEvent("loadexception", this, o, trans.arg, e);
13165             trans.callback.call(trans.scope||window, null, trans.arg, false);
13166             return;
13167         }
13168         this.fireEvent("load", this, o, trans.arg);
13169         trans.callback.call(trans.scope||window, result, trans.arg, true);
13170     },
13171
13172     // private
13173     handleFailure : function(trans){
13174         this.trans = false;
13175         this.destroyTrans(trans, false);
13176         this.fireEvent("loadexception", this, null, trans.arg);
13177         trans.callback.call(trans.scope||window, null, trans.arg, false);
13178     }
13179 });/*
13180  * Based on:
13181  * Ext JS Library 1.1.1
13182  * Copyright(c) 2006-2007, Ext JS, LLC.
13183  *
13184  * Originally Released Under LGPL - original licence link has changed is not relivant.
13185  *
13186  * Fork - LGPL
13187  * <script type="text/javascript">
13188  */
13189
13190 /**
13191  * @class Roo.data.JsonReader
13192  * @extends Roo.data.DataReader
13193  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
13194  * based on mappings in a provided Roo.data.Record constructor.
13195  * 
13196  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
13197  * in the reply previously. 
13198  * 
13199  * <p>
13200  * Example code:
13201  * <pre><code>
13202 var RecordDef = Roo.data.Record.create([
13203     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
13204     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
13205 ]);
13206 var myReader = new Roo.data.JsonReader({
13207     totalProperty: "results",    // The property which contains the total dataset size (optional)
13208     root: "rows",                // The property which contains an Array of row objects
13209     id: "id"                     // The property within each row object that provides an ID for the record (optional)
13210 }, RecordDef);
13211 </code></pre>
13212  * <p>
13213  * This would consume a JSON file like this:
13214  * <pre><code>
13215 { 'results': 2, 'rows': [
13216     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
13217     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
13218 }
13219 </code></pre>
13220  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
13221  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
13222  * paged from the remote server.
13223  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
13224  * @cfg {String} root name of the property which contains the Array of row objects.
13225  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13226  * @cfg {Array} fields Array of field definition objects
13227  * @constructor
13228  * Create a new JsonReader
13229  * @param {Object} meta Metadata configuration options
13230  * @param {Object} recordType Either an Array of field definition objects,
13231  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
13232  */
13233 Roo.data.JsonReader = function(meta, recordType){
13234     
13235     meta = meta || {};
13236     // set some defaults:
13237     Roo.applyIf(meta, {
13238         totalProperty: 'total',
13239         successProperty : 'success',
13240         root : 'data',
13241         id : 'id'
13242     });
13243     
13244     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13245 };
13246 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
13247     
13248     readerType : 'Json',
13249     
13250     /**
13251      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
13252      * Used by Store query builder to append _requestMeta to params.
13253      * 
13254      */
13255     metaFromRemote : false,
13256     /**
13257      * This method is only used by a DataProxy which has retrieved data from a remote server.
13258      * @param {Object} response The XHR object which contains the JSON data in its responseText.
13259      * @return {Object} data A data block which is used by an Roo.data.Store object as
13260      * a cache of Roo.data.Records.
13261      */
13262     read : function(response){
13263         var json = response.responseText;
13264        
13265         var o = /* eval:var:o */ eval("("+json+")");
13266         if(!o) {
13267             throw {message: "JsonReader.read: Json object not found"};
13268         }
13269         
13270         if(o.metaData){
13271             
13272             delete this.ef;
13273             this.metaFromRemote = true;
13274             this.meta = o.metaData;
13275             this.recordType = Roo.data.Record.create(o.metaData.fields);
13276             this.onMetaChange(this.meta, this.recordType, o);
13277         }
13278         return this.readRecords(o);
13279     },
13280
13281     // private function a store will implement
13282     onMetaChange : function(meta, recordType, o){
13283
13284     },
13285
13286     /**
13287          * @ignore
13288          */
13289     simpleAccess: function(obj, subsc) {
13290         return obj[subsc];
13291     },
13292
13293         /**
13294          * @ignore
13295          */
13296     getJsonAccessor: function(){
13297         var re = /[\[\.]/;
13298         return function(expr) {
13299             try {
13300                 return(re.test(expr))
13301                     ? new Function("obj", "return obj." + expr)
13302                     : function(obj){
13303                         return obj[expr];
13304                     };
13305             } catch(e){}
13306             return Roo.emptyFn;
13307         };
13308     }(),
13309
13310     /**
13311      * Create a data block containing Roo.data.Records from an XML document.
13312      * @param {Object} o An object which contains an Array of row objects in the property specified
13313      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
13314      * which contains the total size of the dataset.
13315      * @return {Object} data A data block which is used by an Roo.data.Store object as
13316      * a cache of Roo.data.Records.
13317      */
13318     readRecords : function(o){
13319         /**
13320          * After any data loads, the raw JSON data is available for further custom processing.
13321          * @type Object
13322          */
13323         this.o = o;
13324         var s = this.meta, Record = this.recordType,
13325             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
13326
13327 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
13328         if (!this.ef) {
13329             if(s.totalProperty) {
13330                     this.getTotal = this.getJsonAccessor(s.totalProperty);
13331                 }
13332                 if(s.successProperty) {
13333                     this.getSuccess = this.getJsonAccessor(s.successProperty);
13334                 }
13335                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
13336                 if (s.id) {
13337                         var g = this.getJsonAccessor(s.id);
13338                         this.getId = function(rec) {
13339                                 var r = g(rec);  
13340                                 return (r === undefined || r === "") ? null : r;
13341                         };
13342                 } else {
13343                         this.getId = function(){return null;};
13344                 }
13345             this.ef = [];
13346             for(var jj = 0; jj < fl; jj++){
13347                 f = fi[jj];
13348                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
13349                 this.ef[jj] = this.getJsonAccessor(map);
13350             }
13351         }
13352
13353         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
13354         if(s.totalProperty){
13355             var vt = parseInt(this.getTotal(o), 10);
13356             if(!isNaN(vt)){
13357                 totalRecords = vt;
13358             }
13359         }
13360         if(s.successProperty){
13361             var vs = this.getSuccess(o);
13362             if(vs === false || vs === 'false'){
13363                 success = false;
13364             }
13365         }
13366         var records = [];
13367         for(var i = 0; i < c; i++){
13368                 var n = root[i];
13369             var values = {};
13370             var id = this.getId(n);
13371             for(var j = 0; j < fl; j++){
13372                 f = fi[j];
13373             var v = this.ef[j](n);
13374             if (!f.convert) {
13375                 Roo.log('missing convert for ' + f.name);
13376                 Roo.log(f);
13377                 continue;
13378             }
13379             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13380             }
13381             var record = new Record(values, id);
13382             record.json = n;
13383             records[i] = record;
13384         }
13385         return {
13386             raw : o,
13387             success : success,
13388             records : records,
13389             totalRecords : totalRecords
13390         };
13391     },
13392     // used when loading children.. @see loadDataFromChildren
13393     toLoadData: function(rec)
13394     {
13395         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13396         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13397         return { data : data, total : data.length };
13398         
13399     }
13400 });/*
13401  * Based on:
13402  * Ext JS Library 1.1.1
13403  * Copyright(c) 2006-2007, Ext JS, LLC.
13404  *
13405  * Originally Released Under LGPL - original licence link has changed is not relivant.
13406  *
13407  * Fork - LGPL
13408  * <script type="text/javascript">
13409  */
13410
13411 /**
13412  * @class Roo.data.ArrayReader
13413  * @extends Roo.data.DataReader
13414  * Data reader class to create an Array of Roo.data.Record objects from an Array.
13415  * Each element of that Array represents a row of data fields. The
13416  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13417  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13418  * <p>
13419  * Example code:.
13420  * <pre><code>
13421 var RecordDef = Roo.data.Record.create([
13422     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
13423     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
13424 ]);
13425 var myReader = new Roo.data.ArrayReader({
13426     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
13427 }, RecordDef);
13428 </code></pre>
13429  * <p>
13430  * This would consume an Array like this:
13431  * <pre><code>
13432 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13433   </code></pre>
13434  
13435  * @constructor
13436  * Create a new JsonReader
13437  * @param {Object} meta Metadata configuration options.
13438  * @param {Object|Array} recordType Either an Array of field definition objects
13439  * 
13440  * @cfg {Array} fields Array of field definition objects
13441  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13442  * as specified to {@link Roo.data.Record#create},
13443  * or an {@link Roo.data.Record} object
13444  *
13445  * 
13446  * created using {@link Roo.data.Record#create}.
13447  */
13448 Roo.data.ArrayReader = function(meta, recordType)
13449 {    
13450     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13451 };
13452
13453 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13454     
13455       /**
13456      * Create a data block containing Roo.data.Records from an XML document.
13457      * @param {Object} o An Array of row objects which represents the dataset.
13458      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13459      * a cache of Roo.data.Records.
13460      */
13461     readRecords : function(o)
13462     {
13463         var sid = this.meta ? this.meta.id : null;
13464         var recordType = this.recordType, fields = recordType.prototype.fields;
13465         var records = [];
13466         var root = o;
13467         for(var i = 0; i < root.length; i++){
13468                 var n = root[i];
13469             var values = {};
13470             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13471             for(var j = 0, jlen = fields.length; j < jlen; j++){
13472                 var f = fields.items[j];
13473                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13474                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13475                 v = f.convert(v);
13476                 values[f.name] = v;
13477             }
13478             var record = new recordType(values, id);
13479             record.json = n;
13480             records[records.length] = record;
13481         }
13482         return {
13483             records : records,
13484             totalRecords : records.length
13485         };
13486     },
13487     // used when loading children.. @see loadDataFromChildren
13488     toLoadData: function(rec)
13489     {
13490         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
13491         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
13492         
13493     }
13494     
13495     
13496 });/*
13497  * - LGPL
13498  * * 
13499  */
13500
13501 /**
13502  * @class Roo.bootstrap.ComboBox
13503  * @extends Roo.bootstrap.TriggerField
13504  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13505  * @cfg {Boolean} append (true|false) default false
13506  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13507  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13508  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13509  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13510  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13511  * @cfg {Boolean} animate default true
13512  * @cfg {Boolean} emptyResultText only for touch device
13513  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13514  * @cfg {String} emptyTitle default ''
13515  * @constructor
13516  * Create a new ComboBox.
13517  * @param {Object} config Configuration options
13518  */
13519 Roo.bootstrap.ComboBox = function(config){
13520     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13521     this.addEvents({
13522         /**
13523          * @event expand
13524          * Fires when the dropdown list is expanded
13525         * @param {Roo.bootstrap.ComboBox} combo This combo box
13526         */
13527         'expand' : true,
13528         /**
13529          * @event collapse
13530          * Fires when the dropdown list is collapsed
13531         * @param {Roo.bootstrap.ComboBox} combo This combo box
13532         */
13533         'collapse' : true,
13534         /**
13535          * @event beforeselect
13536          * Fires before a list item is selected. Return false to cancel the selection.
13537         * @param {Roo.bootstrap.ComboBox} combo This combo box
13538         * @param {Roo.data.Record} record The data record returned from the underlying store
13539         * @param {Number} index The index of the selected item in the dropdown list
13540         */
13541         'beforeselect' : true,
13542         /**
13543          * @event select
13544          * Fires when a list item is selected
13545         * @param {Roo.bootstrap.ComboBox} combo This combo box
13546         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13547         * @param {Number} index The index of the selected item in the dropdown list
13548         */
13549         'select' : true,
13550         /**
13551          * @event beforequery
13552          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13553          * The event object passed has these properties:
13554         * @param {Roo.bootstrap.ComboBox} combo This combo box
13555         * @param {String} query The query
13556         * @param {Boolean} forceAll true to force "all" query
13557         * @param {Boolean} cancel true to cancel the query
13558         * @param {Object} e The query event object
13559         */
13560         'beforequery': true,
13561          /**
13562          * @event add
13563          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13564         * @param {Roo.bootstrap.ComboBox} combo This combo box
13565         */
13566         'add' : true,
13567         /**
13568          * @event edit
13569          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13570         * @param {Roo.bootstrap.ComboBox} combo This combo box
13571         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13572         */
13573         'edit' : true,
13574         /**
13575          * @event remove
13576          * Fires when the remove value from the combobox array
13577         * @param {Roo.bootstrap.ComboBox} combo This combo box
13578         */
13579         'remove' : true,
13580         /**
13581          * @event afterremove
13582          * Fires when the remove value from the combobox array
13583         * @param {Roo.bootstrap.ComboBox} combo This combo box
13584         */
13585         'afterremove' : true,
13586         /**
13587          * @event specialfilter
13588          * Fires when specialfilter
13589             * @param {Roo.bootstrap.ComboBox} combo This combo box
13590             */
13591         'specialfilter' : true,
13592         /**
13593          * @event tick
13594          * Fires when tick the element
13595             * @param {Roo.bootstrap.ComboBox} combo This combo box
13596             */
13597         'tick' : true,
13598         /**
13599          * @event touchviewdisplay
13600          * Fires when touch view require special display (default is using displayField)
13601             * @param {Roo.bootstrap.ComboBox} combo This combo box
13602             * @param {Object} cfg set html .
13603             */
13604         'touchviewdisplay' : true
13605         
13606     });
13607     
13608     this.item = [];
13609     this.tickItems = [];
13610     
13611     this.selectedIndex = -1;
13612     if(this.mode == 'local'){
13613         if(config.queryDelay === undefined){
13614             this.queryDelay = 10;
13615         }
13616         if(config.minChars === undefined){
13617             this.minChars = 0;
13618         }
13619     }
13620 };
13621
13622 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13623      
13624     /**
13625      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13626      * rendering into an Roo.Editor, defaults to false)
13627      */
13628     /**
13629      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13630      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13631      */
13632     /**
13633      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13634      */
13635     /**
13636      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13637      * the dropdown list (defaults to undefined, with no header element)
13638      */
13639
13640      /**
13641      * @cfg {String/Roo.Template} tpl The template to use to render the output
13642      */
13643      
13644      /**
13645      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13646      */
13647     listWidth: undefined,
13648     /**
13649      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13650      * mode = 'remote' or 'text' if mode = 'local')
13651      */
13652     displayField: undefined,
13653     
13654     /**
13655      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13656      * mode = 'remote' or 'value' if mode = 'local'). 
13657      * Note: use of a valueField requires the user make a selection
13658      * in order for a value to be mapped.
13659      */
13660     valueField: undefined,
13661     /**
13662      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13663      */
13664     modalTitle : '',
13665     
13666     /**
13667      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13668      * field's data value (defaults to the underlying DOM element's name)
13669      */
13670     hiddenName: undefined,
13671     /**
13672      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13673      */
13674     listClass: '',
13675     /**
13676      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13677      */
13678     selectedClass: 'active',
13679     
13680     /**
13681      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13682      */
13683     shadow:'sides',
13684     /**
13685      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13686      * anchor positions (defaults to 'tl-bl')
13687      */
13688     listAlign: 'tl-bl?',
13689     /**
13690      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13691      */
13692     maxHeight: 300,
13693     /**
13694      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13695      * query specified by the allQuery config option (defaults to 'query')
13696      */
13697     triggerAction: 'query',
13698     /**
13699      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13700      * (defaults to 4, does not apply if editable = false)
13701      */
13702     minChars : 4,
13703     /**
13704      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13705      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13706      */
13707     typeAhead: false,
13708     /**
13709      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13710      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13711      */
13712     queryDelay: 500,
13713     /**
13714      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13715      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13716      */
13717     pageSize: 0,
13718     /**
13719      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13720      * when editable = true (defaults to false)
13721      */
13722     selectOnFocus:false,
13723     /**
13724      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13725      */
13726     queryParam: 'query',
13727     /**
13728      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13729      * when mode = 'remote' (defaults to 'Loading...')
13730      */
13731     loadingText: 'Loading...',
13732     /**
13733      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13734      */
13735     resizable: false,
13736     /**
13737      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13738      */
13739     handleHeight : 8,
13740     /**
13741      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13742      * traditional select (defaults to true)
13743      */
13744     editable: true,
13745     /**
13746      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13747      */
13748     allQuery: '',
13749     /**
13750      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13751      */
13752     mode: 'remote',
13753     /**
13754      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13755      * listWidth has a higher value)
13756      */
13757     minListWidth : 70,
13758     /**
13759      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13760      * allow the user to set arbitrary text into the field (defaults to false)
13761      */
13762     forceSelection:false,
13763     /**
13764      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13765      * if typeAhead = true (defaults to 250)
13766      */
13767     typeAheadDelay : 250,
13768     /**
13769      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13770      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13771      */
13772     valueNotFoundText : undefined,
13773     /**
13774      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13775      */
13776     blockFocus : false,
13777     
13778     /**
13779      * @cfg {Boolean} disableClear Disable showing of clear button.
13780      */
13781     disableClear : false,
13782     /**
13783      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13784      */
13785     alwaysQuery : false,
13786     
13787     /**
13788      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13789      */
13790     multiple : false,
13791     
13792     /**
13793      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13794      */
13795     invalidClass : "has-warning",
13796     
13797     /**
13798      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13799      */
13800     validClass : "has-success",
13801     
13802     /**
13803      * @cfg {Boolean} specialFilter (true|false) special filter default false
13804      */
13805     specialFilter : false,
13806     
13807     /**
13808      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13809      */
13810     mobileTouchView : true,
13811     
13812     /**
13813      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13814      */
13815     useNativeIOS : false,
13816     
13817     /**
13818      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13819      */
13820     mobile_restrict_height : false,
13821     
13822     ios_options : false,
13823     
13824     //private
13825     addicon : false,
13826     editicon: false,
13827     
13828     page: 0,
13829     hasQuery: false,
13830     append: false,
13831     loadNext: false,
13832     autoFocus : true,
13833     tickable : false,
13834     btnPosition : 'right',
13835     triggerList : true,
13836     showToggleBtn : true,
13837     animate : true,
13838     emptyResultText: 'Empty',
13839     triggerText : 'Select',
13840     emptyTitle : '',
13841     
13842     // element that contains real text value.. (when hidden is used..)
13843     
13844     getAutoCreate : function()
13845     {   
13846         var cfg = false;
13847         //render
13848         /*
13849          * Render classic select for iso
13850          */
13851         
13852         if(Roo.isIOS && this.useNativeIOS){
13853             cfg = this.getAutoCreateNativeIOS();
13854             return cfg;
13855         }
13856         
13857         /*
13858          * Touch Devices
13859          */
13860         
13861         if(Roo.isTouch && this.mobileTouchView){
13862             cfg = this.getAutoCreateTouchView();
13863             return cfg;;
13864         }
13865         
13866         /*
13867          *  Normal ComboBox
13868          */
13869         if(!this.tickable){
13870             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13871             return cfg;
13872         }
13873         
13874         /*
13875          *  ComboBox with tickable selections
13876          */
13877              
13878         var align = this.labelAlign || this.parentLabelAlign();
13879         
13880         cfg = {
13881             cls : 'form-group roo-combobox-tickable' //input-group
13882         };
13883         
13884         var btn_text_select = '';
13885         var btn_text_done = '';
13886         var btn_text_cancel = '';
13887         
13888         if (this.btn_text_show) {
13889             btn_text_select = 'Select';
13890             btn_text_done = 'Done';
13891             btn_text_cancel = 'Cancel'; 
13892         }
13893         
13894         var buttons = {
13895             tag : 'div',
13896             cls : 'tickable-buttons',
13897             cn : [
13898                 {
13899                     tag : 'button',
13900                     type : 'button',
13901                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13902                     //html : this.triggerText
13903                     html: btn_text_select
13904                 },
13905                 {
13906                     tag : 'button',
13907                     type : 'button',
13908                     name : 'ok',
13909                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13910                     //html : 'Done'
13911                     html: btn_text_done
13912                 },
13913                 {
13914                     tag : 'button',
13915                     type : 'button',
13916                     name : 'cancel',
13917                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13918                     //html : 'Cancel'
13919                     html: btn_text_cancel
13920                 }
13921             ]
13922         };
13923         
13924         if(this.editable){
13925             buttons.cn.unshift({
13926                 tag: 'input',
13927                 cls: 'roo-select2-search-field-input'
13928             });
13929         }
13930         
13931         var _this = this;
13932         
13933         Roo.each(buttons.cn, function(c){
13934             if (_this.size) {
13935                 c.cls += ' btn-' + _this.size;
13936             }
13937
13938             if (_this.disabled) {
13939                 c.disabled = true;
13940             }
13941         });
13942         
13943         var box = {
13944             tag: 'div',
13945             style : 'display: contents',
13946             cn: [
13947                 {
13948                     tag: 'input',
13949                     type : 'hidden',
13950                     cls: 'form-hidden-field'
13951                 },
13952                 {
13953                     tag: 'ul',
13954                     cls: 'roo-select2-choices',
13955                     cn:[
13956                         {
13957                             tag: 'li',
13958                             cls: 'roo-select2-search-field',
13959                             cn: [
13960                                 buttons
13961                             ]
13962                         }
13963                     ]
13964                 }
13965             ]
13966         };
13967         
13968         var combobox = {
13969             cls: 'roo-select2-container input-group roo-select2-container-multi',
13970             cn: [
13971                 
13972                 box
13973 //                {
13974 //                    tag: 'ul',
13975 //                    cls: 'typeahead typeahead-long dropdown-menu',
13976 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13977 //                }
13978             ]
13979         };
13980         
13981         if(this.hasFeedback && !this.allowBlank){
13982             
13983             var feedback = {
13984                 tag: 'span',
13985                 cls: 'glyphicon form-control-feedback'
13986             };
13987
13988             combobox.cn.push(feedback);
13989         }
13990         
13991         var indicator = {
13992             tag : 'i',
13993             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13994             tooltip : 'This field is required'
13995         };
13996         if (Roo.bootstrap.version == 4) {
13997             indicator = {
13998                 tag : 'i',
13999                 style : 'display:none'
14000             };
14001         }
14002         if (align ==='left' && this.fieldLabel.length) {
14003             
14004             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
14005             
14006             cfg.cn = [
14007                 indicator,
14008                 {
14009                     tag: 'label',
14010                     'for' :  id,
14011                     cls : 'control-label col-form-label',
14012                     html : this.fieldLabel
14013
14014                 },
14015                 {
14016                     cls : "", 
14017                     cn: [
14018                         combobox
14019                     ]
14020                 }
14021
14022             ];
14023             
14024             var labelCfg = cfg.cn[1];
14025             var contentCfg = cfg.cn[2];
14026             
14027
14028             if(this.indicatorpos == 'right'){
14029                 
14030                 cfg.cn = [
14031                     {
14032                         tag: 'label',
14033                         'for' :  id,
14034                         cls : 'control-label col-form-label',
14035                         cn : [
14036                             {
14037                                 tag : 'span',
14038                                 html : this.fieldLabel
14039                             },
14040                             indicator
14041                         ]
14042                     },
14043                     {
14044                         cls : "",
14045                         cn: [
14046                             combobox
14047                         ]
14048                     }
14049
14050                 ];
14051                 
14052                 
14053                 
14054                 labelCfg = cfg.cn[0];
14055                 contentCfg = cfg.cn[1];
14056             
14057             }
14058             
14059             if(this.labelWidth > 12){
14060                 labelCfg.style = "width: " + this.labelWidth + 'px';
14061             }
14062             
14063             if(this.labelWidth < 13 && this.labelmd == 0){
14064                 this.labelmd = this.labelWidth;
14065             }
14066             
14067             if(this.labellg > 0){
14068                 labelCfg.cls += ' col-lg-' + this.labellg;
14069                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14070             }
14071             
14072             if(this.labelmd > 0){
14073                 labelCfg.cls += ' col-md-' + this.labelmd;
14074                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14075             }
14076             
14077             if(this.labelsm > 0){
14078                 labelCfg.cls += ' col-sm-' + this.labelsm;
14079                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14080             }
14081             
14082             if(this.labelxs > 0){
14083                 labelCfg.cls += ' col-xs-' + this.labelxs;
14084                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14085             }
14086                 
14087                 
14088         } else if ( this.fieldLabel.length) {
14089 //                Roo.log(" label");
14090                  cfg.cn = [
14091                    indicator,
14092                     {
14093                         tag: 'label',
14094                         //cls : 'input-group-addon',
14095                         html : this.fieldLabel
14096                     },
14097                     combobox
14098                 ];
14099                 
14100                 if(this.indicatorpos == 'right'){
14101                     cfg.cn = [
14102                         {
14103                             tag: 'label',
14104                             //cls : 'input-group-addon',
14105                             html : this.fieldLabel
14106                         },
14107                         indicator,
14108                         combobox
14109                     ];
14110                     
14111                 }
14112
14113         } else {
14114             
14115 //                Roo.log(" no label && no align");
14116                 cfg = combobox
14117                      
14118                 
14119         }
14120          
14121         var settings=this;
14122         ['xs','sm','md','lg'].map(function(size){
14123             if (settings[size]) {
14124                 cfg.cls += ' col-' + size + '-' + settings[size];
14125             }
14126         });
14127         
14128         return cfg;
14129         
14130     },
14131     
14132     _initEventsCalled : false,
14133     
14134     // private
14135     initEvents: function()
14136     {   
14137         if (this._initEventsCalled) { // as we call render... prevent looping...
14138             return;
14139         }
14140         this._initEventsCalled = true;
14141         
14142         if (!this.store) {
14143             throw "can not find store for combo";
14144         }
14145         
14146         this.indicator = this.indicatorEl();
14147         
14148         this.store = Roo.factory(this.store, Roo.data);
14149         this.store.parent = this;
14150         
14151         // if we are building from html. then this element is so complex, that we can not really
14152         // use the rendered HTML.
14153         // so we have to trash and replace the previous code.
14154         if (Roo.XComponent.build_from_html) {
14155             // remove this element....
14156             var e = this.el.dom, k=0;
14157             while (e ) { e = e.previousSibling;  ++k;}
14158
14159             this.el.remove();
14160             
14161             this.el=false;
14162             this.rendered = false;
14163             
14164             this.render(this.parent().getChildContainer(true), k);
14165         }
14166         
14167         if(Roo.isIOS && this.useNativeIOS){
14168             this.initIOSView();
14169             return;
14170         }
14171         
14172         /*
14173          * Touch Devices
14174          */
14175         
14176         if(Roo.isTouch && this.mobileTouchView){
14177             this.initTouchView();
14178             return;
14179         }
14180         
14181         if(this.tickable){
14182             this.initTickableEvents();
14183             return;
14184         }
14185         
14186         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
14187         
14188         if(this.hiddenName){
14189             
14190             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14191             
14192             this.hiddenField.dom.value =
14193                 this.hiddenValue !== undefined ? this.hiddenValue :
14194                 this.value !== undefined ? this.value : '';
14195
14196             // prevent input submission
14197             this.el.dom.removeAttribute('name');
14198             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14199              
14200              
14201         }
14202         //if(Roo.isGecko){
14203         //    this.el.dom.setAttribute('autocomplete', 'off');
14204         //}
14205         
14206         var cls = 'x-combo-list';
14207         
14208         //this.list = new Roo.Layer({
14209         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
14210         //});
14211         
14212         var _this = this;
14213         
14214         (function(){
14215             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14216             _this.list.setWidth(lw);
14217         }).defer(100);
14218         
14219         this.list.on('mouseover', this.onViewOver, this);
14220         this.list.on('mousemove', this.onViewMove, this);
14221         this.list.on('scroll', this.onViewScroll, this);
14222         
14223         /*
14224         this.list.swallowEvent('mousewheel');
14225         this.assetHeight = 0;
14226
14227         if(this.title){
14228             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
14229             this.assetHeight += this.header.getHeight();
14230         }
14231
14232         this.innerList = this.list.createChild({cls:cls+'-inner'});
14233         this.innerList.on('mouseover', this.onViewOver, this);
14234         this.innerList.on('mousemove', this.onViewMove, this);
14235         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14236         
14237         if(this.allowBlank && !this.pageSize && !this.disableClear){
14238             this.footer = this.list.createChild({cls:cls+'-ft'});
14239             this.pageTb = new Roo.Toolbar(this.footer);
14240            
14241         }
14242         if(this.pageSize){
14243             this.footer = this.list.createChild({cls:cls+'-ft'});
14244             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
14245                     {pageSize: this.pageSize});
14246             
14247         }
14248         
14249         if (this.pageTb && this.allowBlank && !this.disableClear) {
14250             var _this = this;
14251             this.pageTb.add(new Roo.Toolbar.Fill(), {
14252                 cls: 'x-btn-icon x-btn-clear',
14253                 text: '&#160;',
14254                 handler: function()
14255                 {
14256                     _this.collapse();
14257                     _this.clearValue();
14258                     _this.onSelect(false, -1);
14259                 }
14260             });
14261         }
14262         if (this.footer) {
14263             this.assetHeight += this.footer.getHeight();
14264         }
14265         */
14266             
14267         if(!this.tpl){
14268             this.tpl = Roo.bootstrap.version == 4 ?
14269                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
14270                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
14271         }
14272
14273         this.view = new Roo.View(this.list, this.tpl, {
14274             singleSelect:true, store: this.store, selectedClass: this.selectedClass
14275         });
14276         //this.view.wrapEl.setDisplayed(false);
14277         this.view.on('click', this.onViewClick, this);
14278         
14279         
14280         this.store.on('beforeload', this.onBeforeLoad, this);
14281         this.store.on('load', this.onLoad, this);
14282         this.store.on('loadexception', this.onLoadException, this);
14283         /*
14284         if(this.resizable){
14285             this.resizer = new Roo.Resizable(this.list,  {
14286                pinned:true, handles:'se'
14287             });
14288             this.resizer.on('resize', function(r, w, h){
14289                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
14290                 this.listWidth = w;
14291                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
14292                 this.restrictHeight();
14293             }, this);
14294             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
14295         }
14296         */
14297         if(!this.editable){
14298             this.editable = true;
14299             this.setEditable(false);
14300         }
14301         
14302         /*
14303         
14304         if (typeof(this.events.add.listeners) != 'undefined') {
14305             
14306             this.addicon = this.wrap.createChild(
14307                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
14308        
14309             this.addicon.on('click', function(e) {
14310                 this.fireEvent('add', this);
14311             }, this);
14312         }
14313         if (typeof(this.events.edit.listeners) != 'undefined') {
14314             
14315             this.editicon = this.wrap.createChild(
14316                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
14317             if (this.addicon) {
14318                 this.editicon.setStyle('margin-left', '40px');
14319             }
14320             this.editicon.on('click', function(e) {
14321                 
14322                 // we fire even  if inothing is selected..
14323                 this.fireEvent('edit', this, this.lastData );
14324                 
14325             }, this);
14326         }
14327         */
14328         
14329         this.keyNav = new Roo.KeyNav(this.inputEl(), {
14330             "up" : function(e){
14331                 this.inKeyMode = true;
14332                 this.selectPrev();
14333             },
14334
14335             "down" : function(e){
14336                 if(!this.isExpanded()){
14337                     this.onTriggerClick();
14338                 }else{
14339                     this.inKeyMode = true;
14340                     this.selectNext();
14341                 }
14342             },
14343
14344             "enter" : function(e){
14345 //                this.onViewClick();
14346                 //return true;
14347                 this.collapse();
14348                 
14349                 if(this.fireEvent("specialkey", this, e)){
14350                     this.onViewClick(false);
14351                 }
14352                 
14353                 return true;
14354             },
14355
14356             "esc" : function(e){
14357                 this.collapse();
14358             },
14359
14360             "tab" : function(e){
14361                 this.collapse();
14362                 
14363                 if(this.fireEvent("specialkey", this, e)){
14364                     this.onViewClick(false);
14365                 }
14366                 
14367                 return true;
14368             },
14369
14370             scope : this,
14371
14372             doRelay : function(foo, bar, hname){
14373                 if(hname == 'down' || this.scope.isExpanded()){
14374                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14375                 }
14376                 return true;
14377             },
14378
14379             forceKeyDown: true
14380         });
14381         
14382         
14383         this.queryDelay = Math.max(this.queryDelay || 10,
14384                 this.mode == 'local' ? 10 : 250);
14385         
14386         
14387         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14388         
14389         if(this.typeAhead){
14390             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14391         }
14392         if(this.editable !== false){
14393             this.inputEl().on("keyup", this.onKeyUp, this);
14394         }
14395         if(this.forceSelection){
14396             this.inputEl().on('blur', this.doForce, this);
14397         }
14398         
14399         if(this.multiple){
14400             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14401             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14402         }
14403     },
14404     
14405     initTickableEvents: function()
14406     {   
14407         this.createList();
14408         
14409         if(this.hiddenName){
14410             
14411             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14412             
14413             this.hiddenField.dom.value =
14414                 this.hiddenValue !== undefined ? this.hiddenValue :
14415                 this.value !== undefined ? this.value : '';
14416
14417             // prevent input submission
14418             this.el.dom.removeAttribute('name');
14419             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14420              
14421              
14422         }
14423         
14424 //        this.list = this.el.select('ul.dropdown-menu',true).first();
14425         
14426         this.choices = this.el.select('ul.roo-select2-choices', true).first();
14427         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14428         if(this.triggerList){
14429             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14430         }
14431          
14432         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14433         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14434         
14435         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14436         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14437         
14438         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14439         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14440         
14441         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14442         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14443         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14444         
14445         this.okBtn.hide();
14446         this.cancelBtn.hide();
14447         
14448         var _this = this;
14449         
14450         (function(){
14451             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14452             _this.list.setWidth(lw);
14453         }).defer(100);
14454         
14455         this.list.on('mouseover', this.onViewOver, this);
14456         this.list.on('mousemove', this.onViewMove, this);
14457         
14458         this.list.on('scroll', this.onViewScroll, this);
14459         
14460         if(!this.tpl){
14461             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
14462                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14463         }
14464
14465         this.view = new Roo.View(this.list, this.tpl, {
14466             singleSelect:true,
14467             tickable:true,
14468             parent:this,
14469             store: this.store,
14470             selectedClass: this.selectedClass
14471         });
14472         
14473         //this.view.wrapEl.setDisplayed(false);
14474         this.view.on('click', this.onViewClick, this);
14475         
14476         
14477         
14478         this.store.on('beforeload', this.onBeforeLoad, this);
14479         this.store.on('load', this.onLoad, this);
14480         this.store.on('loadexception', this.onLoadException, this);
14481         
14482         if(this.editable){
14483             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14484                 "up" : function(e){
14485                     this.inKeyMode = true;
14486                     this.selectPrev();
14487                 },
14488
14489                 "down" : function(e){
14490                     this.inKeyMode = true;
14491                     this.selectNext();
14492                 },
14493
14494                 "enter" : function(e){
14495                     if(this.fireEvent("specialkey", this, e)){
14496                         this.onViewClick(false);
14497                     }
14498                     
14499                     return true;
14500                 },
14501
14502                 "esc" : function(e){
14503                     this.onTickableFooterButtonClick(e, false, false);
14504                 },
14505
14506                 "tab" : function(e){
14507                     this.fireEvent("specialkey", this, e);
14508                     
14509                     this.onTickableFooterButtonClick(e, false, false);
14510                     
14511                     return true;
14512                 },
14513
14514                 scope : this,
14515
14516                 doRelay : function(e, fn, key){
14517                     if(this.scope.isExpanded()){
14518                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14519                     }
14520                     return true;
14521                 },
14522
14523                 forceKeyDown: true
14524             });
14525         }
14526         
14527         this.queryDelay = Math.max(this.queryDelay || 10,
14528                 this.mode == 'local' ? 10 : 250);
14529         
14530         
14531         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14532         
14533         if(this.typeAhead){
14534             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14535         }
14536         
14537         if(this.editable !== false){
14538             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14539         }
14540         
14541         this.indicator = this.indicatorEl();
14542         
14543         if(this.indicator){
14544             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14545             this.indicator.hide();
14546         }
14547         
14548     },
14549
14550     onDestroy : function(){
14551         if(this.view){
14552             this.view.setStore(null);
14553             this.view.el.removeAllListeners();
14554             this.view.el.remove();
14555             this.view.purgeListeners();
14556         }
14557         if(this.list){
14558             this.list.dom.innerHTML  = '';
14559         }
14560         
14561         if(this.store){
14562             this.store.un('beforeload', this.onBeforeLoad, this);
14563             this.store.un('load', this.onLoad, this);
14564             this.store.un('loadexception', this.onLoadException, this);
14565         }
14566         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14567     },
14568
14569     // private
14570     fireKey : function(e){
14571         if(e.isNavKeyPress() && !this.list.isVisible()){
14572             this.fireEvent("specialkey", this, e);
14573         }
14574     },
14575
14576     // private
14577     onResize: function(w, h){
14578 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14579 //        
14580 //        if(typeof w != 'number'){
14581 //            // we do not handle it!?!?
14582 //            return;
14583 //        }
14584 //        var tw = this.trigger.getWidth();
14585 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14586 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14587 //        var x = w - tw;
14588 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14589 //            
14590 //        //this.trigger.setStyle('left', x+'px');
14591 //        
14592 //        if(this.list && this.listWidth === undefined){
14593 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14594 //            this.list.setWidth(lw);
14595 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14596 //        }
14597         
14598     
14599         
14600     },
14601
14602     /**
14603      * Allow or prevent the user from directly editing the field text.  If false is passed,
14604      * the user will only be able to select from the items defined in the dropdown list.  This method
14605      * is the runtime equivalent of setting the 'editable' config option at config time.
14606      * @param {Boolean} value True to allow the user to directly edit the field text
14607      */
14608     setEditable : function(value){
14609         if(value == this.editable){
14610             return;
14611         }
14612         this.editable = value;
14613         if(!value){
14614             this.inputEl().dom.setAttribute('readOnly', true);
14615             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14616             this.inputEl().addClass('x-combo-noedit');
14617         }else{
14618             this.inputEl().dom.setAttribute('readOnly', false);
14619             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14620             this.inputEl().removeClass('x-combo-noedit');
14621         }
14622     },
14623
14624     // private
14625     
14626     onBeforeLoad : function(combo,opts){
14627         if(!this.hasFocus){
14628             return;
14629         }
14630          if (!opts.add) {
14631             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14632          }
14633         this.restrictHeight();
14634         this.selectedIndex = -1;
14635     },
14636
14637     // private
14638     onLoad : function(){
14639         
14640         this.hasQuery = false;
14641         
14642         if(!this.hasFocus){
14643             return;
14644         }
14645         
14646         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14647             this.loading.hide();
14648         }
14649         
14650         if(this.store.getCount() > 0){
14651             
14652             this.expand();
14653             this.restrictHeight();
14654             if(this.lastQuery == this.allQuery){
14655                 if(this.editable && !this.tickable){
14656                     this.inputEl().dom.select();
14657                 }
14658                 
14659                 if(
14660                     !this.selectByValue(this.value, true) &&
14661                     this.autoFocus && 
14662                     (
14663                         !this.store.lastOptions ||
14664                         typeof(this.store.lastOptions.add) == 'undefined' || 
14665                         this.store.lastOptions.add != true
14666                     )
14667                 ){
14668                     this.select(0, true);
14669                 }
14670             }else{
14671                 if(this.autoFocus){
14672                     this.selectNext();
14673                 }
14674                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14675                     this.taTask.delay(this.typeAheadDelay);
14676                 }
14677             }
14678         }else{
14679             this.onEmptyResults();
14680         }
14681         
14682         //this.el.focus();
14683     },
14684     // private
14685     onLoadException : function()
14686     {
14687         this.hasQuery = false;
14688         
14689         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14690             this.loading.hide();
14691         }
14692         
14693         if(this.tickable && this.editable){
14694             return;
14695         }
14696         
14697         this.collapse();
14698         // only causes errors at present
14699         //Roo.log(this.store.reader.jsonData);
14700         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14701             // fixme
14702             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14703         //}
14704         
14705         
14706     },
14707     // private
14708     onTypeAhead : function(){
14709         if(this.store.getCount() > 0){
14710             var r = this.store.getAt(0);
14711             var newValue = r.data[this.displayField];
14712             var len = newValue.length;
14713             var selStart = this.getRawValue().length;
14714             
14715             if(selStart != len){
14716                 this.setRawValue(newValue);
14717                 this.selectText(selStart, newValue.length);
14718             }
14719         }
14720     },
14721
14722     // private
14723     onSelect : function(record, index){
14724         
14725         if(this.fireEvent('beforeselect', this, record, index) !== false){
14726         
14727             this.setFromData(index > -1 ? record.data : false);
14728             
14729             this.collapse();
14730             this.fireEvent('select', this, record, index);
14731         }
14732     },
14733
14734     /**
14735      * Returns the currently selected field value or empty string if no value is set.
14736      * @return {String} value The selected value
14737      */
14738     getValue : function()
14739     {
14740         if(Roo.isIOS && this.useNativeIOS){
14741             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14742         }
14743         
14744         if(this.multiple){
14745             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14746         }
14747         
14748         if(this.valueField){
14749             return typeof this.value != 'undefined' ? this.value : '';
14750         }else{
14751             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14752         }
14753     },
14754     
14755     getRawValue : function()
14756     {
14757         if(Roo.isIOS && this.useNativeIOS){
14758             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14759         }
14760         
14761         var v = this.inputEl().getValue();
14762         
14763         return v;
14764     },
14765
14766     /**
14767      * Clears any text/value currently set in the field
14768      */
14769     clearValue : function(){
14770         
14771         if(this.hiddenField){
14772             this.hiddenField.dom.value = '';
14773         }
14774         this.value = '';
14775         this.setRawValue('');
14776         this.lastSelectionText = '';
14777         this.lastData = false;
14778         
14779         var close = this.closeTriggerEl();
14780         
14781         if(close){
14782             close.hide();
14783         }
14784         
14785         this.validate();
14786         
14787     },
14788
14789     /**
14790      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14791      * will be displayed in the field.  If the value does not match the data value of an existing item,
14792      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14793      * Otherwise the field will be blank (although the value will still be set).
14794      * @param {String} value The value to match
14795      */
14796     setValue : function(v)
14797     {
14798         if(Roo.isIOS && this.useNativeIOS){
14799             this.setIOSValue(v);
14800             return;
14801         }
14802         
14803         if(this.multiple){
14804             this.syncValue();
14805             return;
14806         }
14807         
14808         var text = v;
14809         if(this.valueField){
14810             var r = this.findRecord(this.valueField, v);
14811             if(r){
14812                 text = r.data[this.displayField];
14813             }else if(this.valueNotFoundText !== undefined){
14814                 text = this.valueNotFoundText;
14815             }
14816         }
14817         this.lastSelectionText = text;
14818         if(this.hiddenField){
14819             this.hiddenField.dom.value = v;
14820         }
14821         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14822         this.value = v;
14823         
14824         var close = this.closeTriggerEl();
14825         
14826         if(close){
14827             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14828         }
14829         
14830         this.validate();
14831     },
14832     /**
14833      * @property {Object} the last set data for the element
14834      */
14835     
14836     lastData : false,
14837     /**
14838      * Sets the value of the field based on a object which is related to the record format for the store.
14839      * @param {Object} value the value to set as. or false on reset?
14840      */
14841     setFromData : function(o){
14842         
14843         if(this.multiple){
14844             this.addItem(o);
14845             return;
14846         }
14847             
14848         var dv = ''; // display value
14849         var vv = ''; // value value..
14850         this.lastData = o;
14851         if (this.displayField) {
14852             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14853         } else {
14854             // this is an error condition!!!
14855             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14856         }
14857         
14858         if(this.valueField){
14859             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14860         }
14861         
14862         var close = this.closeTriggerEl();
14863         
14864         if(close){
14865             if(dv.length || vv * 1 > 0){
14866                 close.show() ;
14867                 this.blockFocus=true;
14868             } else {
14869                 close.hide();
14870             }             
14871         }
14872         
14873         if(this.hiddenField){
14874             this.hiddenField.dom.value = vv;
14875             
14876             this.lastSelectionText = dv;
14877             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14878             this.value = vv;
14879             return;
14880         }
14881         // no hidden field.. - we store the value in 'value', but still display
14882         // display field!!!!
14883         this.lastSelectionText = dv;
14884         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14885         this.value = vv;
14886         
14887         
14888         
14889     },
14890     // private
14891     reset : function(){
14892         // overridden so that last data is reset..
14893         
14894         if(this.multiple){
14895             this.clearItem();
14896             return;
14897         }
14898         
14899         this.setValue(this.originalValue);
14900         //this.clearInvalid();
14901         this.lastData = false;
14902         if (this.view) {
14903             this.view.clearSelections();
14904         }
14905         
14906         this.validate();
14907     },
14908     // private
14909     findRecord : function(prop, value){
14910         var record;
14911         if(this.store.getCount() > 0){
14912             this.store.each(function(r){
14913                 if(r.data[prop] == value){
14914                     record = r;
14915                     return false;
14916                 }
14917                 return true;
14918             });
14919         }
14920         return record;
14921     },
14922     
14923     getName: function()
14924     {
14925         // returns hidden if it's set..
14926         if (!this.rendered) {return ''};
14927         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14928         
14929     },
14930     // private
14931     onViewMove : function(e, t){
14932         this.inKeyMode = false;
14933     },
14934
14935     // private
14936     onViewOver : function(e, t){
14937         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14938             return;
14939         }
14940         var item = this.view.findItemFromChild(t);
14941         
14942         if(item){
14943             var index = this.view.indexOf(item);
14944             this.select(index, false);
14945         }
14946     },
14947
14948     // private
14949     onViewClick : function(view, doFocus, el, e)
14950     {
14951         var index = this.view.getSelectedIndexes()[0];
14952         
14953         var r = this.store.getAt(index);
14954         
14955         if(this.tickable){
14956             
14957             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14958                 return;
14959             }
14960             
14961             var rm = false;
14962             var _this = this;
14963             
14964             Roo.each(this.tickItems, function(v,k){
14965                 
14966                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14967                     Roo.log(v);
14968                     _this.tickItems.splice(k, 1);
14969                     
14970                     if(typeof(e) == 'undefined' && view == false){
14971                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14972                     }
14973                     
14974                     rm = true;
14975                     return;
14976                 }
14977             });
14978             
14979             if(rm){
14980                 return;
14981             }
14982             
14983             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14984                 this.tickItems.push(r.data);
14985             }
14986             
14987             if(typeof(e) == 'undefined' && view == false){
14988                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14989             }
14990                     
14991             return;
14992         }
14993         
14994         if(r){
14995             this.onSelect(r, index);
14996         }
14997         if(doFocus !== false && !this.blockFocus){
14998             this.inputEl().focus();
14999         }
15000     },
15001
15002     // private
15003     restrictHeight : function(){
15004         //this.innerList.dom.style.height = '';
15005         //var inner = this.innerList.dom;
15006         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
15007         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
15008         //this.list.beginUpdate();
15009         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
15010         this.list.alignTo(this.inputEl(), this.listAlign);
15011         this.list.alignTo(this.inputEl(), this.listAlign);
15012         //this.list.endUpdate();
15013     },
15014
15015     // private
15016     onEmptyResults : function(){
15017         
15018         if(this.tickable && this.editable){
15019             this.hasFocus = false;
15020             this.restrictHeight();
15021             return;
15022         }
15023         
15024         this.collapse();
15025     },
15026
15027     /**
15028      * Returns true if the dropdown list is expanded, else false.
15029      */
15030     isExpanded : function(){
15031         return this.list.isVisible();
15032     },
15033
15034     /**
15035      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
15036      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15037      * @param {String} value The data value of the item to select
15038      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15039      * selected item if it is not currently in view (defaults to true)
15040      * @return {Boolean} True if the value matched an item in the list, else false
15041      */
15042     selectByValue : function(v, scrollIntoView){
15043         if(v !== undefined && v !== null){
15044             var r = this.findRecord(this.valueField || this.displayField, v);
15045             if(r){
15046                 this.select(this.store.indexOf(r), scrollIntoView);
15047                 return true;
15048             }
15049         }
15050         return false;
15051     },
15052
15053     /**
15054      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
15055      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
15056      * @param {Number} index The zero-based index of the list item to select
15057      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
15058      * selected item if it is not currently in view (defaults to true)
15059      */
15060     select : function(index, scrollIntoView){
15061         this.selectedIndex = index;
15062         this.view.select(index);
15063         if(scrollIntoView !== false){
15064             var el = this.view.getNode(index);
15065             /*
15066              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
15067              */
15068             if(el){
15069                 this.list.scrollChildIntoView(el, false);
15070             }
15071         }
15072     },
15073
15074     // private
15075     selectNext : function(){
15076         var ct = this.store.getCount();
15077         if(ct > 0){
15078             if(this.selectedIndex == -1){
15079                 this.select(0);
15080             }else if(this.selectedIndex < ct-1){
15081                 this.select(this.selectedIndex+1);
15082             }
15083         }
15084     },
15085
15086     // private
15087     selectPrev : function(){
15088         var ct = this.store.getCount();
15089         if(ct > 0){
15090             if(this.selectedIndex == -1){
15091                 this.select(0);
15092             }else if(this.selectedIndex != 0){
15093                 this.select(this.selectedIndex-1);
15094             }
15095         }
15096     },
15097
15098     // private
15099     onKeyUp : function(e){
15100         if(this.editable !== false && !e.isSpecialKey()){
15101             this.lastKey = e.getKey();
15102             this.dqTask.delay(this.queryDelay);
15103         }
15104     },
15105
15106     // private
15107     validateBlur : function(){
15108         return !this.list || !this.list.isVisible();   
15109     },
15110
15111     // private
15112     initQuery : function(){
15113         
15114         var v = this.getRawValue();
15115         
15116         if(this.tickable && this.editable){
15117             v = this.tickableInputEl().getValue();
15118         }
15119         
15120         this.doQuery(v);
15121     },
15122
15123     // private
15124     doForce : function(){
15125         if(this.inputEl().dom.value.length > 0){
15126             this.inputEl().dom.value =
15127                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
15128              
15129         }
15130     },
15131
15132     /**
15133      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
15134      * query allowing the query action to be canceled if needed.
15135      * @param {String} query The SQL query to execute
15136      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
15137      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
15138      * saved in the current store (defaults to false)
15139      */
15140     doQuery : function(q, forceAll){
15141         
15142         if(q === undefined || q === null){
15143             q = '';
15144         }
15145         var qe = {
15146             query: q,
15147             forceAll: forceAll,
15148             combo: this,
15149             cancel:false
15150         };
15151         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
15152             return false;
15153         }
15154         q = qe.query;
15155         
15156         forceAll = qe.forceAll;
15157         if(forceAll === true || (q.length >= this.minChars)){
15158             
15159             this.hasQuery = true;
15160             
15161             if(this.lastQuery != q || this.alwaysQuery){
15162                 this.lastQuery = q;
15163                 if(this.mode == 'local'){
15164                     this.selectedIndex = -1;
15165                     if(forceAll){
15166                         this.store.clearFilter();
15167                     }else{
15168                         
15169                         if(this.specialFilter){
15170                             this.fireEvent('specialfilter', this);
15171                             this.onLoad();
15172                             return;
15173                         }
15174                         
15175                         this.store.filter(this.displayField, q);
15176                     }
15177                     
15178                     this.store.fireEvent("datachanged", this.store);
15179                     
15180                     this.onLoad();
15181                     
15182                     
15183                 }else{
15184                     
15185                     this.store.baseParams[this.queryParam] = q;
15186                     
15187                     var options = {params : this.getParams(q)};
15188                     
15189                     if(this.loadNext){
15190                         options.add = true;
15191                         options.params.start = this.page * this.pageSize;
15192                     }
15193                     
15194                     this.store.load(options);
15195                     
15196                     /*
15197                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
15198                      *  we should expand the list on onLoad
15199                      *  so command out it
15200                      */
15201 //                    this.expand();
15202                 }
15203             }else{
15204                 this.selectedIndex = -1;
15205                 this.onLoad();   
15206             }
15207         }
15208         
15209         this.loadNext = false;
15210     },
15211     
15212     // private
15213     getParams : function(q){
15214         var p = {};
15215         //p[this.queryParam] = q;
15216         
15217         if(this.pageSize){
15218             p.start = 0;
15219             p.limit = this.pageSize;
15220         }
15221         return p;
15222     },
15223
15224     /**
15225      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
15226      */
15227     collapse : function(){
15228         if(!this.isExpanded()){
15229             return;
15230         }
15231         
15232         this.list.hide();
15233         
15234         this.hasFocus = false;
15235         
15236         if(this.tickable){
15237             this.okBtn.hide();
15238             this.cancelBtn.hide();
15239             this.trigger.show();
15240             
15241             if(this.editable){
15242                 this.tickableInputEl().dom.value = '';
15243                 this.tickableInputEl().blur();
15244             }
15245             
15246         }
15247         
15248         Roo.get(document).un('mousedown', this.collapseIf, this);
15249         Roo.get(document).un('mousewheel', this.collapseIf, this);
15250         if (!this.editable) {
15251             Roo.get(document).un('keydown', this.listKeyPress, this);
15252         }
15253         this.fireEvent('collapse', this);
15254         
15255         this.validate();
15256     },
15257
15258     // private
15259     collapseIf : function(e){
15260         var in_combo  = e.within(this.el);
15261         var in_list =  e.within(this.list);
15262         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
15263         
15264         if (in_combo || in_list || is_list) {
15265             //e.stopPropagation();
15266             return;
15267         }
15268         
15269         if(this.tickable){
15270             this.onTickableFooterButtonClick(e, false, false);
15271         }
15272
15273         this.collapse();
15274         
15275     },
15276
15277     /**
15278      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
15279      */
15280     expand : function(){
15281        
15282         if(this.isExpanded() || !this.hasFocus){
15283             return;
15284         }
15285         
15286         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
15287         this.list.setWidth(lw);
15288         
15289         Roo.log('expand');
15290         
15291         this.list.show();
15292         
15293         this.restrictHeight();
15294         
15295         if(this.tickable){
15296             
15297             this.tickItems = Roo.apply([], this.item);
15298             
15299             this.okBtn.show();
15300             this.cancelBtn.show();
15301             this.trigger.hide();
15302             
15303             if(this.editable){
15304                 this.tickableInputEl().focus();
15305             }
15306             
15307         }
15308         
15309         Roo.get(document).on('mousedown', this.collapseIf, this);
15310         Roo.get(document).on('mousewheel', this.collapseIf, this);
15311         if (!this.editable) {
15312             Roo.get(document).on('keydown', this.listKeyPress, this);
15313         }
15314         
15315         this.fireEvent('expand', this);
15316     },
15317
15318     // private
15319     // Implements the default empty TriggerField.onTriggerClick function
15320     onTriggerClick : function(e)
15321     {
15322         Roo.log('trigger click');
15323         
15324         if(this.disabled || !this.triggerList){
15325             return;
15326         }
15327         
15328         this.page = 0;
15329         this.loadNext = false;
15330         
15331         if(this.isExpanded()){
15332             this.collapse();
15333             if (!this.blockFocus) {
15334                 this.inputEl().focus();
15335             }
15336             
15337         }else {
15338             this.hasFocus = true;
15339             if(this.triggerAction == 'all') {
15340                 this.doQuery(this.allQuery, true);
15341             } else {
15342                 this.doQuery(this.getRawValue());
15343             }
15344             if (!this.blockFocus) {
15345                 this.inputEl().focus();
15346             }
15347         }
15348     },
15349     
15350     onTickableTriggerClick : function(e)
15351     {
15352         if(this.disabled){
15353             return;
15354         }
15355         
15356         this.page = 0;
15357         this.loadNext = false;
15358         this.hasFocus = true;
15359         
15360         if(this.triggerAction == 'all') {
15361             this.doQuery(this.allQuery, true);
15362         } else {
15363             this.doQuery(this.getRawValue());
15364         }
15365     },
15366     
15367     onSearchFieldClick : function(e)
15368     {
15369         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
15370             this.onTickableFooterButtonClick(e, false, false);
15371             return;
15372         }
15373         
15374         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15375             return;
15376         }
15377         
15378         this.page = 0;
15379         this.loadNext = false;
15380         this.hasFocus = true;
15381         
15382         if(this.triggerAction == 'all') {
15383             this.doQuery(this.allQuery, true);
15384         } else {
15385             this.doQuery(this.getRawValue());
15386         }
15387     },
15388     
15389     listKeyPress : function(e)
15390     {
15391         //Roo.log('listkeypress');
15392         // scroll to first matching element based on key pres..
15393         if (e.isSpecialKey()) {
15394             return false;
15395         }
15396         var k = String.fromCharCode(e.getKey()).toUpperCase();
15397         //Roo.log(k);
15398         var match  = false;
15399         var csel = this.view.getSelectedNodes();
15400         var cselitem = false;
15401         if (csel.length) {
15402             var ix = this.view.indexOf(csel[0]);
15403             cselitem  = this.store.getAt(ix);
15404             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15405                 cselitem = false;
15406             }
15407             
15408         }
15409         
15410         this.store.each(function(v) { 
15411             if (cselitem) {
15412                 // start at existing selection.
15413                 if (cselitem.id == v.id) {
15414                     cselitem = false;
15415                 }
15416                 return true;
15417             }
15418                 
15419             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15420                 match = this.store.indexOf(v);
15421                 return false;
15422             }
15423             return true;
15424         }, this);
15425         
15426         if (match === false) {
15427             return true; // no more action?
15428         }
15429         // scroll to?
15430         this.view.select(match);
15431         var sn = Roo.get(this.view.getSelectedNodes()[0]);
15432         sn.scrollIntoView(sn.dom.parentNode, false);
15433     },
15434     
15435     onViewScroll : function(e, t){
15436         
15437         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){
15438             return;
15439         }
15440         
15441         this.hasQuery = true;
15442         
15443         this.loading = this.list.select('.loading', true).first();
15444         
15445         if(this.loading === null){
15446             this.list.createChild({
15447                 tag: 'div',
15448                 cls: 'loading roo-select2-more-results roo-select2-active',
15449                 html: 'Loading more results...'
15450             });
15451             
15452             this.loading = this.list.select('.loading', true).first();
15453             
15454             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15455             
15456             this.loading.hide();
15457         }
15458         
15459         this.loading.show();
15460         
15461         var _combo = this;
15462         
15463         this.page++;
15464         this.loadNext = true;
15465         
15466         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15467         
15468         return;
15469     },
15470     
15471     addItem : function(o)
15472     {   
15473         var dv = ''; // display value
15474         
15475         if (this.displayField) {
15476             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15477         } else {
15478             // this is an error condition!!!
15479             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15480         }
15481         
15482         if(!dv.length){
15483             return;
15484         }
15485         
15486         var choice = this.choices.createChild({
15487             tag: 'li',
15488             cls: 'roo-select2-search-choice',
15489             cn: [
15490                 {
15491                     tag: 'div',
15492                     html: dv
15493                 },
15494                 {
15495                     tag: 'a',
15496                     href: '#',
15497                     cls: 'roo-select2-search-choice-close fa fa-times',
15498                     tabindex: '-1'
15499                 }
15500             ]
15501             
15502         }, this.searchField);
15503         
15504         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15505         
15506         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15507         
15508         this.item.push(o);
15509         
15510         this.lastData = o;
15511         
15512         this.syncValue();
15513         
15514         this.inputEl().dom.value = '';
15515         
15516         this.validate();
15517     },
15518     
15519     onRemoveItem : function(e, _self, o)
15520     {
15521         e.preventDefault();
15522         
15523         this.lastItem = Roo.apply([], this.item);
15524         
15525         var index = this.item.indexOf(o.data) * 1;
15526         
15527         if( index < 0){
15528             Roo.log('not this item?!');
15529             return;
15530         }
15531         
15532         this.item.splice(index, 1);
15533         o.item.remove();
15534         
15535         this.syncValue();
15536         
15537         this.fireEvent('remove', this, e);
15538         
15539         this.validate();
15540         
15541     },
15542     
15543     syncValue : function()
15544     {
15545         if(!this.item.length){
15546             this.clearValue();
15547             return;
15548         }
15549             
15550         var value = [];
15551         var _this = this;
15552         Roo.each(this.item, function(i){
15553             if(_this.valueField){
15554                 value.push(i[_this.valueField]);
15555                 return;
15556             }
15557
15558             value.push(i);
15559         });
15560
15561         this.value = value.join(',');
15562
15563         if(this.hiddenField){
15564             this.hiddenField.dom.value = this.value;
15565         }
15566         
15567         this.store.fireEvent("datachanged", this.store);
15568         
15569         this.validate();
15570     },
15571     
15572     clearItem : function()
15573     {
15574         if(!this.multiple){
15575             return;
15576         }
15577         
15578         this.item = [];
15579         
15580         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15581            c.remove();
15582         });
15583         
15584         this.syncValue();
15585         
15586         this.validate();
15587         
15588         if(this.tickable && !Roo.isTouch){
15589             this.view.refresh();
15590         }
15591     },
15592     
15593     inputEl: function ()
15594     {
15595         if(Roo.isIOS && this.useNativeIOS){
15596             return this.el.select('select.roo-ios-select', true).first();
15597         }
15598         
15599         if(Roo.isTouch && this.mobileTouchView){
15600             return this.el.select('input.form-control',true).first();
15601         }
15602         
15603         if(this.tickable){
15604             return this.searchField;
15605         }
15606         
15607         return this.el.select('input.form-control',true).first();
15608     },
15609     
15610     onTickableFooterButtonClick : function(e, btn, el)
15611     {
15612         e.preventDefault();
15613         
15614         this.lastItem = Roo.apply([], this.item);
15615         
15616         if(btn && btn.name == 'cancel'){
15617             this.tickItems = Roo.apply([], this.item);
15618             this.collapse();
15619             return;
15620         }
15621         
15622         this.clearItem();
15623         
15624         var _this = this;
15625         
15626         Roo.each(this.tickItems, function(o){
15627             _this.addItem(o);
15628         });
15629         
15630         this.collapse();
15631         
15632     },
15633     
15634     validate : function()
15635     {
15636         if(this.getVisibilityEl().hasClass('hidden')){
15637             return true;
15638         }
15639         
15640         var v = this.getRawValue();
15641         
15642         if(this.multiple){
15643             v = this.getValue();
15644         }
15645         
15646         if(this.disabled || this.allowBlank || v.length){
15647             this.markValid();
15648             return true;
15649         }
15650         
15651         this.markInvalid();
15652         return false;
15653     },
15654     
15655     tickableInputEl : function()
15656     {
15657         if(!this.tickable || !this.editable){
15658             return this.inputEl();
15659         }
15660         
15661         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15662     },
15663     
15664     
15665     getAutoCreateTouchView : function()
15666     {
15667         var id = Roo.id();
15668         
15669         var cfg = {
15670             cls: 'form-group' //input-group
15671         };
15672         
15673         var input =  {
15674             tag: 'input',
15675             id : id,
15676             type : this.inputType,
15677             cls : 'form-control x-combo-noedit',
15678             autocomplete: 'new-password',
15679             placeholder : this.placeholder || '',
15680             readonly : true
15681         };
15682         
15683         if (this.name) {
15684             input.name = this.name;
15685         }
15686         
15687         if (this.size) {
15688             input.cls += ' input-' + this.size;
15689         }
15690         
15691         if (this.disabled) {
15692             input.disabled = true;
15693         }
15694         
15695         var inputblock = {
15696             cls : '',
15697             cn : [
15698                 input
15699             ]
15700         };
15701         
15702         if(this.before){
15703             inputblock.cls += ' input-group';
15704             
15705             inputblock.cn.unshift({
15706                 tag :'span',
15707                 cls : 'input-group-addon input-group-prepend input-group-text',
15708                 html : this.before
15709             });
15710         }
15711         
15712         if(this.removable && !this.multiple){
15713             inputblock.cls += ' roo-removable';
15714             
15715             inputblock.cn.push({
15716                 tag: 'button',
15717                 html : 'x',
15718                 cls : 'roo-combo-removable-btn close'
15719             });
15720         }
15721
15722         if(this.hasFeedback && !this.allowBlank){
15723             
15724             inputblock.cls += ' has-feedback';
15725             
15726             inputblock.cn.push({
15727                 tag: 'span',
15728                 cls: 'glyphicon form-control-feedback'
15729             });
15730             
15731         }
15732         
15733         if (this.after) {
15734             
15735             inputblock.cls += (this.before) ? '' : ' input-group';
15736             
15737             inputblock.cn.push({
15738                 tag :'span',
15739                 cls : 'input-group-addon input-group-append input-group-text',
15740                 html : this.after
15741             });
15742         }
15743
15744         
15745         var ibwrap = inputblock;
15746         
15747         if(this.multiple){
15748             ibwrap = {
15749                 tag: 'ul',
15750                 cls: 'roo-select2-choices',
15751                 cn:[
15752                     {
15753                         tag: 'li',
15754                         cls: 'roo-select2-search-field',
15755                         cn: [
15756
15757                             inputblock
15758                         ]
15759                     }
15760                 ]
15761             };
15762         
15763             
15764         }
15765         
15766         var combobox = {
15767             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15768             cn: [
15769                 {
15770                     tag: 'input',
15771                     type : 'hidden',
15772                     cls: 'form-hidden-field'
15773                 },
15774                 ibwrap
15775             ]
15776         };
15777         
15778         if(!this.multiple && this.showToggleBtn){
15779             
15780             var caret = {
15781                 cls: 'caret'
15782             };
15783             
15784             if (this.caret != false) {
15785                 caret = {
15786                      tag: 'i',
15787                      cls: 'fa fa-' + this.caret
15788                 };
15789                 
15790             }
15791             
15792             combobox.cn.push({
15793                 tag :'span',
15794                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15795                 cn : [
15796                     Roo.bootstrap.version == 3 ? caret : '',
15797                     {
15798                         tag: 'span',
15799                         cls: 'combobox-clear',
15800                         cn  : [
15801                             {
15802                                 tag : 'i',
15803                                 cls: 'icon-remove'
15804                             }
15805                         ]
15806                     }
15807                 ]
15808
15809             })
15810         }
15811         
15812         if(this.multiple){
15813             combobox.cls += ' roo-select2-container-multi';
15814         }
15815         
15816         var align = this.labelAlign || this.parentLabelAlign();
15817         
15818         if (align ==='left' && this.fieldLabel.length) {
15819
15820             cfg.cn = [
15821                 {
15822                    tag : 'i',
15823                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15824                    tooltip : 'This field is required'
15825                 },
15826                 {
15827                     tag: 'label',
15828                     cls : 'control-label col-form-label',
15829                     html : this.fieldLabel
15830
15831                 },
15832                 {
15833                     cls : '', 
15834                     cn: [
15835                         combobox
15836                     ]
15837                 }
15838             ];
15839             
15840             var labelCfg = cfg.cn[1];
15841             var contentCfg = cfg.cn[2];
15842             
15843
15844             if(this.indicatorpos == 'right'){
15845                 cfg.cn = [
15846                     {
15847                         tag: 'label',
15848                         'for' :  id,
15849                         cls : 'control-label col-form-label',
15850                         cn : [
15851                             {
15852                                 tag : 'span',
15853                                 html : this.fieldLabel
15854                             },
15855                             {
15856                                 tag : 'i',
15857                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15858                                 tooltip : 'This field is required'
15859                             }
15860                         ]
15861                     },
15862                     {
15863                         cls : "",
15864                         cn: [
15865                             combobox
15866                         ]
15867                     }
15868
15869                 ];
15870                 
15871                 labelCfg = cfg.cn[0];
15872                 contentCfg = cfg.cn[1];
15873             }
15874             
15875            
15876             
15877             if(this.labelWidth > 12){
15878                 labelCfg.style = "width: " + this.labelWidth + 'px';
15879             }
15880             
15881             if(this.labelWidth < 13 && this.labelmd == 0){
15882                 this.labelmd = this.labelWidth;
15883             }
15884             
15885             if(this.labellg > 0){
15886                 labelCfg.cls += ' col-lg-' + this.labellg;
15887                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15888             }
15889             
15890             if(this.labelmd > 0){
15891                 labelCfg.cls += ' col-md-' + this.labelmd;
15892                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15893             }
15894             
15895             if(this.labelsm > 0){
15896                 labelCfg.cls += ' col-sm-' + this.labelsm;
15897                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15898             }
15899             
15900             if(this.labelxs > 0){
15901                 labelCfg.cls += ' col-xs-' + this.labelxs;
15902                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15903             }
15904                 
15905                 
15906         } else if ( this.fieldLabel.length) {
15907             cfg.cn = [
15908                 {
15909                    tag : 'i',
15910                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15911                    tooltip : 'This field is required'
15912                 },
15913                 {
15914                     tag: 'label',
15915                     cls : 'control-label',
15916                     html : this.fieldLabel
15917
15918                 },
15919                 {
15920                     cls : '', 
15921                     cn: [
15922                         combobox
15923                     ]
15924                 }
15925             ];
15926             
15927             if(this.indicatorpos == 'right'){
15928                 cfg.cn = [
15929                     {
15930                         tag: 'label',
15931                         cls : 'control-label',
15932                         html : this.fieldLabel,
15933                         cn : [
15934                             {
15935                                tag : 'i',
15936                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15937                                tooltip : 'This field is required'
15938                             }
15939                         ]
15940                     },
15941                     {
15942                         cls : '', 
15943                         cn: [
15944                             combobox
15945                         ]
15946                     }
15947                 ];
15948             }
15949         } else {
15950             cfg.cn = combobox;    
15951         }
15952         
15953         
15954         var settings = this;
15955         
15956         ['xs','sm','md','lg'].map(function(size){
15957             if (settings[size]) {
15958                 cfg.cls += ' col-' + size + '-' + settings[size];
15959             }
15960         });
15961         
15962         return cfg;
15963     },
15964     
15965     initTouchView : function()
15966     {
15967         this.renderTouchView();
15968         
15969         this.touchViewEl.on('scroll', function(){
15970             this.el.dom.scrollTop = 0;
15971         }, this);
15972         
15973         this.originalValue = this.getValue();
15974         
15975         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15976         
15977         this.inputEl().on("click", this.showTouchView, this);
15978         if (this.triggerEl) {
15979             this.triggerEl.on("click", this.showTouchView, this);
15980         }
15981         
15982         
15983         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15984         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15985         
15986         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15987         
15988         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15989         this.store.on('load', this.onTouchViewLoad, this);
15990         this.store.on('loadexception', this.onTouchViewLoadException, this);
15991         
15992         if(this.hiddenName){
15993             
15994             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15995             
15996             this.hiddenField.dom.value =
15997                 this.hiddenValue !== undefined ? this.hiddenValue :
15998                 this.value !== undefined ? this.value : '';
15999         
16000             this.el.dom.removeAttribute('name');
16001             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16002         }
16003         
16004         if(this.multiple){
16005             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16006             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16007         }
16008         
16009         if(this.removable && !this.multiple){
16010             var close = this.closeTriggerEl();
16011             if(close){
16012                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
16013                 close.on('click', this.removeBtnClick, this, close);
16014             }
16015         }
16016         /*
16017          * fix the bug in Safari iOS8
16018          */
16019         this.inputEl().on("focus", function(e){
16020             document.activeElement.blur();
16021         }, this);
16022         
16023         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
16024         
16025         return;
16026         
16027         
16028     },
16029     
16030     renderTouchView : function()
16031     {
16032         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
16033         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16034         
16035         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
16036         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16037         
16038         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
16039         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16040         this.touchViewBodyEl.setStyle('overflow', 'auto');
16041         
16042         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
16043         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16044         
16045         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
16046         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16047         
16048     },
16049     
16050     showTouchView : function()
16051     {
16052         if(this.disabled){
16053             return;
16054         }
16055         
16056         this.touchViewHeaderEl.hide();
16057
16058         if(this.modalTitle.length){
16059             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
16060             this.touchViewHeaderEl.show();
16061         }
16062
16063         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
16064         this.touchViewEl.show();
16065
16066         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
16067         
16068         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
16069         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
16070
16071         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16072
16073         if(this.modalTitle.length){
16074             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16075         }
16076         
16077         this.touchViewBodyEl.setHeight(bodyHeight);
16078
16079         if(this.animate){
16080             var _this = this;
16081             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
16082         }else{
16083             this.touchViewEl.addClass('in');
16084         }
16085         
16086         if(this._touchViewMask){
16087             Roo.get(document.body).addClass("x-body-masked");
16088             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
16089             this._touchViewMask.setStyle('z-index', 10000);
16090             this._touchViewMask.addClass('show');
16091         }
16092         
16093         this.doTouchViewQuery();
16094         
16095     },
16096     
16097     hideTouchView : function()
16098     {
16099         this.touchViewEl.removeClass('in');
16100
16101         if(this.animate){
16102             var _this = this;
16103             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
16104         }else{
16105             this.touchViewEl.setStyle('display', 'none');
16106         }
16107         
16108         if(this._touchViewMask){
16109             this._touchViewMask.removeClass('show');
16110             Roo.get(document.body).removeClass("x-body-masked");
16111         }
16112     },
16113     
16114     setTouchViewValue : function()
16115     {
16116         if(this.multiple){
16117             this.clearItem();
16118         
16119             var _this = this;
16120
16121             Roo.each(this.tickItems, function(o){
16122                 this.addItem(o);
16123             }, this);
16124         }
16125         
16126         this.hideTouchView();
16127     },
16128     
16129     doTouchViewQuery : function()
16130     {
16131         var qe = {
16132             query: '',
16133             forceAll: true,
16134             combo: this,
16135             cancel:false
16136         };
16137         
16138         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
16139             return false;
16140         }
16141         
16142         if(!this.alwaysQuery || this.mode == 'local'){
16143             this.onTouchViewLoad();
16144             return;
16145         }
16146         
16147         this.store.load();
16148     },
16149     
16150     onTouchViewBeforeLoad : function(combo,opts)
16151     {
16152         return;
16153     },
16154
16155     // private
16156     onTouchViewLoad : function()
16157     {
16158         if(this.store.getCount() < 1){
16159             this.onTouchViewEmptyResults();
16160             return;
16161         }
16162         
16163         this.clearTouchView();
16164         
16165         var rawValue = this.getRawValue();
16166         
16167         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
16168         
16169         this.tickItems = [];
16170         
16171         this.store.data.each(function(d, rowIndex){
16172             var row = this.touchViewListGroup.createChild(template);
16173             
16174             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
16175                 row.addClass(d.data.cls);
16176             }
16177             
16178             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16179                 var cfg = {
16180                     data : d.data,
16181                     html : d.data[this.displayField]
16182                 };
16183                 
16184                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
16185                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
16186                 }
16187             }
16188             row.removeClass('selected');
16189             if(!this.multiple && this.valueField &&
16190                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
16191             {
16192                 // radio buttons..
16193                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16194                 row.addClass('selected');
16195             }
16196             
16197             if(this.multiple && this.valueField &&
16198                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
16199             {
16200                 
16201                 // checkboxes...
16202                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16203                 this.tickItems.push(d.data);
16204             }
16205             
16206             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
16207             
16208         }, this);
16209         
16210         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
16211         
16212         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
16213
16214         if(this.modalTitle.length){
16215             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
16216         }
16217
16218         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
16219         
16220         if(this.mobile_restrict_height && listHeight < bodyHeight){
16221             this.touchViewBodyEl.setHeight(listHeight);
16222         }
16223         
16224         var _this = this;
16225         
16226         if(firstChecked && listHeight > bodyHeight){
16227             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
16228         }
16229         
16230     },
16231     
16232     onTouchViewLoadException : function()
16233     {
16234         this.hideTouchView();
16235     },
16236     
16237     onTouchViewEmptyResults : function()
16238     {
16239         this.clearTouchView();
16240         
16241         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
16242         
16243         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
16244         
16245     },
16246     
16247     clearTouchView : function()
16248     {
16249         this.touchViewListGroup.dom.innerHTML = '';
16250     },
16251     
16252     onTouchViewClick : function(e, el, o)
16253     {
16254         e.preventDefault();
16255         
16256         var row = o.row;
16257         var rowIndex = o.rowIndex;
16258         
16259         var r = this.store.getAt(rowIndex);
16260         
16261         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
16262             
16263             if(!this.multiple){
16264                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
16265                     c.dom.removeAttribute('checked');
16266                 }, this);
16267
16268                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16269
16270                 this.setFromData(r.data);
16271
16272                 var close = this.closeTriggerEl();
16273
16274                 if(close){
16275                     close.show();
16276                 }
16277
16278                 this.hideTouchView();
16279
16280                 this.fireEvent('select', this, r, rowIndex);
16281
16282                 return;
16283             }
16284
16285             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
16286                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
16287                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
16288                 return;
16289             }
16290
16291             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
16292             this.addItem(r.data);
16293             this.tickItems.push(r.data);
16294         }
16295     },
16296     
16297     getAutoCreateNativeIOS : function()
16298     {
16299         var cfg = {
16300             cls: 'form-group' //input-group,
16301         };
16302         
16303         var combobox =  {
16304             tag: 'select',
16305             cls : 'roo-ios-select'
16306         };
16307         
16308         if (this.name) {
16309             combobox.name = this.name;
16310         }
16311         
16312         if (this.disabled) {
16313             combobox.disabled = true;
16314         }
16315         
16316         var settings = this;
16317         
16318         ['xs','sm','md','lg'].map(function(size){
16319             if (settings[size]) {
16320                 cfg.cls += ' col-' + size + '-' + settings[size];
16321             }
16322         });
16323         
16324         cfg.cn = combobox;
16325         
16326         return cfg;
16327         
16328     },
16329     
16330     initIOSView : function()
16331     {
16332         this.store.on('load', this.onIOSViewLoad, this);
16333         
16334         return;
16335     },
16336     
16337     onIOSViewLoad : function()
16338     {
16339         if(this.store.getCount() < 1){
16340             return;
16341         }
16342         
16343         this.clearIOSView();
16344         
16345         if(this.allowBlank) {
16346             
16347             var default_text = '-- SELECT --';
16348             
16349             if(this.placeholder.length){
16350                 default_text = this.placeholder;
16351             }
16352             
16353             if(this.emptyTitle.length){
16354                 default_text += ' - ' + this.emptyTitle + ' -';
16355             }
16356             
16357             var opt = this.inputEl().createChild({
16358                 tag: 'option',
16359                 value : 0,
16360                 html : default_text
16361             });
16362             
16363             var o = {};
16364             o[this.valueField] = 0;
16365             o[this.displayField] = default_text;
16366             
16367             this.ios_options.push({
16368                 data : o,
16369                 el : opt
16370             });
16371             
16372         }
16373         
16374         this.store.data.each(function(d, rowIndex){
16375             
16376             var html = '';
16377             
16378             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16379                 html = d.data[this.displayField];
16380             }
16381             
16382             var value = '';
16383             
16384             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16385                 value = d.data[this.valueField];
16386             }
16387             
16388             var option = {
16389                 tag: 'option',
16390                 value : value,
16391                 html : html
16392             };
16393             
16394             if(this.value == d.data[this.valueField]){
16395                 option['selected'] = true;
16396             }
16397             
16398             var opt = this.inputEl().createChild(option);
16399             
16400             this.ios_options.push({
16401                 data : d.data,
16402                 el : opt
16403             });
16404             
16405         }, this);
16406         
16407         this.inputEl().on('change', function(){
16408            this.fireEvent('select', this);
16409         }, this);
16410         
16411     },
16412     
16413     clearIOSView: function()
16414     {
16415         this.inputEl().dom.innerHTML = '';
16416         
16417         this.ios_options = [];
16418     },
16419     
16420     setIOSValue: function(v)
16421     {
16422         this.value = v;
16423         
16424         if(!this.ios_options){
16425             return;
16426         }
16427         
16428         Roo.each(this.ios_options, function(opts){
16429            
16430            opts.el.dom.removeAttribute('selected');
16431            
16432            if(opts.data[this.valueField] != v){
16433                return;
16434            }
16435            
16436            opts.el.dom.setAttribute('selected', true);
16437            
16438         }, this);
16439     }
16440
16441     /** 
16442     * @cfg {Boolean} grow 
16443     * @hide 
16444     */
16445     /** 
16446     * @cfg {Number} growMin 
16447     * @hide 
16448     */
16449     /** 
16450     * @cfg {Number} growMax 
16451     * @hide 
16452     */
16453     /**
16454      * @hide
16455      * @method autoSize
16456      */
16457 });
16458
16459 Roo.apply(Roo.bootstrap.ComboBox,  {
16460     
16461     header : {
16462         tag: 'div',
16463         cls: 'modal-header',
16464         cn: [
16465             {
16466                 tag: 'h4',
16467                 cls: 'modal-title'
16468             }
16469         ]
16470     },
16471     
16472     body : {
16473         tag: 'div',
16474         cls: 'modal-body',
16475         cn: [
16476             {
16477                 tag: 'ul',
16478                 cls: 'list-group'
16479             }
16480         ]
16481     },
16482     
16483     listItemRadio : {
16484         tag: 'li',
16485         cls: 'list-group-item',
16486         cn: [
16487             {
16488                 tag: 'span',
16489                 cls: 'roo-combobox-list-group-item-value'
16490             },
16491             {
16492                 tag: 'div',
16493                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16494                 cn: [
16495                     {
16496                         tag: 'input',
16497                         type: 'radio'
16498                     },
16499                     {
16500                         tag: 'label'
16501                     }
16502                 ]
16503             }
16504         ]
16505     },
16506     
16507     listItemCheckbox : {
16508         tag: 'li',
16509         cls: 'list-group-item',
16510         cn: [
16511             {
16512                 tag: 'span',
16513                 cls: 'roo-combobox-list-group-item-value'
16514             },
16515             {
16516                 tag: 'div',
16517                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16518                 cn: [
16519                     {
16520                         tag: 'input',
16521                         type: 'checkbox'
16522                     },
16523                     {
16524                         tag: 'label'
16525                     }
16526                 ]
16527             }
16528         ]
16529     },
16530     
16531     emptyResult : {
16532         tag: 'div',
16533         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16534     },
16535     
16536     footer : {
16537         tag: 'div',
16538         cls: 'modal-footer',
16539         cn: [
16540             {
16541                 tag: 'div',
16542                 cls: 'row',
16543                 cn: [
16544                     {
16545                         tag: 'div',
16546                         cls: 'col-xs-6 text-left',
16547                         cn: {
16548                             tag: 'button',
16549                             cls: 'btn btn-danger roo-touch-view-cancel',
16550                             html: 'Cancel'
16551                         }
16552                     },
16553                     {
16554                         tag: 'div',
16555                         cls: 'col-xs-6 text-right',
16556                         cn: {
16557                             tag: 'button',
16558                             cls: 'btn btn-success roo-touch-view-ok',
16559                             html: 'OK'
16560                         }
16561                     }
16562                 ]
16563             }
16564         ]
16565         
16566     }
16567 });
16568
16569 Roo.apply(Roo.bootstrap.ComboBox,  {
16570     
16571     touchViewTemplate : {
16572         tag: 'div',
16573         cls: 'modal fade roo-combobox-touch-view',
16574         cn: [
16575             {
16576                 tag: 'div',
16577                 cls: 'modal-dialog',
16578                 style : 'position:fixed', // we have to fix position....
16579                 cn: [
16580                     {
16581                         tag: 'div',
16582                         cls: 'modal-content',
16583                         cn: [
16584                             Roo.bootstrap.ComboBox.header,
16585                             Roo.bootstrap.ComboBox.body,
16586                             Roo.bootstrap.ComboBox.footer
16587                         ]
16588                     }
16589                 ]
16590             }
16591         ]
16592     }
16593 });/*
16594  * Based on:
16595  * Ext JS Library 1.1.1
16596  * Copyright(c) 2006-2007, Ext JS, LLC.
16597  *
16598  * Originally Released Under LGPL - original licence link has changed is not relivant.
16599  *
16600  * Fork - LGPL
16601  * <script type="text/javascript">
16602  */
16603
16604 /**
16605  * @class Roo.View
16606  * @extends Roo.util.Observable
16607  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16608  * This class also supports single and multi selection modes. <br>
16609  * Create a data model bound view:
16610  <pre><code>
16611  var store = new Roo.data.Store(...);
16612
16613  var view = new Roo.View({
16614     el : "my-element",
16615     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16616  
16617     singleSelect: true,
16618     selectedClass: "ydataview-selected",
16619     store: store
16620  });
16621
16622  // listen for node click?
16623  view.on("click", function(vw, index, node, e){
16624  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16625  });
16626
16627  // load XML data
16628  dataModel.load("foobar.xml");
16629  </code></pre>
16630  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16631  * <br><br>
16632  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16633  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16634  * 
16635  * Note: old style constructor is still suported (container, template, config)
16636  * 
16637  * @constructor
16638  * Create a new View
16639  * @param {Object} config The config object
16640  * 
16641  */
16642 Roo.View = function(config, depreciated_tpl, depreciated_config){
16643     
16644     this.parent = false;
16645     
16646     if (typeof(depreciated_tpl) == 'undefined') {
16647         // new way.. - universal constructor.
16648         Roo.apply(this, config);
16649         this.el  = Roo.get(this.el);
16650     } else {
16651         // old format..
16652         this.el  = Roo.get(config);
16653         this.tpl = depreciated_tpl;
16654         Roo.apply(this, depreciated_config);
16655     }
16656     this.wrapEl  = this.el.wrap().wrap();
16657     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16658     
16659     
16660     if(typeof(this.tpl) == "string"){
16661         this.tpl = new Roo.Template(this.tpl);
16662     } else {
16663         // support xtype ctors..
16664         this.tpl = new Roo.factory(this.tpl, Roo);
16665     }
16666     
16667     
16668     this.tpl.compile();
16669     
16670     /** @private */
16671     this.addEvents({
16672         /**
16673          * @event beforeclick
16674          * Fires before a click is processed. Returns false to cancel the default action.
16675          * @param {Roo.View} this
16676          * @param {Number} index The index of the target node
16677          * @param {HTMLElement} node The target node
16678          * @param {Roo.EventObject} e The raw event object
16679          */
16680             "beforeclick" : true,
16681         /**
16682          * @event click
16683          * Fires when a template node is clicked.
16684          * @param {Roo.View} this
16685          * @param {Number} index The index of the target node
16686          * @param {HTMLElement} node The target node
16687          * @param {Roo.EventObject} e The raw event object
16688          */
16689             "click" : true,
16690         /**
16691          * @event dblclick
16692          * Fires when a template node is double clicked.
16693          * @param {Roo.View} this
16694          * @param {Number} index The index of the target node
16695          * @param {HTMLElement} node The target node
16696          * @param {Roo.EventObject} e The raw event object
16697          */
16698             "dblclick" : true,
16699         /**
16700          * @event contextmenu
16701          * Fires when a template node is right clicked.
16702          * @param {Roo.View} this
16703          * @param {Number} index The index of the target node
16704          * @param {HTMLElement} node The target node
16705          * @param {Roo.EventObject} e The raw event object
16706          */
16707             "contextmenu" : true,
16708         /**
16709          * @event selectionchange
16710          * Fires when the selected nodes change.
16711          * @param {Roo.View} this
16712          * @param {Array} selections Array of the selected nodes
16713          */
16714             "selectionchange" : true,
16715     
16716         /**
16717          * @event beforeselect
16718          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16719          * @param {Roo.View} this
16720          * @param {HTMLElement} node The node to be selected
16721          * @param {Array} selections Array of currently selected nodes
16722          */
16723             "beforeselect" : true,
16724         /**
16725          * @event preparedata
16726          * Fires on every row to render, to allow you to change the data.
16727          * @param {Roo.View} this
16728          * @param {Object} data to be rendered (change this)
16729          */
16730           "preparedata" : true
16731           
16732           
16733         });
16734
16735
16736
16737     this.el.on({
16738         "click": this.onClick,
16739         "dblclick": this.onDblClick,
16740         "contextmenu": this.onContextMenu,
16741         scope:this
16742     });
16743
16744     this.selections = [];
16745     this.nodes = [];
16746     this.cmp = new Roo.CompositeElementLite([]);
16747     if(this.store){
16748         this.store = Roo.factory(this.store, Roo.data);
16749         this.setStore(this.store, true);
16750     }
16751     
16752     if ( this.footer && this.footer.xtype) {
16753            
16754          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16755         
16756         this.footer.dataSource = this.store;
16757         this.footer.container = fctr;
16758         this.footer = Roo.factory(this.footer, Roo);
16759         fctr.insertFirst(this.el);
16760         
16761         // this is a bit insane - as the paging toolbar seems to detach the el..
16762 //        dom.parentNode.parentNode.parentNode
16763          // they get detached?
16764     }
16765     
16766     
16767     Roo.View.superclass.constructor.call(this);
16768     
16769     
16770 };
16771
16772 Roo.extend(Roo.View, Roo.util.Observable, {
16773     
16774      /**
16775      * @cfg {Roo.data.Store} store Data store to load data from.
16776      */
16777     store : false,
16778     
16779     /**
16780      * @cfg {String|Roo.Element} el The container element.
16781      */
16782     el : '',
16783     
16784     /**
16785      * @cfg {String|Roo.Template} tpl The template used by this View 
16786      */
16787     tpl : false,
16788     /**
16789      * @cfg {String} dataName the named area of the template to use as the data area
16790      *                          Works with domtemplates roo-name="name"
16791      */
16792     dataName: false,
16793     /**
16794      * @cfg {String} selectedClass The css class to add to selected nodes
16795      */
16796     selectedClass : "x-view-selected",
16797      /**
16798      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16799      */
16800     emptyText : "",
16801     
16802     /**
16803      * @cfg {String} text to display on mask (default Loading)
16804      */
16805     mask : false,
16806     /**
16807      * @cfg {Boolean} multiSelect Allow multiple selection
16808      */
16809     multiSelect : false,
16810     /**
16811      * @cfg {Boolean} singleSelect Allow single selection
16812      */
16813     singleSelect:  false,
16814     
16815     /**
16816      * @cfg {Boolean} toggleSelect - selecting 
16817      */
16818     toggleSelect : false,
16819     
16820     /**
16821      * @cfg {Boolean} tickable - selecting 
16822      */
16823     tickable : false,
16824     
16825     /**
16826      * Returns the element this view is bound to.
16827      * @return {Roo.Element}
16828      */
16829     getEl : function(){
16830         return this.wrapEl;
16831     },
16832     
16833     
16834
16835     /**
16836      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16837      */
16838     refresh : function(){
16839         //Roo.log('refresh');
16840         var t = this.tpl;
16841         
16842         // if we are using something like 'domtemplate', then
16843         // the what gets used is:
16844         // t.applySubtemplate(NAME, data, wrapping data..)
16845         // the outer template then get' applied with
16846         //     the store 'extra data'
16847         // and the body get's added to the
16848         //      roo-name="data" node?
16849         //      <span class='roo-tpl-{name}'></span> ?????
16850         
16851         
16852         
16853         this.clearSelections();
16854         this.el.update("");
16855         var html = [];
16856         var records = this.store.getRange();
16857         if(records.length < 1) {
16858             
16859             // is this valid??  = should it render a template??
16860             
16861             this.el.update(this.emptyText);
16862             return;
16863         }
16864         var el = this.el;
16865         if (this.dataName) {
16866             this.el.update(t.apply(this.store.meta)); //????
16867             el = this.el.child('.roo-tpl-' + this.dataName);
16868         }
16869         
16870         for(var i = 0, len = records.length; i < len; i++){
16871             var data = this.prepareData(records[i].data, i, records[i]);
16872             this.fireEvent("preparedata", this, data, i, records[i]);
16873             
16874             var d = Roo.apply({}, data);
16875             
16876             if(this.tickable){
16877                 Roo.apply(d, {'roo-id' : Roo.id()});
16878                 
16879                 var _this = this;
16880             
16881                 Roo.each(this.parent.item, function(item){
16882                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16883                         return;
16884                     }
16885                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16886                 });
16887             }
16888             
16889             html[html.length] = Roo.util.Format.trim(
16890                 this.dataName ?
16891                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16892                     t.apply(d)
16893             );
16894         }
16895         
16896         
16897         
16898         el.update(html.join(""));
16899         this.nodes = el.dom.childNodes;
16900         this.updateIndexes(0);
16901     },
16902     
16903
16904     /**
16905      * Function to override to reformat the data that is sent to
16906      * the template for each node.
16907      * DEPRICATED - use the preparedata event handler.
16908      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16909      * a JSON object for an UpdateManager bound view).
16910      */
16911     prepareData : function(data, index, record)
16912     {
16913         this.fireEvent("preparedata", this, data, index, record);
16914         return data;
16915     },
16916
16917     onUpdate : function(ds, record){
16918         // Roo.log('on update');   
16919         this.clearSelections();
16920         var index = this.store.indexOf(record);
16921         var n = this.nodes[index];
16922         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16923         n.parentNode.removeChild(n);
16924         this.updateIndexes(index, index);
16925     },
16926
16927     
16928     
16929 // --------- FIXME     
16930     onAdd : function(ds, records, index)
16931     {
16932         //Roo.log(['on Add', ds, records, index] );        
16933         this.clearSelections();
16934         if(this.nodes.length == 0){
16935             this.refresh();
16936             return;
16937         }
16938         var n = this.nodes[index];
16939         for(var i = 0, len = records.length; i < len; i++){
16940             var d = this.prepareData(records[i].data, i, records[i]);
16941             if(n){
16942                 this.tpl.insertBefore(n, d);
16943             }else{
16944                 
16945                 this.tpl.append(this.el, d);
16946             }
16947         }
16948         this.updateIndexes(index);
16949     },
16950
16951     onRemove : function(ds, record, index){
16952        // Roo.log('onRemove');
16953         this.clearSelections();
16954         var el = this.dataName  ?
16955             this.el.child('.roo-tpl-' + this.dataName) :
16956             this.el; 
16957         
16958         el.dom.removeChild(this.nodes[index]);
16959         this.updateIndexes(index);
16960     },
16961
16962     /**
16963      * Refresh an individual node.
16964      * @param {Number} index
16965      */
16966     refreshNode : function(index){
16967         this.onUpdate(this.store, this.store.getAt(index));
16968     },
16969
16970     updateIndexes : function(startIndex, endIndex){
16971         var ns = this.nodes;
16972         startIndex = startIndex || 0;
16973         endIndex = endIndex || ns.length - 1;
16974         for(var i = startIndex; i <= endIndex; i++){
16975             ns[i].nodeIndex = i;
16976         }
16977     },
16978
16979     /**
16980      * Changes the data store this view uses and refresh the view.
16981      * @param {Store} store
16982      */
16983     setStore : function(store, initial){
16984         if(!initial && this.store){
16985             this.store.un("datachanged", this.refresh);
16986             this.store.un("add", this.onAdd);
16987             this.store.un("remove", this.onRemove);
16988             this.store.un("update", this.onUpdate);
16989             this.store.un("clear", this.refresh);
16990             this.store.un("beforeload", this.onBeforeLoad);
16991             this.store.un("load", this.onLoad);
16992             this.store.un("loadexception", this.onLoad);
16993         }
16994         if(store){
16995           
16996             store.on("datachanged", this.refresh, this);
16997             store.on("add", this.onAdd, this);
16998             store.on("remove", this.onRemove, this);
16999             store.on("update", this.onUpdate, this);
17000             store.on("clear", this.refresh, this);
17001             store.on("beforeload", this.onBeforeLoad, this);
17002             store.on("load", this.onLoad, this);
17003             store.on("loadexception", this.onLoad, this);
17004         }
17005         
17006         if(store){
17007             this.refresh();
17008         }
17009     },
17010     /**
17011      * onbeforeLoad - masks the loading area.
17012      *
17013      */
17014     onBeforeLoad : function(store,opts)
17015     {
17016          //Roo.log('onBeforeLoad');   
17017         if (!opts.add) {
17018             this.el.update("");
17019         }
17020         this.el.mask(this.mask ? this.mask : "Loading" ); 
17021     },
17022     onLoad : function ()
17023     {
17024         this.el.unmask();
17025     },
17026     
17027
17028     /**
17029      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
17030      * @param {HTMLElement} node
17031      * @return {HTMLElement} The template node
17032      */
17033     findItemFromChild : function(node){
17034         var el = this.dataName  ?
17035             this.el.child('.roo-tpl-' + this.dataName,true) :
17036             this.el.dom; 
17037         
17038         if(!node || node.parentNode == el){
17039                     return node;
17040             }
17041             var p = node.parentNode;
17042             while(p && p != el){
17043             if(p.parentNode == el){
17044                 return p;
17045             }
17046             p = p.parentNode;
17047         }
17048             return null;
17049     },
17050
17051     /** @ignore */
17052     onClick : function(e){
17053         var item = this.findItemFromChild(e.getTarget());
17054         if(item){
17055             var index = this.indexOf(item);
17056             if(this.onItemClick(item, index, e) !== false){
17057                 this.fireEvent("click", this, index, item, e);
17058             }
17059         }else{
17060             this.clearSelections();
17061         }
17062     },
17063
17064     /** @ignore */
17065     onContextMenu : function(e){
17066         var item = this.findItemFromChild(e.getTarget());
17067         if(item){
17068             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
17069         }
17070     },
17071
17072     /** @ignore */
17073     onDblClick : function(e){
17074         var item = this.findItemFromChild(e.getTarget());
17075         if(item){
17076             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
17077         }
17078     },
17079
17080     onItemClick : function(item, index, e)
17081     {
17082         if(this.fireEvent("beforeclick", this, index, item, e) === false){
17083             return false;
17084         }
17085         if (this.toggleSelect) {
17086             var m = this.isSelected(item) ? 'unselect' : 'select';
17087             //Roo.log(m);
17088             var _t = this;
17089             _t[m](item, true, false);
17090             return true;
17091         }
17092         if(this.multiSelect || this.singleSelect){
17093             if(this.multiSelect && e.shiftKey && this.lastSelection){
17094                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
17095             }else{
17096                 this.select(item, this.multiSelect && e.ctrlKey);
17097                 this.lastSelection = item;
17098             }
17099             
17100             if(!this.tickable){
17101                 e.preventDefault();
17102             }
17103             
17104         }
17105         return true;
17106     },
17107
17108     /**
17109      * Get the number of selected nodes.
17110      * @return {Number}
17111      */
17112     getSelectionCount : function(){
17113         return this.selections.length;
17114     },
17115
17116     /**
17117      * Get the currently selected nodes.
17118      * @return {Array} An array of HTMLElements
17119      */
17120     getSelectedNodes : function(){
17121         return this.selections;
17122     },
17123
17124     /**
17125      * Get the indexes of the selected nodes.
17126      * @return {Array}
17127      */
17128     getSelectedIndexes : function(){
17129         var indexes = [], s = this.selections;
17130         for(var i = 0, len = s.length; i < len; i++){
17131             indexes.push(s[i].nodeIndex);
17132         }
17133         return indexes;
17134     },
17135
17136     /**
17137      * Clear all selections
17138      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
17139      */
17140     clearSelections : function(suppressEvent){
17141         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
17142             this.cmp.elements = this.selections;
17143             this.cmp.removeClass(this.selectedClass);
17144             this.selections = [];
17145             if(!suppressEvent){
17146                 this.fireEvent("selectionchange", this, this.selections);
17147             }
17148         }
17149     },
17150
17151     /**
17152      * Returns true if the passed node is selected
17153      * @param {HTMLElement/Number} node The node or node index
17154      * @return {Boolean}
17155      */
17156     isSelected : function(node){
17157         var s = this.selections;
17158         if(s.length < 1){
17159             return false;
17160         }
17161         node = this.getNode(node);
17162         return s.indexOf(node) !== -1;
17163     },
17164
17165     /**
17166      * Selects nodes.
17167      * @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
17168      * @param {Boolean} keepExisting (optional) true to keep existing selections
17169      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17170      */
17171     select : function(nodeInfo, keepExisting, suppressEvent){
17172         if(nodeInfo instanceof Array){
17173             if(!keepExisting){
17174                 this.clearSelections(true);
17175             }
17176             for(var i = 0, len = nodeInfo.length; i < len; i++){
17177                 this.select(nodeInfo[i], true, true);
17178             }
17179             return;
17180         } 
17181         var node = this.getNode(nodeInfo);
17182         if(!node || this.isSelected(node)){
17183             return; // already selected.
17184         }
17185         if(!keepExisting){
17186             this.clearSelections(true);
17187         }
17188         
17189         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
17190             Roo.fly(node).addClass(this.selectedClass);
17191             this.selections.push(node);
17192             if(!suppressEvent){
17193                 this.fireEvent("selectionchange", this, this.selections);
17194             }
17195         }
17196         
17197         
17198     },
17199       /**
17200      * Unselects nodes.
17201      * @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
17202      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
17203      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
17204      */
17205     unselect : function(nodeInfo, keepExisting, suppressEvent)
17206     {
17207         if(nodeInfo instanceof Array){
17208             Roo.each(this.selections, function(s) {
17209                 this.unselect(s, nodeInfo);
17210             }, this);
17211             return;
17212         }
17213         var node = this.getNode(nodeInfo);
17214         if(!node || !this.isSelected(node)){
17215             //Roo.log("not selected");
17216             return; // not selected.
17217         }
17218         // fireevent???
17219         var ns = [];
17220         Roo.each(this.selections, function(s) {
17221             if (s == node ) {
17222                 Roo.fly(node).removeClass(this.selectedClass);
17223
17224                 return;
17225             }
17226             ns.push(s);
17227         },this);
17228         
17229         this.selections= ns;
17230         this.fireEvent("selectionchange", this, this.selections);
17231     },
17232
17233     /**
17234      * Gets a template node.
17235      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17236      * @return {HTMLElement} The node or null if it wasn't found
17237      */
17238     getNode : function(nodeInfo){
17239         if(typeof nodeInfo == "string"){
17240             return document.getElementById(nodeInfo);
17241         }else if(typeof nodeInfo == "number"){
17242             return this.nodes[nodeInfo];
17243         }
17244         return nodeInfo;
17245     },
17246
17247     /**
17248      * Gets a range template nodes.
17249      * @param {Number} startIndex
17250      * @param {Number} endIndex
17251      * @return {Array} An array of nodes
17252      */
17253     getNodes : function(start, end){
17254         var ns = this.nodes;
17255         start = start || 0;
17256         end = typeof end == "undefined" ? ns.length - 1 : end;
17257         var nodes = [];
17258         if(start <= end){
17259             for(var i = start; i <= end; i++){
17260                 nodes.push(ns[i]);
17261             }
17262         } else{
17263             for(var i = start; i >= end; i--){
17264                 nodes.push(ns[i]);
17265             }
17266         }
17267         return nodes;
17268     },
17269
17270     /**
17271      * Finds the index of the passed node
17272      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
17273      * @return {Number} The index of the node or -1
17274      */
17275     indexOf : function(node){
17276         node = this.getNode(node);
17277         if(typeof node.nodeIndex == "number"){
17278             return node.nodeIndex;
17279         }
17280         var ns = this.nodes;
17281         for(var i = 0, len = ns.length; i < len; i++){
17282             if(ns[i] == node){
17283                 return i;
17284             }
17285         }
17286         return -1;
17287     }
17288 });
17289 /*
17290  * - LGPL
17291  *
17292  * based on jquery fullcalendar
17293  * 
17294  */
17295
17296 Roo.bootstrap = Roo.bootstrap || {};
17297 /**
17298  * @class Roo.bootstrap.Calendar
17299  * @extends Roo.bootstrap.Component
17300  * Bootstrap Calendar class
17301  * @cfg {Boolean} loadMask (true|false) default false
17302  * @cfg {Object} header generate the user specific header of the calendar, default false
17303
17304  * @constructor
17305  * Create a new Container
17306  * @param {Object} config The config object
17307  */
17308
17309
17310
17311 Roo.bootstrap.Calendar = function(config){
17312     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
17313      this.addEvents({
17314         /**
17315              * @event select
17316              * Fires when a date is selected
17317              * @param {DatePicker} this
17318              * @param {Date} date The selected date
17319              */
17320         'select': true,
17321         /**
17322              * @event monthchange
17323              * Fires when the displayed month changes 
17324              * @param {DatePicker} this
17325              * @param {Date} date The selected month
17326              */
17327         'monthchange': true,
17328         /**
17329              * @event evententer
17330              * Fires when mouse over an event
17331              * @param {Calendar} this
17332              * @param {event} Event
17333              */
17334         'evententer': true,
17335         /**
17336              * @event eventleave
17337              * Fires when the mouse leaves an
17338              * @param {Calendar} this
17339              * @param {event}
17340              */
17341         'eventleave': true,
17342         /**
17343              * @event eventclick
17344              * Fires when the mouse click an
17345              * @param {Calendar} this
17346              * @param {event}
17347              */
17348         'eventclick': true
17349         
17350     });
17351
17352 };
17353
17354 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
17355     
17356      /**
17357      * @cfg {Number} startDay
17358      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
17359      */
17360     startDay : 0,
17361     
17362     loadMask : false,
17363     
17364     header : false,
17365       
17366     getAutoCreate : function(){
17367         
17368         
17369         var fc_button = function(name, corner, style, content ) {
17370             return Roo.apply({},{
17371                 tag : 'span',
17372                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
17373                          (corner.length ?
17374                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17375                             ''
17376                         ),
17377                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17378                 unselectable: 'on'
17379             });
17380         };
17381         
17382         var header = {};
17383         
17384         if(!this.header){
17385             header = {
17386                 tag : 'table',
17387                 cls : 'fc-header',
17388                 style : 'width:100%',
17389                 cn : [
17390                     {
17391                         tag: 'tr',
17392                         cn : [
17393                             {
17394                                 tag : 'td',
17395                                 cls : 'fc-header-left',
17396                                 cn : [
17397                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
17398                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
17399                                     { tag: 'span', cls: 'fc-header-space' },
17400                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
17401
17402
17403                                 ]
17404                             },
17405
17406                             {
17407                                 tag : 'td',
17408                                 cls : 'fc-header-center',
17409                                 cn : [
17410                                     {
17411                                         tag: 'span',
17412                                         cls: 'fc-header-title',
17413                                         cn : {
17414                                             tag: 'H2',
17415                                             html : 'month / year'
17416                                         }
17417                                     }
17418
17419                                 ]
17420                             },
17421                             {
17422                                 tag : 'td',
17423                                 cls : 'fc-header-right',
17424                                 cn : [
17425                               /*      fc_button('month', 'left', '', 'month' ),
17426                                     fc_button('week', '', '', 'week' ),
17427                                     fc_button('day', 'right', '', 'day' )
17428                                 */    
17429
17430                                 ]
17431                             }
17432
17433                         ]
17434                     }
17435                 ]
17436             };
17437         }
17438         
17439         header = this.header;
17440         
17441        
17442         var cal_heads = function() {
17443             var ret = [];
17444             // fixme - handle this.
17445             
17446             for (var i =0; i < Date.dayNames.length; i++) {
17447                 var d = Date.dayNames[i];
17448                 ret.push({
17449                     tag: 'th',
17450                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17451                     html : d.substring(0,3)
17452                 });
17453                 
17454             }
17455             ret[0].cls += ' fc-first';
17456             ret[6].cls += ' fc-last';
17457             return ret;
17458         };
17459         var cal_cell = function(n) {
17460             return  {
17461                 tag: 'td',
17462                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17463                 cn : [
17464                     {
17465                         cn : [
17466                             {
17467                                 cls: 'fc-day-number',
17468                                 html: 'D'
17469                             },
17470                             {
17471                                 cls: 'fc-day-content',
17472                              
17473                                 cn : [
17474                                      {
17475                                         style: 'position: relative;' // height: 17px;
17476                                     }
17477                                 ]
17478                             }
17479                             
17480                             
17481                         ]
17482                     }
17483                 ]
17484                 
17485             }
17486         };
17487         var cal_rows = function() {
17488             
17489             var ret = [];
17490             for (var r = 0; r < 6; r++) {
17491                 var row= {
17492                     tag : 'tr',
17493                     cls : 'fc-week',
17494                     cn : []
17495                 };
17496                 
17497                 for (var i =0; i < Date.dayNames.length; i++) {
17498                     var d = Date.dayNames[i];
17499                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17500
17501                 }
17502                 row.cn[0].cls+=' fc-first';
17503                 row.cn[0].cn[0].style = 'min-height:90px';
17504                 row.cn[6].cls+=' fc-last';
17505                 ret.push(row);
17506                 
17507             }
17508             ret[0].cls += ' fc-first';
17509             ret[4].cls += ' fc-prev-last';
17510             ret[5].cls += ' fc-last';
17511             return ret;
17512             
17513         };
17514         
17515         var cal_table = {
17516             tag: 'table',
17517             cls: 'fc-border-separate',
17518             style : 'width:100%',
17519             cellspacing  : 0,
17520             cn : [
17521                 { 
17522                     tag: 'thead',
17523                     cn : [
17524                         { 
17525                             tag: 'tr',
17526                             cls : 'fc-first fc-last',
17527                             cn : cal_heads()
17528                         }
17529                     ]
17530                 },
17531                 { 
17532                     tag: 'tbody',
17533                     cn : cal_rows()
17534                 }
17535                   
17536             ]
17537         };
17538          
17539          var cfg = {
17540             cls : 'fc fc-ltr',
17541             cn : [
17542                 header,
17543                 {
17544                     cls : 'fc-content',
17545                     style : "position: relative;",
17546                     cn : [
17547                         {
17548                             cls : 'fc-view fc-view-month fc-grid',
17549                             style : 'position: relative',
17550                             unselectable : 'on',
17551                             cn : [
17552                                 {
17553                                     cls : 'fc-event-container',
17554                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17555                                 },
17556                                 cal_table
17557                             ]
17558                         }
17559                     ]
17560     
17561                 }
17562            ] 
17563             
17564         };
17565         
17566          
17567         
17568         return cfg;
17569     },
17570     
17571     
17572     initEvents : function()
17573     {
17574         if(!this.store){
17575             throw "can not find store for calendar";
17576         }
17577         
17578         var mark = {
17579             tag: "div",
17580             cls:"x-dlg-mask",
17581             style: "text-align:center",
17582             cn: [
17583                 {
17584                     tag: "div",
17585                     style: "background-color:white;width:50%;margin:250 auto",
17586                     cn: [
17587                         {
17588                             tag: "img",
17589                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17590                         },
17591                         {
17592                             tag: "span",
17593                             html: "Loading"
17594                         }
17595                         
17596                     ]
17597                 }
17598             ]
17599         };
17600         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17601         
17602         var size = this.el.select('.fc-content', true).first().getSize();
17603         this.maskEl.setSize(size.width, size.height);
17604         this.maskEl.enableDisplayMode("block");
17605         if(!this.loadMask){
17606             this.maskEl.hide();
17607         }
17608         
17609         this.store = Roo.factory(this.store, Roo.data);
17610         this.store.on('load', this.onLoad, this);
17611         this.store.on('beforeload', this.onBeforeLoad, this);
17612         
17613         this.resize();
17614         
17615         this.cells = this.el.select('.fc-day',true);
17616         //Roo.log(this.cells);
17617         this.textNodes = this.el.query('.fc-day-number');
17618         this.cells.addClassOnOver('fc-state-hover');
17619         
17620         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17621         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17622         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17623         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17624         
17625         this.on('monthchange', this.onMonthChange, this);
17626         
17627         this.update(new Date().clearTime());
17628     },
17629     
17630     resize : function() {
17631         var sz  = this.el.getSize();
17632         
17633         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17634         this.el.select('.fc-day-content div',true).setHeight(34);
17635     },
17636     
17637     
17638     // private
17639     showPrevMonth : function(e){
17640         this.update(this.activeDate.add("mo", -1));
17641     },
17642     showToday : function(e){
17643         this.update(new Date().clearTime());
17644     },
17645     // private
17646     showNextMonth : function(e){
17647         this.update(this.activeDate.add("mo", 1));
17648     },
17649
17650     // private
17651     showPrevYear : function(){
17652         this.update(this.activeDate.add("y", -1));
17653     },
17654
17655     // private
17656     showNextYear : function(){
17657         this.update(this.activeDate.add("y", 1));
17658     },
17659
17660     
17661    // private
17662     update : function(date)
17663     {
17664         var vd = this.activeDate;
17665         this.activeDate = date;
17666 //        if(vd && this.el){
17667 //            var t = date.getTime();
17668 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17669 //                Roo.log('using add remove');
17670 //                
17671 //                this.fireEvent('monthchange', this, date);
17672 //                
17673 //                this.cells.removeClass("fc-state-highlight");
17674 //                this.cells.each(function(c){
17675 //                   if(c.dateValue == t){
17676 //                       c.addClass("fc-state-highlight");
17677 //                       setTimeout(function(){
17678 //                            try{c.dom.firstChild.focus();}catch(e){}
17679 //                       }, 50);
17680 //                       return false;
17681 //                   }
17682 //                   return true;
17683 //                });
17684 //                return;
17685 //            }
17686 //        }
17687         
17688         var days = date.getDaysInMonth();
17689         
17690         var firstOfMonth = date.getFirstDateOfMonth();
17691         var startingPos = firstOfMonth.getDay()-this.startDay;
17692         
17693         if(startingPos < this.startDay){
17694             startingPos += 7;
17695         }
17696         
17697         var pm = date.add(Date.MONTH, -1);
17698         var prevStart = pm.getDaysInMonth()-startingPos;
17699 //        
17700         this.cells = this.el.select('.fc-day',true);
17701         this.textNodes = this.el.query('.fc-day-number');
17702         this.cells.addClassOnOver('fc-state-hover');
17703         
17704         var cells = this.cells.elements;
17705         var textEls = this.textNodes;
17706         
17707         Roo.each(cells, function(cell){
17708             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17709         });
17710         
17711         days += startingPos;
17712
17713         // convert everything to numbers so it's fast
17714         var day = 86400000;
17715         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17716         //Roo.log(d);
17717         //Roo.log(pm);
17718         //Roo.log(prevStart);
17719         
17720         var today = new Date().clearTime().getTime();
17721         var sel = date.clearTime().getTime();
17722         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17723         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17724         var ddMatch = this.disabledDatesRE;
17725         var ddText = this.disabledDatesText;
17726         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17727         var ddaysText = this.disabledDaysText;
17728         var format = this.format;
17729         
17730         var setCellClass = function(cal, cell){
17731             cell.row = 0;
17732             cell.events = [];
17733             cell.more = [];
17734             //Roo.log('set Cell Class');
17735             cell.title = "";
17736             var t = d.getTime();
17737             
17738             //Roo.log(d);
17739             
17740             cell.dateValue = t;
17741             if(t == today){
17742                 cell.className += " fc-today";
17743                 cell.className += " fc-state-highlight";
17744                 cell.title = cal.todayText;
17745             }
17746             if(t == sel){
17747                 // disable highlight in other month..
17748                 //cell.className += " fc-state-highlight";
17749                 
17750             }
17751             // disabling
17752             if(t < min) {
17753                 cell.className = " fc-state-disabled";
17754                 cell.title = cal.minText;
17755                 return;
17756             }
17757             if(t > max) {
17758                 cell.className = " fc-state-disabled";
17759                 cell.title = cal.maxText;
17760                 return;
17761             }
17762             if(ddays){
17763                 if(ddays.indexOf(d.getDay()) != -1){
17764                     cell.title = ddaysText;
17765                     cell.className = " fc-state-disabled";
17766                 }
17767             }
17768             if(ddMatch && format){
17769                 var fvalue = d.dateFormat(format);
17770                 if(ddMatch.test(fvalue)){
17771                     cell.title = ddText.replace("%0", fvalue);
17772                     cell.className = " fc-state-disabled";
17773                 }
17774             }
17775             
17776             if (!cell.initialClassName) {
17777                 cell.initialClassName = cell.dom.className;
17778             }
17779             
17780             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17781         };
17782
17783         var i = 0;
17784         
17785         for(; i < startingPos; i++) {
17786             textEls[i].innerHTML = (++prevStart);
17787             d.setDate(d.getDate()+1);
17788             
17789             cells[i].className = "fc-past fc-other-month";
17790             setCellClass(this, cells[i]);
17791         }
17792         
17793         var intDay = 0;
17794         
17795         for(; i < days; i++){
17796             intDay = i - startingPos + 1;
17797             textEls[i].innerHTML = (intDay);
17798             d.setDate(d.getDate()+1);
17799             
17800             cells[i].className = ''; // "x-date-active";
17801             setCellClass(this, cells[i]);
17802         }
17803         var extraDays = 0;
17804         
17805         for(; i < 42; i++) {
17806             textEls[i].innerHTML = (++extraDays);
17807             d.setDate(d.getDate()+1);
17808             
17809             cells[i].className = "fc-future fc-other-month";
17810             setCellClass(this, cells[i]);
17811         }
17812         
17813         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17814         
17815         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17816         
17817         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17818         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17819         
17820         if(totalRows != 6){
17821             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17822             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17823         }
17824         
17825         this.fireEvent('monthchange', this, date);
17826         
17827         
17828         /*
17829         if(!this.internalRender){
17830             var main = this.el.dom.firstChild;
17831             var w = main.offsetWidth;
17832             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17833             Roo.fly(main).setWidth(w);
17834             this.internalRender = true;
17835             // opera does not respect the auto grow header center column
17836             // then, after it gets a width opera refuses to recalculate
17837             // without a second pass
17838             if(Roo.isOpera && !this.secondPass){
17839                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17840                 this.secondPass = true;
17841                 this.update.defer(10, this, [date]);
17842             }
17843         }
17844         */
17845         
17846     },
17847     
17848     findCell : function(dt) {
17849         dt = dt.clearTime().getTime();
17850         var ret = false;
17851         this.cells.each(function(c){
17852             //Roo.log("check " +c.dateValue + '?=' + dt);
17853             if(c.dateValue == dt){
17854                 ret = c;
17855                 return false;
17856             }
17857             return true;
17858         });
17859         
17860         return ret;
17861     },
17862     
17863     findCells : function(ev) {
17864         var s = ev.start.clone().clearTime().getTime();
17865        // Roo.log(s);
17866         var e= ev.end.clone().clearTime().getTime();
17867        // Roo.log(e);
17868         var ret = [];
17869         this.cells.each(function(c){
17870              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17871             
17872             if(c.dateValue > e){
17873                 return ;
17874             }
17875             if(c.dateValue < s){
17876                 return ;
17877             }
17878             ret.push(c);
17879         });
17880         
17881         return ret;    
17882     },
17883     
17884 //    findBestRow: function(cells)
17885 //    {
17886 //        var ret = 0;
17887 //        
17888 //        for (var i =0 ; i < cells.length;i++) {
17889 //            ret  = Math.max(cells[i].rows || 0,ret);
17890 //        }
17891 //        return ret;
17892 //        
17893 //    },
17894     
17895     
17896     addItem : function(ev)
17897     {
17898         // look for vertical location slot in
17899         var cells = this.findCells(ev);
17900         
17901 //        ev.row = this.findBestRow(cells);
17902         
17903         // work out the location.
17904         
17905         var crow = false;
17906         var rows = [];
17907         for(var i =0; i < cells.length; i++) {
17908             
17909             cells[i].row = cells[0].row;
17910             
17911             if(i == 0){
17912                 cells[i].row = cells[i].row + 1;
17913             }
17914             
17915             if (!crow) {
17916                 crow = {
17917                     start : cells[i],
17918                     end :  cells[i]
17919                 };
17920                 continue;
17921             }
17922             if (crow.start.getY() == cells[i].getY()) {
17923                 // on same row.
17924                 crow.end = cells[i];
17925                 continue;
17926             }
17927             // different row.
17928             rows.push(crow);
17929             crow = {
17930                 start: cells[i],
17931                 end : cells[i]
17932             };
17933             
17934         }
17935         
17936         rows.push(crow);
17937         ev.els = [];
17938         ev.rows = rows;
17939         ev.cells = cells;
17940         
17941         cells[0].events.push(ev);
17942         
17943         this.calevents.push(ev);
17944     },
17945     
17946     clearEvents: function() {
17947         
17948         if(!this.calevents){
17949             return;
17950         }
17951         
17952         Roo.each(this.cells.elements, function(c){
17953             c.row = 0;
17954             c.events = [];
17955             c.more = [];
17956         });
17957         
17958         Roo.each(this.calevents, function(e) {
17959             Roo.each(e.els, function(el) {
17960                 el.un('mouseenter' ,this.onEventEnter, this);
17961                 el.un('mouseleave' ,this.onEventLeave, this);
17962                 el.remove();
17963             },this);
17964         },this);
17965         
17966         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17967             e.remove();
17968         });
17969         
17970     },
17971     
17972     renderEvents: function()
17973     {   
17974         var _this = this;
17975         
17976         this.cells.each(function(c) {
17977             
17978             if(c.row < 5){
17979                 return;
17980             }
17981             
17982             var ev = c.events;
17983             
17984             var r = 4;
17985             if(c.row != c.events.length){
17986                 r = 4 - (4 - (c.row - c.events.length));
17987             }
17988             
17989             c.events = ev.slice(0, r);
17990             c.more = ev.slice(r);
17991             
17992             if(c.more.length && c.more.length == 1){
17993                 c.events.push(c.more.pop());
17994             }
17995             
17996             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17997             
17998         });
17999             
18000         this.cells.each(function(c) {
18001             
18002             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
18003             
18004             
18005             for (var e = 0; e < c.events.length; e++){
18006                 var ev = c.events[e];
18007                 var rows = ev.rows;
18008                 
18009                 for(var i = 0; i < rows.length; i++) {
18010                 
18011                     // how many rows should it span..
18012
18013                     var  cfg = {
18014                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
18015                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
18016
18017                         unselectable : "on",
18018                         cn : [
18019                             {
18020                                 cls: 'fc-event-inner',
18021                                 cn : [
18022     //                                {
18023     //                                  tag:'span',
18024     //                                  cls: 'fc-event-time',
18025     //                                  html : cells.length > 1 ? '' : ev.time
18026     //                                },
18027                                     {
18028                                       tag:'span',
18029                                       cls: 'fc-event-title',
18030                                       html : String.format('{0}', ev.title)
18031                                     }
18032
18033
18034                                 ]
18035                             },
18036                             {
18037                                 cls: 'ui-resizable-handle ui-resizable-e',
18038                                 html : '&nbsp;&nbsp;&nbsp'
18039                             }
18040
18041                         ]
18042                     };
18043
18044                     if (i == 0) {
18045                         cfg.cls += ' fc-event-start';
18046                     }
18047                     if ((i+1) == rows.length) {
18048                         cfg.cls += ' fc-event-end';
18049                     }
18050
18051                     var ctr = _this.el.select('.fc-event-container',true).first();
18052                     var cg = ctr.createChild(cfg);
18053
18054                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
18055                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
18056
18057                     var r = (c.more.length) ? 1 : 0;
18058                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
18059                     cg.setWidth(ebox.right - sbox.x -2);
18060
18061                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
18062                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
18063                     cg.on('click', _this.onEventClick, _this, ev);
18064
18065                     ev.els.push(cg);
18066                     
18067                 }
18068                 
18069             }
18070             
18071             
18072             if(c.more.length){
18073                 var  cfg = {
18074                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
18075                     style : 'position: absolute',
18076                     unselectable : "on",
18077                     cn : [
18078                         {
18079                             cls: 'fc-event-inner',
18080                             cn : [
18081                                 {
18082                                   tag:'span',
18083                                   cls: 'fc-event-title',
18084                                   html : 'More'
18085                                 }
18086
18087
18088                             ]
18089                         },
18090                         {
18091                             cls: 'ui-resizable-handle ui-resizable-e',
18092                             html : '&nbsp;&nbsp;&nbsp'
18093                         }
18094
18095                     ]
18096                 };
18097
18098                 var ctr = _this.el.select('.fc-event-container',true).first();
18099                 var cg = ctr.createChild(cfg);
18100
18101                 var sbox = c.select('.fc-day-content',true).first().getBox();
18102                 var ebox = c.select('.fc-day-content',true).first().getBox();
18103                 //Roo.log(cg);
18104                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
18105                 cg.setWidth(ebox.right - sbox.x -2);
18106
18107                 cg.on('click', _this.onMoreEventClick, _this, c.more);
18108                 
18109             }
18110             
18111         });
18112         
18113         
18114         
18115     },
18116     
18117     onEventEnter: function (e, el,event,d) {
18118         this.fireEvent('evententer', this, el, event);
18119     },
18120     
18121     onEventLeave: function (e, el,event,d) {
18122         this.fireEvent('eventleave', this, el, event);
18123     },
18124     
18125     onEventClick: function (e, el,event,d) {
18126         this.fireEvent('eventclick', this, el, event);
18127     },
18128     
18129     onMonthChange: function () {
18130         this.store.load();
18131     },
18132     
18133     onMoreEventClick: function(e, el, more)
18134     {
18135         var _this = this;
18136         
18137         this.calpopover.placement = 'right';
18138         this.calpopover.setTitle('More');
18139         
18140         this.calpopover.setContent('');
18141         
18142         var ctr = this.calpopover.el.select('.popover-content', true).first();
18143         
18144         Roo.each(more, function(m){
18145             var cfg = {
18146                 cls : 'fc-event-hori fc-event-draggable',
18147                 html : m.title
18148             };
18149             var cg = ctr.createChild(cfg);
18150             
18151             cg.on('click', _this.onEventClick, _this, m);
18152         });
18153         
18154         this.calpopover.show(el);
18155         
18156         
18157     },
18158     
18159     onLoad: function () 
18160     {   
18161         this.calevents = [];
18162         var cal = this;
18163         
18164         if(this.store.getCount() > 0){
18165             this.store.data.each(function(d){
18166                cal.addItem({
18167                     id : d.data.id,
18168                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
18169                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
18170                     time : d.data.start_time,
18171                     title : d.data.title,
18172                     description : d.data.description,
18173                     venue : d.data.venue
18174                 });
18175             });
18176         }
18177         
18178         this.renderEvents();
18179         
18180         if(this.calevents.length && this.loadMask){
18181             this.maskEl.hide();
18182         }
18183     },
18184     
18185     onBeforeLoad: function()
18186     {
18187         this.clearEvents();
18188         if(this.loadMask){
18189             this.maskEl.show();
18190         }
18191     }
18192 });
18193
18194  
18195  /*
18196  * - LGPL
18197  *
18198  * element
18199  * 
18200  */
18201
18202 /**
18203  * @class Roo.bootstrap.Popover
18204  * @extends Roo.bootstrap.Component
18205  * Bootstrap Popover class
18206  * @cfg {String} html contents of the popover   (or false to use children..)
18207  * @cfg {String} title of popover (or false to hide)
18208  * @cfg {String} placement how it is placed
18209  * @cfg {String} trigger click || hover (or false to trigger manually)
18210  * @cfg {String} over what (parent or false to trigger manually.)
18211  * @cfg {Number} delay - delay before showing
18212  
18213  * @constructor
18214  * Create a new Popover
18215  * @param {Object} config The config object
18216  */
18217
18218 Roo.bootstrap.Popover = function(config){
18219     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
18220     
18221     this.addEvents({
18222         // raw events
18223          /**
18224          * @event show
18225          * After the popover show
18226          * 
18227          * @param {Roo.bootstrap.Popover} this
18228          */
18229         "show" : true,
18230         /**
18231          * @event hide
18232          * After the popover hide
18233          * 
18234          * @param {Roo.bootstrap.Popover} this
18235          */
18236         "hide" : true
18237     });
18238 };
18239
18240 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
18241     
18242     title: 'Fill in a title',
18243     html: false,
18244     
18245     placement : 'right',
18246     trigger : 'hover', // hover
18247     
18248     delay : 0,
18249     
18250     over: 'parent',
18251     
18252     can_build_overlaid : false,
18253     
18254     getChildContainer : function()
18255     {
18256         return this.el.select('.popover-content',true).first();
18257     },
18258     
18259     getAutoCreate : function(){
18260          
18261         var cfg = {
18262            cls : 'popover roo-dynamic',
18263            style: 'display:block',
18264            cn : [
18265                 {
18266                     cls : 'arrow'
18267                 },
18268                 {
18269                     cls : 'popover-inner',
18270                     cn : [
18271                         {
18272                             tag: 'h3',
18273                             cls: 'popover-title popover-header',
18274                             html : this.title
18275                         },
18276                         {
18277                             cls : 'popover-content popover-body',
18278                             html : this.html
18279                         }
18280                     ]
18281                     
18282                 }
18283            ]
18284         };
18285         
18286         return cfg;
18287     },
18288     setTitle: function(str)
18289     {
18290         this.title = str;
18291         this.el.select('.popover-title',true).first().dom.innerHTML = str;
18292     },
18293     setContent: function(str)
18294     {
18295         this.html = str;
18296         this.el.select('.popover-content',true).first().dom.innerHTML = str;
18297     },
18298     // as it get's added to the bottom of the page.
18299     onRender : function(ct, position)
18300     {
18301         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
18302         if(!this.el){
18303             var cfg = Roo.apply({},  this.getAutoCreate());
18304             cfg.id = Roo.id();
18305             
18306             if (this.cls) {
18307                 cfg.cls += ' ' + this.cls;
18308             }
18309             if (this.style) {
18310                 cfg.style = this.style;
18311             }
18312             //Roo.log("adding to ");
18313             this.el = Roo.get(document.body).createChild(cfg, position);
18314 //            Roo.log(this.el);
18315         }
18316         this.initEvents();
18317     },
18318     
18319     initEvents : function()
18320     {
18321         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
18322         this.el.enableDisplayMode('block');
18323         this.el.hide();
18324         if (this.over === false) {
18325             return; 
18326         }
18327         if (this.triggers === false) {
18328             return;
18329         }
18330         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18331         var triggers = this.trigger ? this.trigger.split(' ') : [];
18332         Roo.each(triggers, function(trigger) {
18333         
18334             if (trigger == 'click') {
18335                 on_el.on('click', this.toggle, this);
18336             } else if (trigger != 'manual') {
18337                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
18338                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
18339       
18340                 on_el.on(eventIn  ,this.enter, this);
18341                 on_el.on(eventOut, this.leave, this);
18342             }
18343         }, this);
18344         
18345     },
18346     
18347     
18348     // private
18349     timeout : null,
18350     hoverState : null,
18351     
18352     toggle : function () {
18353         this.hoverState == 'in' ? this.leave() : this.enter();
18354     },
18355     
18356     enter : function () {
18357         
18358         clearTimeout(this.timeout);
18359     
18360         this.hoverState = 'in';
18361     
18362         if (!this.delay || !this.delay.show) {
18363             this.show();
18364             return;
18365         }
18366         var _t = this;
18367         this.timeout = setTimeout(function () {
18368             if (_t.hoverState == 'in') {
18369                 _t.show();
18370             }
18371         }, this.delay.show)
18372     },
18373     
18374     leave : function() {
18375         clearTimeout(this.timeout);
18376     
18377         this.hoverState = 'out';
18378     
18379         if (!this.delay || !this.delay.hide) {
18380             this.hide();
18381             return;
18382         }
18383         var _t = this;
18384         this.timeout = setTimeout(function () {
18385             if (_t.hoverState == 'out') {
18386                 _t.hide();
18387             }
18388         }, this.delay.hide)
18389     },
18390     
18391     show : function (on_el)
18392     {
18393         if (!on_el) {
18394             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18395         }
18396         
18397         // set content.
18398         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18399         if (this.html !== false) {
18400             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18401         }
18402         this.el.removeClass([
18403             'fade','top','bottom', 'left', 'right','in',
18404             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18405         ]);
18406         if (!this.title.length) {
18407             this.el.select('.popover-title',true).hide();
18408         }
18409         
18410         var placement = typeof this.placement == 'function' ?
18411             this.placement.call(this, this.el, on_el) :
18412             this.placement;
18413             
18414         var autoToken = /\s?auto?\s?/i;
18415         var autoPlace = autoToken.test(placement);
18416         if (autoPlace) {
18417             placement = placement.replace(autoToken, '') || 'top';
18418         }
18419         
18420         //this.el.detach()
18421         //this.el.setXY([0,0]);
18422         this.el.show();
18423         this.el.dom.style.display='block';
18424         this.el.addClass(placement);
18425         
18426         //this.el.appendTo(on_el);
18427         
18428         var p = this.getPosition();
18429         var box = this.el.getBox();
18430         
18431         if (autoPlace) {
18432             // fixme..
18433         }
18434         var align = Roo.bootstrap.Popover.alignment[placement];
18435         
18436 //        Roo.log(align);
18437         this.el.alignTo(on_el, align[0],align[1]);
18438         //var arrow = this.el.select('.arrow',true).first();
18439         //arrow.set(align[2], 
18440         
18441         this.el.addClass('in');
18442         
18443         
18444         if (this.el.hasClass('fade')) {
18445             // fade it?
18446         }
18447         
18448         this.hoverState = 'in';
18449         
18450         this.fireEvent('show', this);
18451         
18452     },
18453     hide : function()
18454     {
18455         this.el.setXY([0,0]);
18456         this.el.removeClass('in');
18457         this.el.hide();
18458         this.hoverState = null;
18459         
18460         this.fireEvent('hide', this);
18461     }
18462     
18463 });
18464
18465 Roo.bootstrap.Popover.alignment = {
18466     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18467     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18468     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18469     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18470 };
18471
18472  /*
18473  * - LGPL
18474  *
18475  * Progress
18476  * 
18477  */
18478
18479 /**
18480  * @class Roo.bootstrap.Progress
18481  * @extends Roo.bootstrap.Component
18482  * Bootstrap Progress class
18483  * @cfg {Boolean} striped striped of the progress bar
18484  * @cfg {Boolean} active animated of the progress bar
18485  * 
18486  * 
18487  * @constructor
18488  * Create a new Progress
18489  * @param {Object} config The config object
18490  */
18491
18492 Roo.bootstrap.Progress = function(config){
18493     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18494 };
18495
18496 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18497     
18498     striped : false,
18499     active: false,
18500     
18501     getAutoCreate : function(){
18502         var cfg = {
18503             tag: 'div',
18504             cls: 'progress'
18505         };
18506         
18507         
18508         if(this.striped){
18509             cfg.cls += ' progress-striped';
18510         }
18511       
18512         if(this.active){
18513             cfg.cls += ' active';
18514         }
18515         
18516         
18517         return cfg;
18518     }
18519    
18520 });
18521
18522  
18523
18524  /*
18525  * - LGPL
18526  *
18527  * ProgressBar
18528  * 
18529  */
18530
18531 /**
18532  * @class Roo.bootstrap.ProgressBar
18533  * @extends Roo.bootstrap.Component
18534  * Bootstrap ProgressBar class
18535  * @cfg {Number} aria_valuenow aria-value now
18536  * @cfg {Number} aria_valuemin aria-value min
18537  * @cfg {Number} aria_valuemax aria-value max
18538  * @cfg {String} label label for the progress bar
18539  * @cfg {String} panel (success | info | warning | danger )
18540  * @cfg {String} role role of the progress bar
18541  * @cfg {String} sr_only text
18542  * 
18543  * 
18544  * @constructor
18545  * Create a new ProgressBar
18546  * @param {Object} config The config object
18547  */
18548
18549 Roo.bootstrap.ProgressBar = function(config){
18550     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18551 };
18552
18553 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18554     
18555     aria_valuenow : 0,
18556     aria_valuemin : 0,
18557     aria_valuemax : 100,
18558     label : false,
18559     panel : false,
18560     role : false,
18561     sr_only: false,
18562     
18563     getAutoCreate : function()
18564     {
18565         
18566         var cfg = {
18567             tag: 'div',
18568             cls: 'progress-bar',
18569             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18570         };
18571         
18572         if(this.sr_only){
18573             cfg.cn = {
18574                 tag: 'span',
18575                 cls: 'sr-only',
18576                 html: this.sr_only
18577             }
18578         }
18579         
18580         if(this.role){
18581             cfg.role = this.role;
18582         }
18583         
18584         if(this.aria_valuenow){
18585             cfg['aria-valuenow'] = this.aria_valuenow;
18586         }
18587         
18588         if(this.aria_valuemin){
18589             cfg['aria-valuemin'] = this.aria_valuemin;
18590         }
18591         
18592         if(this.aria_valuemax){
18593             cfg['aria-valuemax'] = this.aria_valuemax;
18594         }
18595         
18596         if(this.label && !this.sr_only){
18597             cfg.html = this.label;
18598         }
18599         
18600         if(this.panel){
18601             cfg.cls += ' progress-bar-' + this.panel;
18602         }
18603         
18604         return cfg;
18605     },
18606     
18607     update : function(aria_valuenow)
18608     {
18609         this.aria_valuenow = aria_valuenow;
18610         
18611         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18612     }
18613    
18614 });
18615
18616  
18617
18618  /*
18619  * - LGPL
18620  *
18621  * column
18622  * 
18623  */
18624
18625 /**
18626  * @class Roo.bootstrap.TabGroup
18627  * @extends Roo.bootstrap.Column
18628  * Bootstrap Column class
18629  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18630  * @cfg {Boolean} carousel true to make the group behave like a carousel
18631  * @cfg {Boolean} bullets show bullets for the panels
18632  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18633  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18634  * @cfg {Boolean} showarrow (true|false) show arrow default true
18635  * 
18636  * @constructor
18637  * Create a new TabGroup
18638  * @param {Object} config The config object
18639  */
18640
18641 Roo.bootstrap.TabGroup = function(config){
18642     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18643     if (!this.navId) {
18644         this.navId = Roo.id();
18645     }
18646     this.tabs = [];
18647     Roo.bootstrap.TabGroup.register(this);
18648     
18649 };
18650
18651 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18652     
18653     carousel : false,
18654     transition : false,
18655     bullets : 0,
18656     timer : 0,
18657     autoslide : false,
18658     slideFn : false,
18659     slideOnTouch : false,
18660     showarrow : true,
18661     
18662     getAutoCreate : function()
18663     {
18664         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18665         
18666         cfg.cls += ' tab-content';
18667         
18668         if (this.carousel) {
18669             cfg.cls += ' carousel slide';
18670             
18671             cfg.cn = [{
18672                cls : 'carousel-inner',
18673                cn : []
18674             }];
18675         
18676             if(this.bullets  && !Roo.isTouch){
18677                 
18678                 var bullets = {
18679                     cls : 'carousel-bullets',
18680                     cn : []
18681                 };
18682                
18683                 if(this.bullets_cls){
18684                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18685                 }
18686                 
18687                 bullets.cn.push({
18688                     cls : 'clear'
18689                 });
18690                 
18691                 cfg.cn[0].cn.push(bullets);
18692             }
18693             
18694             if(this.showarrow){
18695                 cfg.cn[0].cn.push({
18696                     tag : 'div',
18697                     class : 'carousel-arrow',
18698                     cn : [
18699                         {
18700                             tag : 'div',
18701                             class : 'carousel-prev',
18702                             cn : [
18703                                 {
18704                                     tag : 'i',
18705                                     class : 'fa fa-chevron-left'
18706                                 }
18707                             ]
18708                         },
18709                         {
18710                             tag : 'div',
18711                             class : 'carousel-next',
18712                             cn : [
18713                                 {
18714                                     tag : 'i',
18715                                     class : 'fa fa-chevron-right'
18716                                 }
18717                             ]
18718                         }
18719                     ]
18720                 });
18721             }
18722             
18723         }
18724         
18725         return cfg;
18726     },
18727     
18728     initEvents:  function()
18729     {
18730 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18731 //            this.el.on("touchstart", this.onTouchStart, this);
18732 //        }
18733         
18734         if(this.autoslide){
18735             var _this = this;
18736             
18737             this.slideFn = window.setInterval(function() {
18738                 _this.showPanelNext();
18739             }, this.timer);
18740         }
18741         
18742         if(this.showarrow){
18743             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18744             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18745         }
18746         
18747         
18748     },
18749     
18750 //    onTouchStart : function(e, el, o)
18751 //    {
18752 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18753 //            return;
18754 //        }
18755 //        
18756 //        this.showPanelNext();
18757 //    },
18758     
18759     
18760     getChildContainer : function()
18761     {
18762         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18763     },
18764     
18765     /**
18766     * register a Navigation item
18767     * @param {Roo.bootstrap.NavItem} the navitem to add
18768     */
18769     register : function(item)
18770     {
18771         this.tabs.push( item);
18772         item.navId = this.navId; // not really needed..
18773         this.addBullet();
18774     
18775     },
18776     
18777     getActivePanel : function()
18778     {
18779         var r = false;
18780         Roo.each(this.tabs, function(t) {
18781             if (t.active) {
18782                 r = t;
18783                 return false;
18784             }
18785             return null;
18786         });
18787         return r;
18788         
18789     },
18790     getPanelByName : function(n)
18791     {
18792         var r = false;
18793         Roo.each(this.tabs, function(t) {
18794             if (t.tabId == n) {
18795                 r = t;
18796                 return false;
18797             }
18798             return null;
18799         });
18800         return r;
18801     },
18802     indexOfPanel : function(p)
18803     {
18804         var r = false;
18805         Roo.each(this.tabs, function(t,i) {
18806             if (t.tabId == p.tabId) {
18807                 r = i;
18808                 return false;
18809             }
18810             return null;
18811         });
18812         return r;
18813     },
18814     /**
18815      * show a specific panel
18816      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18817      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18818      */
18819     showPanel : function (pan)
18820     {
18821         if(this.transition || typeof(pan) == 'undefined'){
18822             Roo.log("waiting for the transitionend");
18823             return false;
18824         }
18825         
18826         if (typeof(pan) == 'number') {
18827             pan = this.tabs[pan];
18828         }
18829         
18830         if (typeof(pan) == 'string') {
18831             pan = this.getPanelByName(pan);
18832         }
18833         
18834         var cur = this.getActivePanel();
18835         
18836         if(!pan || !cur){
18837             Roo.log('pan or acitve pan is undefined');
18838             return false;
18839         }
18840         
18841         if (pan.tabId == this.getActivePanel().tabId) {
18842             return true;
18843         }
18844         
18845         if (false === cur.fireEvent('beforedeactivate')) {
18846             return false;
18847         }
18848         
18849         if(this.bullets > 0 && !Roo.isTouch){
18850             this.setActiveBullet(this.indexOfPanel(pan));
18851         }
18852         
18853         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18854             
18855             //class="carousel-item carousel-item-next carousel-item-left"
18856             
18857             this.transition = true;
18858             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18859             var lr = dir == 'next' ? 'left' : 'right';
18860             pan.el.addClass(dir); // or prev
18861             pan.el.addClass('carousel-item-' + dir); // or prev
18862             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18863             cur.el.addClass(lr); // or right
18864             pan.el.addClass(lr);
18865             cur.el.addClass('carousel-item-' +lr); // or right
18866             pan.el.addClass('carousel-item-' +lr);
18867             
18868             
18869             var _this = this;
18870             cur.el.on('transitionend', function() {
18871                 Roo.log("trans end?");
18872                 
18873                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18874                 pan.setActive(true);
18875                 
18876                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18877                 cur.setActive(false);
18878                 
18879                 _this.transition = false;
18880                 
18881             }, this, { single:  true } );
18882             
18883             return true;
18884         }
18885         
18886         cur.setActive(false);
18887         pan.setActive(true);
18888         
18889         return true;
18890         
18891     },
18892     showPanelNext : function()
18893     {
18894         var i = this.indexOfPanel(this.getActivePanel());
18895         
18896         if (i >= this.tabs.length - 1 && !this.autoslide) {
18897             return;
18898         }
18899         
18900         if (i >= this.tabs.length - 1 && this.autoslide) {
18901             i = -1;
18902         }
18903         
18904         this.showPanel(this.tabs[i+1]);
18905     },
18906     
18907     showPanelPrev : function()
18908     {
18909         var i = this.indexOfPanel(this.getActivePanel());
18910         
18911         if (i  < 1 && !this.autoslide) {
18912             return;
18913         }
18914         
18915         if (i < 1 && this.autoslide) {
18916             i = this.tabs.length;
18917         }
18918         
18919         this.showPanel(this.tabs[i-1]);
18920     },
18921     
18922     
18923     addBullet: function()
18924     {
18925         if(!this.bullets || Roo.isTouch){
18926             return;
18927         }
18928         var ctr = this.el.select('.carousel-bullets',true).first();
18929         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18930         var bullet = ctr.createChild({
18931             cls : 'bullet bullet-' + i
18932         },ctr.dom.lastChild);
18933         
18934         
18935         var _this = this;
18936         
18937         bullet.on('click', (function(e, el, o, ii, t){
18938
18939             e.preventDefault();
18940
18941             this.showPanel(ii);
18942
18943             if(this.autoslide && this.slideFn){
18944                 clearInterval(this.slideFn);
18945                 this.slideFn = window.setInterval(function() {
18946                     _this.showPanelNext();
18947                 }, this.timer);
18948             }
18949
18950         }).createDelegate(this, [i, bullet], true));
18951                 
18952         
18953     },
18954      
18955     setActiveBullet : function(i)
18956     {
18957         if(Roo.isTouch){
18958             return;
18959         }
18960         
18961         Roo.each(this.el.select('.bullet', true).elements, function(el){
18962             el.removeClass('selected');
18963         });
18964
18965         var bullet = this.el.select('.bullet-' + i, true).first();
18966         
18967         if(!bullet){
18968             return;
18969         }
18970         
18971         bullet.addClass('selected');
18972     }
18973     
18974     
18975   
18976 });
18977
18978  
18979
18980  
18981  
18982 Roo.apply(Roo.bootstrap.TabGroup, {
18983     
18984     groups: {},
18985      /**
18986     * register a Navigation Group
18987     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18988     */
18989     register : function(navgrp)
18990     {
18991         this.groups[navgrp.navId] = navgrp;
18992         
18993     },
18994     /**
18995     * fetch a Navigation Group based on the navigation ID
18996     * if one does not exist , it will get created.
18997     * @param {string} the navgroup to add
18998     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18999     */
19000     get: function(navId) {
19001         if (typeof(this.groups[navId]) == 'undefined') {
19002             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
19003         }
19004         return this.groups[navId] ;
19005     }
19006     
19007     
19008     
19009 });
19010
19011  /*
19012  * - LGPL
19013  *
19014  * TabPanel
19015  * 
19016  */
19017
19018 /**
19019  * @class Roo.bootstrap.TabPanel
19020  * @extends Roo.bootstrap.Component
19021  * Bootstrap TabPanel class
19022  * @cfg {Boolean} active panel active
19023  * @cfg {String} html panel content
19024  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
19025  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
19026  * @cfg {String} href click to link..
19027  * 
19028  * 
19029  * @constructor
19030  * Create a new TabPanel
19031  * @param {Object} config The config object
19032  */
19033
19034 Roo.bootstrap.TabPanel = function(config){
19035     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
19036     this.addEvents({
19037         /**
19038              * @event changed
19039              * Fires when the active status changes
19040              * @param {Roo.bootstrap.TabPanel} this
19041              * @param {Boolean} state the new state
19042             
19043          */
19044         'changed': true,
19045         /**
19046              * @event beforedeactivate
19047              * Fires before a tab is de-activated - can be used to do validation on a form.
19048              * @param {Roo.bootstrap.TabPanel} this
19049              * @return {Boolean} false if there is an error
19050             
19051          */
19052         'beforedeactivate': true
19053      });
19054     
19055     this.tabId = this.tabId || Roo.id();
19056   
19057 };
19058
19059 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
19060     
19061     active: false,
19062     html: false,
19063     tabId: false,
19064     navId : false,
19065     href : '',
19066     
19067     getAutoCreate : function(){
19068         
19069         
19070         var cfg = {
19071             tag: 'div',
19072             // item is needed for carousel - not sure if it has any effect otherwise
19073             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
19074             html: this.html || ''
19075         };
19076         
19077         if(this.active){
19078             cfg.cls += ' active';
19079         }
19080         
19081         if(this.tabId){
19082             cfg.tabId = this.tabId;
19083         }
19084         
19085         
19086         
19087         return cfg;
19088     },
19089     
19090     initEvents:  function()
19091     {
19092         var p = this.parent();
19093         
19094         this.navId = this.navId || p.navId;
19095         
19096         if (typeof(this.navId) != 'undefined') {
19097             // not really needed.. but just in case.. parent should be a NavGroup.
19098             var tg = Roo.bootstrap.TabGroup.get(this.navId);
19099             
19100             tg.register(this);
19101             
19102             var i = tg.tabs.length - 1;
19103             
19104             if(this.active && tg.bullets > 0 && i < tg.bullets){
19105                 tg.setActiveBullet(i);
19106             }
19107         }
19108         
19109         this.el.on('click', this.onClick, this);
19110         
19111         if(Roo.isTouch){
19112             this.el.on("touchstart", this.onTouchStart, this);
19113             this.el.on("touchmove", this.onTouchMove, this);
19114             this.el.on("touchend", this.onTouchEnd, this);
19115         }
19116         
19117     },
19118     
19119     onRender : function(ct, position)
19120     {
19121         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
19122     },
19123     
19124     setActive : function(state)
19125     {
19126         Roo.log("panel - set active " + this.tabId + "=" + state);
19127         
19128         this.active = state;
19129         if (!state) {
19130             this.el.removeClass('active');
19131             
19132         } else  if (!this.el.hasClass('active')) {
19133             this.el.addClass('active');
19134         }
19135         
19136         this.fireEvent('changed', this, state);
19137     },
19138     
19139     onClick : function(e)
19140     {
19141         e.preventDefault();
19142         
19143         if(!this.href.length){
19144             return;
19145         }
19146         
19147         window.location.href = this.href;
19148     },
19149     
19150     startX : 0,
19151     startY : 0,
19152     endX : 0,
19153     endY : 0,
19154     swiping : false,
19155     
19156     onTouchStart : function(e)
19157     {
19158         this.swiping = false;
19159         
19160         this.startX = e.browserEvent.touches[0].clientX;
19161         this.startY = e.browserEvent.touches[0].clientY;
19162     },
19163     
19164     onTouchMove : function(e)
19165     {
19166         this.swiping = true;
19167         
19168         this.endX = e.browserEvent.touches[0].clientX;
19169         this.endY = e.browserEvent.touches[0].clientY;
19170     },
19171     
19172     onTouchEnd : function(e)
19173     {
19174         if(!this.swiping){
19175             this.onClick(e);
19176             return;
19177         }
19178         
19179         var tabGroup = this.parent();
19180         
19181         if(this.endX > this.startX){ // swiping right
19182             tabGroup.showPanelPrev();
19183             return;
19184         }
19185         
19186         if(this.startX > this.endX){ // swiping left
19187             tabGroup.showPanelNext();
19188             return;
19189         }
19190     }
19191     
19192     
19193 });
19194  
19195
19196  
19197
19198  /*
19199  * - LGPL
19200  *
19201  * DateField
19202  * 
19203  */
19204
19205 /**
19206  * @class Roo.bootstrap.DateField
19207  * @extends Roo.bootstrap.Input
19208  * Bootstrap DateField class
19209  * @cfg {Number} weekStart default 0
19210  * @cfg {String} viewMode default empty, (months|years)
19211  * @cfg {String} minViewMode default empty, (months|years)
19212  * @cfg {Number} startDate default -Infinity
19213  * @cfg {Number} endDate default Infinity
19214  * @cfg {Boolean} todayHighlight default false
19215  * @cfg {Boolean} todayBtn default false
19216  * @cfg {Boolean} calendarWeeks default false
19217  * @cfg {Object} daysOfWeekDisabled default empty
19218  * @cfg {Boolean} singleMode default false (true | false)
19219  * 
19220  * @cfg {Boolean} keyboardNavigation default true
19221  * @cfg {String} language default en
19222  * 
19223  * @constructor
19224  * Create a new DateField
19225  * @param {Object} config The config object
19226  */
19227
19228 Roo.bootstrap.DateField = function(config){
19229     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
19230      this.addEvents({
19231             /**
19232              * @event show
19233              * Fires when this field show.
19234              * @param {Roo.bootstrap.DateField} this
19235              * @param {Mixed} date The date value
19236              */
19237             show : true,
19238             /**
19239              * @event show
19240              * Fires when this field hide.
19241              * @param {Roo.bootstrap.DateField} this
19242              * @param {Mixed} date The date value
19243              */
19244             hide : true,
19245             /**
19246              * @event select
19247              * Fires when select a date.
19248              * @param {Roo.bootstrap.DateField} this
19249              * @param {Mixed} date The date value
19250              */
19251             select : true,
19252             /**
19253              * @event beforeselect
19254              * Fires when before select a date.
19255              * @param {Roo.bootstrap.DateField} this
19256              * @param {Mixed} date The date value
19257              */
19258             beforeselect : true
19259         });
19260 };
19261
19262 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
19263     
19264     /**
19265      * @cfg {String} format
19266      * The default date format string which can be overriden for localization support.  The format must be
19267      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
19268      */
19269     format : "m/d/y",
19270     /**
19271      * @cfg {String} altFormats
19272      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
19273      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
19274      */
19275     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
19276     
19277     weekStart : 0,
19278     
19279     viewMode : '',
19280     
19281     minViewMode : '',
19282     
19283     todayHighlight : false,
19284     
19285     todayBtn: false,
19286     
19287     language: 'en',
19288     
19289     keyboardNavigation: true,
19290     
19291     calendarWeeks: false,
19292     
19293     startDate: -Infinity,
19294     
19295     endDate: Infinity,
19296     
19297     daysOfWeekDisabled: [],
19298     
19299     _events: [],
19300     
19301     singleMode : false,
19302     
19303     UTCDate: function()
19304     {
19305         return new Date(Date.UTC.apply(Date, arguments));
19306     },
19307     
19308     UTCToday: function()
19309     {
19310         var today = new Date();
19311         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
19312     },
19313     
19314     getDate: function() {
19315             var d = this.getUTCDate();
19316             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
19317     },
19318     
19319     getUTCDate: function() {
19320             return this.date;
19321     },
19322     
19323     setDate: function(d) {
19324             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
19325     },
19326     
19327     setUTCDate: function(d) {
19328             this.date = d;
19329             this.setValue(this.formatDate(this.date));
19330     },
19331         
19332     onRender: function(ct, position)
19333     {
19334         
19335         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
19336         
19337         this.language = this.language || 'en';
19338         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
19339         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
19340         
19341         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
19342         this.format = this.format || 'm/d/y';
19343         this.isInline = false;
19344         this.isInput = true;
19345         this.component = this.el.select('.add-on', true).first() || false;
19346         this.component = (this.component && this.component.length === 0) ? false : this.component;
19347         this.hasInput = this.component && this.inputEl().length;
19348         
19349         if (typeof(this.minViewMode === 'string')) {
19350             switch (this.minViewMode) {
19351                 case 'months':
19352                     this.minViewMode = 1;
19353                     break;
19354                 case 'years':
19355                     this.minViewMode = 2;
19356                     break;
19357                 default:
19358                     this.minViewMode = 0;
19359                     break;
19360             }
19361         }
19362         
19363         if (typeof(this.viewMode === 'string')) {
19364             switch (this.viewMode) {
19365                 case 'months':
19366                     this.viewMode = 1;
19367                     break;
19368                 case 'years':
19369                     this.viewMode = 2;
19370                     break;
19371                 default:
19372                     this.viewMode = 0;
19373                     break;
19374             }
19375         }
19376                 
19377         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19378         
19379 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19380         
19381         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19382         
19383         this.picker().on('mousedown', this.onMousedown, this);
19384         this.picker().on('click', this.onClick, this);
19385         
19386         this.picker().addClass('datepicker-dropdown');
19387         
19388         this.startViewMode = this.viewMode;
19389         
19390         if(this.singleMode){
19391             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19392                 v.setVisibilityMode(Roo.Element.DISPLAY);
19393                 v.hide();
19394             });
19395             
19396             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19397                 v.setStyle('width', '189px');
19398             });
19399         }
19400         
19401         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19402             if(!this.calendarWeeks){
19403                 v.remove();
19404                 return;
19405             }
19406             
19407             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19408             v.attr('colspan', function(i, val){
19409                 return parseInt(val) + 1;
19410             });
19411         });
19412                         
19413         
19414         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19415         
19416         this.setStartDate(this.startDate);
19417         this.setEndDate(this.endDate);
19418         
19419         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19420         
19421         this.fillDow();
19422         this.fillMonths();
19423         this.update();
19424         this.showMode();
19425         
19426         if(this.isInline) {
19427             this.showPopup();
19428         }
19429     },
19430     
19431     picker : function()
19432     {
19433         return this.pickerEl;
19434 //        return this.el.select('.datepicker', true).first();
19435     },
19436     
19437     fillDow: function()
19438     {
19439         var dowCnt = this.weekStart;
19440         
19441         var dow = {
19442             tag: 'tr',
19443             cn: [
19444                 
19445             ]
19446         };
19447         
19448         if(this.calendarWeeks){
19449             dow.cn.push({
19450                 tag: 'th',
19451                 cls: 'cw',
19452                 html: '&nbsp;'
19453             })
19454         }
19455         
19456         while (dowCnt < this.weekStart + 7) {
19457             dow.cn.push({
19458                 tag: 'th',
19459                 cls: 'dow',
19460                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19461             });
19462         }
19463         
19464         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19465     },
19466     
19467     fillMonths: function()
19468     {    
19469         var i = 0;
19470         var months = this.picker().select('>.datepicker-months td', true).first();
19471         
19472         months.dom.innerHTML = '';
19473         
19474         while (i < 12) {
19475             var month = {
19476                 tag: 'span',
19477                 cls: 'month',
19478                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19479             };
19480             
19481             months.createChild(month);
19482         }
19483         
19484     },
19485     
19486     update: function()
19487     {
19488         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;
19489         
19490         if (this.date < this.startDate) {
19491             this.viewDate = new Date(this.startDate);
19492         } else if (this.date > this.endDate) {
19493             this.viewDate = new Date(this.endDate);
19494         } else {
19495             this.viewDate = new Date(this.date);
19496         }
19497         
19498         this.fill();
19499     },
19500     
19501     fill: function() 
19502     {
19503         var d = new Date(this.viewDate),
19504                 year = d.getUTCFullYear(),
19505                 month = d.getUTCMonth(),
19506                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19507                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19508                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19509                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19510                 currentDate = this.date && this.date.valueOf(),
19511                 today = this.UTCToday();
19512         
19513         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19514         
19515 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19516         
19517 //        this.picker.select('>tfoot th.today').
19518 //                                              .text(dates[this.language].today)
19519 //                                              .toggle(this.todayBtn !== false);
19520     
19521         this.updateNavArrows();
19522         this.fillMonths();
19523                                                 
19524         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19525         
19526         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19527          
19528         prevMonth.setUTCDate(day);
19529         
19530         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19531         
19532         var nextMonth = new Date(prevMonth);
19533         
19534         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19535         
19536         nextMonth = nextMonth.valueOf();
19537         
19538         var fillMonths = false;
19539         
19540         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19541         
19542         while(prevMonth.valueOf() <= nextMonth) {
19543             var clsName = '';
19544             
19545             if (prevMonth.getUTCDay() === this.weekStart) {
19546                 if(fillMonths){
19547                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19548                 }
19549                     
19550                 fillMonths = {
19551                     tag: 'tr',
19552                     cn: []
19553                 };
19554                 
19555                 if(this.calendarWeeks){
19556                     // ISO 8601: First week contains first thursday.
19557                     // ISO also states week starts on Monday, but we can be more abstract here.
19558                     var
19559                     // Start of current week: based on weekstart/current date
19560                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19561                     // Thursday of this week
19562                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19563                     // First Thursday of year, year from thursday
19564                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19565                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19566                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19567                     
19568                     fillMonths.cn.push({
19569                         tag: 'td',
19570                         cls: 'cw',
19571                         html: calWeek
19572                     });
19573                 }
19574             }
19575             
19576             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19577                 clsName += ' old';
19578             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19579                 clsName += ' new';
19580             }
19581             if (this.todayHighlight &&
19582                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19583                 prevMonth.getUTCMonth() == today.getMonth() &&
19584                 prevMonth.getUTCDate() == today.getDate()) {
19585                 clsName += ' today';
19586             }
19587             
19588             if (currentDate && prevMonth.valueOf() === currentDate) {
19589                 clsName += ' active';
19590             }
19591             
19592             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19593                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19594                     clsName += ' disabled';
19595             }
19596             
19597             fillMonths.cn.push({
19598                 tag: 'td',
19599                 cls: 'day ' + clsName,
19600                 html: prevMonth.getDate()
19601             });
19602             
19603             prevMonth.setDate(prevMonth.getDate()+1);
19604         }
19605           
19606         var currentYear = this.date && this.date.getUTCFullYear();
19607         var currentMonth = this.date && this.date.getUTCMonth();
19608         
19609         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19610         
19611         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19612             v.removeClass('active');
19613             
19614             if(currentYear === year && k === currentMonth){
19615                 v.addClass('active');
19616             }
19617             
19618             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19619                 v.addClass('disabled');
19620             }
19621             
19622         });
19623         
19624         
19625         year = parseInt(year/10, 10) * 10;
19626         
19627         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19628         
19629         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19630         
19631         year -= 1;
19632         for (var i = -1; i < 11; i++) {
19633             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19634                 tag: 'span',
19635                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19636                 html: year
19637             });
19638             
19639             year += 1;
19640         }
19641     },
19642     
19643     showMode: function(dir) 
19644     {
19645         if (dir) {
19646             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19647         }
19648         
19649         Roo.each(this.picker().select('>div',true).elements, function(v){
19650             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19651             v.hide();
19652         });
19653         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19654     },
19655     
19656     place: function()
19657     {
19658         if(this.isInline) {
19659             return;
19660         }
19661         
19662         this.picker().removeClass(['bottom', 'top']);
19663         
19664         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19665             /*
19666              * place to the top of element!
19667              *
19668              */
19669             
19670             this.picker().addClass('top');
19671             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19672             
19673             return;
19674         }
19675         
19676         this.picker().addClass('bottom');
19677         
19678         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19679     },
19680     
19681     parseDate : function(value)
19682     {
19683         if(!value || value instanceof Date){
19684             return value;
19685         }
19686         var v = Date.parseDate(value, this.format);
19687         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19688             v = Date.parseDate(value, 'Y-m-d');
19689         }
19690         if(!v && this.altFormats){
19691             if(!this.altFormatsArray){
19692                 this.altFormatsArray = this.altFormats.split("|");
19693             }
19694             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19695                 v = Date.parseDate(value, this.altFormatsArray[i]);
19696             }
19697         }
19698         return v;
19699     },
19700     
19701     formatDate : function(date, fmt)
19702     {   
19703         return (!date || !(date instanceof Date)) ?
19704         date : date.dateFormat(fmt || this.format);
19705     },
19706     
19707     onFocus : function()
19708     {
19709         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19710         this.showPopup();
19711     },
19712     
19713     onBlur : function()
19714     {
19715         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19716         
19717         var d = this.inputEl().getValue();
19718         
19719         this.setValue(d);
19720                 
19721         this.hidePopup();
19722     },
19723     
19724     showPopup : function()
19725     {
19726         this.picker().show();
19727         this.update();
19728         this.place();
19729         
19730         this.fireEvent('showpopup', this, this.date);
19731     },
19732     
19733     hidePopup : function()
19734     {
19735         if(this.isInline) {
19736             return;
19737         }
19738         this.picker().hide();
19739         this.viewMode = this.startViewMode;
19740         this.showMode();
19741         
19742         this.fireEvent('hidepopup', this, this.date);
19743         
19744     },
19745     
19746     onMousedown: function(e)
19747     {
19748         e.stopPropagation();
19749         e.preventDefault();
19750     },
19751     
19752     keyup: function(e)
19753     {
19754         Roo.bootstrap.DateField.superclass.keyup.call(this);
19755         this.update();
19756     },
19757
19758     setValue: function(v)
19759     {
19760         if(this.fireEvent('beforeselect', this, v) !== false){
19761             var d = new Date(this.parseDate(v) ).clearTime();
19762         
19763             if(isNaN(d.getTime())){
19764                 this.date = this.viewDate = '';
19765                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19766                 return;
19767             }
19768
19769             v = this.formatDate(d);
19770
19771             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19772
19773             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19774
19775             this.update();
19776
19777             this.fireEvent('select', this, this.date);
19778         }
19779     },
19780     
19781     getValue: function()
19782     {
19783         return this.formatDate(this.date);
19784     },
19785     
19786     fireKey: function(e)
19787     {
19788         if (!this.picker().isVisible()){
19789             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19790                 this.showPopup();
19791             }
19792             return;
19793         }
19794         
19795         var dateChanged = false,
19796         dir, day, month,
19797         newDate, newViewDate;
19798         
19799         switch(e.keyCode){
19800             case 27: // escape
19801                 this.hidePopup();
19802                 e.preventDefault();
19803                 break;
19804             case 37: // left
19805             case 39: // right
19806                 if (!this.keyboardNavigation) {
19807                     break;
19808                 }
19809                 dir = e.keyCode == 37 ? -1 : 1;
19810                 
19811                 if (e.ctrlKey){
19812                     newDate = this.moveYear(this.date, dir);
19813                     newViewDate = this.moveYear(this.viewDate, dir);
19814                 } else if (e.shiftKey){
19815                     newDate = this.moveMonth(this.date, dir);
19816                     newViewDate = this.moveMonth(this.viewDate, dir);
19817                 } else {
19818                     newDate = new Date(this.date);
19819                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19820                     newViewDate = new Date(this.viewDate);
19821                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19822                 }
19823                 if (this.dateWithinRange(newDate)){
19824                     this.date = newDate;
19825                     this.viewDate = newViewDate;
19826                     this.setValue(this.formatDate(this.date));
19827 //                    this.update();
19828                     e.preventDefault();
19829                     dateChanged = true;
19830                 }
19831                 break;
19832             case 38: // up
19833             case 40: // down
19834                 if (!this.keyboardNavigation) {
19835                     break;
19836                 }
19837                 dir = e.keyCode == 38 ? -1 : 1;
19838                 if (e.ctrlKey){
19839                     newDate = this.moveYear(this.date, dir);
19840                     newViewDate = this.moveYear(this.viewDate, dir);
19841                 } else if (e.shiftKey){
19842                     newDate = this.moveMonth(this.date, dir);
19843                     newViewDate = this.moveMonth(this.viewDate, dir);
19844                 } else {
19845                     newDate = new Date(this.date);
19846                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19847                     newViewDate = new Date(this.viewDate);
19848                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19849                 }
19850                 if (this.dateWithinRange(newDate)){
19851                     this.date = newDate;
19852                     this.viewDate = newViewDate;
19853                     this.setValue(this.formatDate(this.date));
19854 //                    this.update();
19855                     e.preventDefault();
19856                     dateChanged = true;
19857                 }
19858                 break;
19859             case 13: // enter
19860                 this.setValue(this.formatDate(this.date));
19861                 this.hidePopup();
19862                 e.preventDefault();
19863                 break;
19864             case 9: // tab
19865                 this.setValue(this.formatDate(this.date));
19866                 this.hidePopup();
19867                 break;
19868             case 16: // shift
19869             case 17: // ctrl
19870             case 18: // alt
19871                 break;
19872             default :
19873                 this.hidePopup();
19874                 
19875         }
19876     },
19877     
19878     
19879     onClick: function(e) 
19880     {
19881         e.stopPropagation();
19882         e.preventDefault();
19883         
19884         var target = e.getTarget();
19885         
19886         if(target.nodeName.toLowerCase() === 'i'){
19887             target = Roo.get(target).dom.parentNode;
19888         }
19889         
19890         var nodeName = target.nodeName;
19891         var className = target.className;
19892         var html = target.innerHTML;
19893         //Roo.log(nodeName);
19894         
19895         switch(nodeName.toLowerCase()) {
19896             case 'th':
19897                 switch(className) {
19898                     case 'switch':
19899                         this.showMode(1);
19900                         break;
19901                     case 'prev':
19902                     case 'next':
19903                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19904                         switch(this.viewMode){
19905                                 case 0:
19906                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19907                                         break;
19908                                 case 1:
19909                                 case 2:
19910                                         this.viewDate = this.moveYear(this.viewDate, dir);
19911                                         break;
19912                         }
19913                         this.fill();
19914                         break;
19915                     case 'today':
19916                         var date = new Date();
19917                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19918 //                        this.fill()
19919                         this.setValue(this.formatDate(this.date));
19920                         
19921                         this.hidePopup();
19922                         break;
19923                 }
19924                 break;
19925             case 'span':
19926                 if (className.indexOf('disabled') < 0) {
19927                     this.viewDate.setUTCDate(1);
19928                     if (className.indexOf('month') > -1) {
19929                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19930                     } else {
19931                         var year = parseInt(html, 10) || 0;
19932                         this.viewDate.setUTCFullYear(year);
19933                         
19934                     }
19935                     
19936                     if(this.singleMode){
19937                         this.setValue(this.formatDate(this.viewDate));
19938                         this.hidePopup();
19939                         return;
19940                     }
19941                     
19942                     this.showMode(-1);
19943                     this.fill();
19944                 }
19945                 break;
19946                 
19947             case 'td':
19948                 //Roo.log(className);
19949                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19950                     var day = parseInt(html, 10) || 1;
19951                     var year = this.viewDate.getUTCFullYear(),
19952                         month = this.viewDate.getUTCMonth();
19953
19954                     if (className.indexOf('old') > -1) {
19955                         if(month === 0 ){
19956                             month = 11;
19957                             year -= 1;
19958                         }else{
19959                             month -= 1;
19960                         }
19961                     } else if (className.indexOf('new') > -1) {
19962                         if (month == 11) {
19963                             month = 0;
19964                             year += 1;
19965                         } else {
19966                             month += 1;
19967                         }
19968                     }
19969                     //Roo.log([year,month,day]);
19970                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19971                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19972 //                    this.fill();
19973                     //Roo.log(this.formatDate(this.date));
19974                     this.setValue(this.formatDate(this.date));
19975                     this.hidePopup();
19976                 }
19977                 break;
19978         }
19979     },
19980     
19981     setStartDate: function(startDate)
19982     {
19983         this.startDate = startDate || -Infinity;
19984         if (this.startDate !== -Infinity) {
19985             this.startDate = this.parseDate(this.startDate);
19986         }
19987         this.update();
19988         this.updateNavArrows();
19989     },
19990
19991     setEndDate: function(endDate)
19992     {
19993         this.endDate = endDate || Infinity;
19994         if (this.endDate !== Infinity) {
19995             this.endDate = this.parseDate(this.endDate);
19996         }
19997         this.update();
19998         this.updateNavArrows();
19999     },
20000     
20001     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
20002     {
20003         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
20004         if (typeof(this.daysOfWeekDisabled) !== 'object') {
20005             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
20006         }
20007         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
20008             return parseInt(d, 10);
20009         });
20010         this.update();
20011         this.updateNavArrows();
20012     },
20013     
20014     updateNavArrows: function() 
20015     {
20016         if(this.singleMode){
20017             return;
20018         }
20019         
20020         var d = new Date(this.viewDate),
20021         year = d.getUTCFullYear(),
20022         month = d.getUTCMonth();
20023         
20024         Roo.each(this.picker().select('.prev', true).elements, function(v){
20025             v.show();
20026             switch (this.viewMode) {
20027                 case 0:
20028
20029                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
20030                         v.hide();
20031                     }
20032                     break;
20033                 case 1:
20034                 case 2:
20035                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
20036                         v.hide();
20037                     }
20038                     break;
20039             }
20040         });
20041         
20042         Roo.each(this.picker().select('.next', true).elements, function(v){
20043             v.show();
20044             switch (this.viewMode) {
20045                 case 0:
20046
20047                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
20048                         v.hide();
20049                     }
20050                     break;
20051                 case 1:
20052                 case 2:
20053                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
20054                         v.hide();
20055                     }
20056                     break;
20057             }
20058         })
20059     },
20060     
20061     moveMonth: function(date, dir)
20062     {
20063         if (!dir) {
20064             return date;
20065         }
20066         var new_date = new Date(date.valueOf()),
20067         day = new_date.getUTCDate(),
20068         month = new_date.getUTCMonth(),
20069         mag = Math.abs(dir),
20070         new_month, test;
20071         dir = dir > 0 ? 1 : -1;
20072         if (mag == 1){
20073             test = dir == -1
20074             // If going back one month, make sure month is not current month
20075             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
20076             ? function(){
20077                 return new_date.getUTCMonth() == month;
20078             }
20079             // If going forward one month, make sure month is as expected
20080             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
20081             : function(){
20082                 return new_date.getUTCMonth() != new_month;
20083             };
20084             new_month = month + dir;
20085             new_date.setUTCMonth(new_month);
20086             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
20087             if (new_month < 0 || new_month > 11) {
20088                 new_month = (new_month + 12) % 12;
20089             }
20090         } else {
20091             // For magnitudes >1, move one month at a time...
20092             for (var i=0; i<mag; i++) {
20093                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
20094                 new_date = this.moveMonth(new_date, dir);
20095             }
20096             // ...then reset the day, keeping it in the new month
20097             new_month = new_date.getUTCMonth();
20098             new_date.setUTCDate(day);
20099             test = function(){
20100                 return new_month != new_date.getUTCMonth();
20101             };
20102         }
20103         // Common date-resetting loop -- if date is beyond end of month, make it
20104         // end of month
20105         while (test()){
20106             new_date.setUTCDate(--day);
20107             new_date.setUTCMonth(new_month);
20108         }
20109         return new_date;
20110     },
20111
20112     moveYear: function(date, dir)
20113     {
20114         return this.moveMonth(date, dir*12);
20115     },
20116
20117     dateWithinRange: function(date)
20118     {
20119         return date >= this.startDate && date <= this.endDate;
20120     },
20121
20122     
20123     remove: function() 
20124     {
20125         this.picker().remove();
20126     },
20127     
20128     validateValue : function(value)
20129     {
20130         if(this.getVisibilityEl().hasClass('hidden')){
20131             return true;
20132         }
20133         
20134         if(value.length < 1)  {
20135             if(this.allowBlank){
20136                 return true;
20137             }
20138             return false;
20139         }
20140         
20141         if(value.length < this.minLength){
20142             return false;
20143         }
20144         if(value.length > this.maxLength){
20145             return false;
20146         }
20147         if(this.vtype){
20148             var vt = Roo.form.VTypes;
20149             if(!vt[this.vtype](value, this)){
20150                 return false;
20151             }
20152         }
20153         if(typeof this.validator == "function"){
20154             var msg = this.validator(value);
20155             if(msg !== true){
20156                 return false;
20157             }
20158         }
20159         
20160         if(this.regex && !this.regex.test(value)){
20161             return false;
20162         }
20163         
20164         if(typeof(this.parseDate(value)) == 'undefined'){
20165             return false;
20166         }
20167         
20168         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
20169             return false;
20170         }      
20171         
20172         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
20173             return false;
20174         } 
20175         
20176         
20177         return true;
20178     },
20179     
20180     reset : function()
20181     {
20182         this.date = this.viewDate = '';
20183         
20184         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20185     }
20186    
20187 });
20188
20189 Roo.apply(Roo.bootstrap.DateField,  {
20190     
20191     head : {
20192         tag: 'thead',
20193         cn: [
20194         {
20195             tag: 'tr',
20196             cn: [
20197             {
20198                 tag: 'th',
20199                 cls: 'prev',
20200                 html: '<i class="fa fa-arrow-left"/>'
20201             },
20202             {
20203                 tag: 'th',
20204                 cls: 'switch',
20205                 colspan: '5'
20206             },
20207             {
20208                 tag: 'th',
20209                 cls: 'next',
20210                 html: '<i class="fa fa-arrow-right"/>'
20211             }
20212
20213             ]
20214         }
20215         ]
20216     },
20217     
20218     content : {
20219         tag: 'tbody',
20220         cn: [
20221         {
20222             tag: 'tr',
20223             cn: [
20224             {
20225                 tag: 'td',
20226                 colspan: '7'
20227             }
20228             ]
20229         }
20230         ]
20231     },
20232     
20233     footer : {
20234         tag: 'tfoot',
20235         cn: [
20236         {
20237             tag: 'tr',
20238             cn: [
20239             {
20240                 tag: 'th',
20241                 colspan: '7',
20242                 cls: 'today'
20243             }
20244                     
20245             ]
20246         }
20247         ]
20248     },
20249     
20250     dates:{
20251         en: {
20252             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
20253             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
20254             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
20255             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20256             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
20257             today: "Today"
20258         }
20259     },
20260     
20261     modes: [
20262     {
20263         clsName: 'days',
20264         navFnc: 'Month',
20265         navStep: 1
20266     },
20267     {
20268         clsName: 'months',
20269         navFnc: 'FullYear',
20270         navStep: 1
20271     },
20272     {
20273         clsName: 'years',
20274         navFnc: 'FullYear',
20275         navStep: 10
20276     }]
20277 });
20278
20279 Roo.apply(Roo.bootstrap.DateField,  {
20280   
20281     template : {
20282         tag: 'div',
20283         cls: 'datepicker dropdown-menu roo-dynamic',
20284         cn: [
20285         {
20286             tag: 'div',
20287             cls: 'datepicker-days',
20288             cn: [
20289             {
20290                 tag: 'table',
20291                 cls: 'table-condensed',
20292                 cn:[
20293                 Roo.bootstrap.DateField.head,
20294                 {
20295                     tag: 'tbody'
20296                 },
20297                 Roo.bootstrap.DateField.footer
20298                 ]
20299             }
20300             ]
20301         },
20302         {
20303             tag: 'div',
20304             cls: 'datepicker-months',
20305             cn: [
20306             {
20307                 tag: 'table',
20308                 cls: 'table-condensed',
20309                 cn:[
20310                 Roo.bootstrap.DateField.head,
20311                 Roo.bootstrap.DateField.content,
20312                 Roo.bootstrap.DateField.footer
20313                 ]
20314             }
20315             ]
20316         },
20317         {
20318             tag: 'div',
20319             cls: 'datepicker-years',
20320             cn: [
20321             {
20322                 tag: 'table',
20323                 cls: 'table-condensed',
20324                 cn:[
20325                 Roo.bootstrap.DateField.head,
20326                 Roo.bootstrap.DateField.content,
20327                 Roo.bootstrap.DateField.footer
20328                 ]
20329             }
20330             ]
20331         }
20332         ]
20333     }
20334 });
20335
20336  
20337
20338  /*
20339  * - LGPL
20340  *
20341  * TimeField
20342  * 
20343  */
20344
20345 /**
20346  * @class Roo.bootstrap.TimeField
20347  * @extends Roo.bootstrap.Input
20348  * Bootstrap DateField class
20349  * 
20350  * 
20351  * @constructor
20352  * Create a new TimeField
20353  * @param {Object} config The config object
20354  */
20355
20356 Roo.bootstrap.TimeField = function(config){
20357     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
20358     this.addEvents({
20359             /**
20360              * @event show
20361              * Fires when this field show.
20362              * @param {Roo.bootstrap.DateField} thisthis
20363              * @param {Mixed} date The date value
20364              */
20365             show : true,
20366             /**
20367              * @event show
20368              * Fires when this field hide.
20369              * @param {Roo.bootstrap.DateField} this
20370              * @param {Mixed} date The date value
20371              */
20372             hide : true,
20373             /**
20374              * @event select
20375              * Fires when select a date.
20376              * @param {Roo.bootstrap.DateField} this
20377              * @param {Mixed} date The date value
20378              */
20379             select : true
20380         });
20381 };
20382
20383 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
20384     
20385     /**
20386      * @cfg {String} format
20387      * The default time format string which can be overriden for localization support.  The format must be
20388      * valid according to {@link Date#parseDate} (defaults to 'H:i').
20389      */
20390     format : "H:i",
20391        
20392     onRender: function(ct, position)
20393     {
20394         
20395         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20396                 
20397         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20398         
20399         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20400         
20401         this.pop = this.picker().select('>.datepicker-time',true).first();
20402         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20403         
20404         this.picker().on('mousedown', this.onMousedown, this);
20405         this.picker().on('click', this.onClick, this);
20406         
20407         this.picker().addClass('datepicker-dropdown');
20408     
20409         this.fillTime();
20410         this.update();
20411             
20412         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20413         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20414         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20415         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20416         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20417         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20418
20419     },
20420     
20421     fireKey: function(e){
20422         if (!this.picker().isVisible()){
20423             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20424                 this.show();
20425             }
20426             return;
20427         }
20428
20429         e.preventDefault();
20430         
20431         switch(e.keyCode){
20432             case 27: // escape
20433                 this.hide();
20434                 break;
20435             case 37: // left
20436             case 39: // right
20437                 this.onTogglePeriod();
20438                 break;
20439             case 38: // up
20440                 this.onIncrementMinutes();
20441                 break;
20442             case 40: // down
20443                 this.onDecrementMinutes();
20444                 break;
20445             case 13: // enter
20446             case 9: // tab
20447                 this.setTime();
20448                 break;
20449         }
20450     },
20451     
20452     onClick: function(e) {
20453         e.stopPropagation();
20454         e.preventDefault();
20455     },
20456     
20457     picker : function()
20458     {
20459         return this.el.select('.datepicker', true).first();
20460     },
20461     
20462     fillTime: function()
20463     {    
20464         var time = this.pop.select('tbody', true).first();
20465         
20466         time.dom.innerHTML = '';
20467         
20468         time.createChild({
20469             tag: 'tr',
20470             cn: [
20471                 {
20472                     tag: 'td',
20473                     cn: [
20474                         {
20475                             tag: 'a',
20476                             href: '#',
20477                             cls: 'btn',
20478                             cn: [
20479                                 {
20480                                     tag: 'span',
20481                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20482                                 }
20483                             ]
20484                         } 
20485                     ]
20486                 },
20487                 {
20488                     tag: 'td',
20489                     cls: 'separator'
20490                 },
20491                 {
20492                     tag: 'td',
20493                     cn: [
20494                         {
20495                             tag: 'a',
20496                             href: '#',
20497                             cls: 'btn',
20498                             cn: [
20499                                 {
20500                                     tag: 'span',
20501                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20502                                 }
20503                             ]
20504                         }
20505                     ]
20506                 },
20507                 {
20508                     tag: 'td',
20509                     cls: 'separator'
20510                 }
20511             ]
20512         });
20513         
20514         time.createChild({
20515             tag: 'tr',
20516             cn: [
20517                 {
20518                     tag: 'td',
20519                     cn: [
20520                         {
20521                             tag: 'span',
20522                             cls: 'timepicker-hour',
20523                             html: '00'
20524                         }  
20525                     ]
20526                 },
20527                 {
20528                     tag: 'td',
20529                     cls: 'separator',
20530                     html: ':'
20531                 },
20532                 {
20533                     tag: 'td',
20534                     cn: [
20535                         {
20536                             tag: 'span',
20537                             cls: 'timepicker-minute',
20538                             html: '00'
20539                         }  
20540                     ]
20541                 },
20542                 {
20543                     tag: 'td',
20544                     cls: 'separator'
20545                 },
20546                 {
20547                     tag: 'td',
20548                     cn: [
20549                         {
20550                             tag: 'button',
20551                             type: 'button',
20552                             cls: 'btn btn-primary period',
20553                             html: 'AM'
20554                             
20555                         }
20556                     ]
20557                 }
20558             ]
20559         });
20560         
20561         time.createChild({
20562             tag: 'tr',
20563             cn: [
20564                 {
20565                     tag: 'td',
20566                     cn: [
20567                         {
20568                             tag: 'a',
20569                             href: '#',
20570                             cls: 'btn',
20571                             cn: [
20572                                 {
20573                                     tag: 'span',
20574                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20575                                 }
20576                             ]
20577                         }
20578                     ]
20579                 },
20580                 {
20581                     tag: 'td',
20582                     cls: 'separator'
20583                 },
20584                 {
20585                     tag: 'td',
20586                     cn: [
20587                         {
20588                             tag: 'a',
20589                             href: '#',
20590                             cls: 'btn',
20591                             cn: [
20592                                 {
20593                                     tag: 'span',
20594                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20595                                 }
20596                             ]
20597                         }
20598                     ]
20599                 },
20600                 {
20601                     tag: 'td',
20602                     cls: 'separator'
20603                 }
20604             ]
20605         });
20606         
20607     },
20608     
20609     update: function()
20610     {
20611         
20612         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20613         
20614         this.fill();
20615     },
20616     
20617     fill: function() 
20618     {
20619         var hours = this.time.getHours();
20620         var minutes = this.time.getMinutes();
20621         var period = 'AM';
20622         
20623         if(hours > 11){
20624             period = 'PM';
20625         }
20626         
20627         if(hours == 0){
20628             hours = 12;
20629         }
20630         
20631         
20632         if(hours > 12){
20633             hours = hours - 12;
20634         }
20635         
20636         if(hours < 10){
20637             hours = '0' + hours;
20638         }
20639         
20640         if(minutes < 10){
20641             minutes = '0' + minutes;
20642         }
20643         
20644         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20645         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20646         this.pop.select('button', true).first().dom.innerHTML = period;
20647         
20648     },
20649     
20650     place: function()
20651     {   
20652         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20653         
20654         var cls = ['bottom'];
20655         
20656         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20657             cls.pop();
20658             cls.push('top');
20659         }
20660         
20661         cls.push('right');
20662         
20663         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20664             cls.pop();
20665             cls.push('left');
20666         }
20667         
20668         this.picker().addClass(cls.join('-'));
20669         
20670         var _this = this;
20671         
20672         Roo.each(cls, function(c){
20673             if(c == 'bottom'){
20674                 _this.picker().setTop(_this.inputEl().getHeight());
20675                 return;
20676             }
20677             if(c == 'top'){
20678                 _this.picker().setTop(0 - _this.picker().getHeight());
20679                 return;
20680             }
20681             
20682             if(c == 'left'){
20683                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20684                 return;
20685             }
20686             if(c == 'right'){
20687                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20688                 return;
20689             }
20690         });
20691         
20692     },
20693   
20694     onFocus : function()
20695     {
20696         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20697         this.show();
20698     },
20699     
20700     onBlur : function()
20701     {
20702         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20703         this.hide();
20704     },
20705     
20706     show : function()
20707     {
20708         this.picker().show();
20709         this.pop.show();
20710         this.update();
20711         this.place();
20712         
20713         this.fireEvent('show', this, this.date);
20714     },
20715     
20716     hide : function()
20717     {
20718         this.picker().hide();
20719         this.pop.hide();
20720         
20721         this.fireEvent('hide', this, this.date);
20722     },
20723     
20724     setTime : function()
20725     {
20726         this.hide();
20727         this.setValue(this.time.format(this.format));
20728         
20729         this.fireEvent('select', this, this.date);
20730         
20731         
20732     },
20733     
20734     onMousedown: function(e){
20735         e.stopPropagation();
20736         e.preventDefault();
20737     },
20738     
20739     onIncrementHours: function()
20740     {
20741         Roo.log('onIncrementHours');
20742         this.time = this.time.add(Date.HOUR, 1);
20743         this.update();
20744         
20745     },
20746     
20747     onDecrementHours: function()
20748     {
20749         Roo.log('onDecrementHours');
20750         this.time = this.time.add(Date.HOUR, -1);
20751         this.update();
20752     },
20753     
20754     onIncrementMinutes: function()
20755     {
20756         Roo.log('onIncrementMinutes');
20757         this.time = this.time.add(Date.MINUTE, 1);
20758         this.update();
20759     },
20760     
20761     onDecrementMinutes: function()
20762     {
20763         Roo.log('onDecrementMinutes');
20764         this.time = this.time.add(Date.MINUTE, -1);
20765         this.update();
20766     },
20767     
20768     onTogglePeriod: function()
20769     {
20770         Roo.log('onTogglePeriod');
20771         this.time = this.time.add(Date.HOUR, 12);
20772         this.update();
20773     }
20774     
20775    
20776 });
20777
20778 Roo.apply(Roo.bootstrap.TimeField,  {
20779     
20780     content : {
20781         tag: 'tbody',
20782         cn: [
20783             {
20784                 tag: 'tr',
20785                 cn: [
20786                 {
20787                     tag: 'td',
20788                     colspan: '7'
20789                 }
20790                 ]
20791             }
20792         ]
20793     },
20794     
20795     footer : {
20796         tag: 'tfoot',
20797         cn: [
20798             {
20799                 tag: 'tr',
20800                 cn: [
20801                 {
20802                     tag: 'th',
20803                     colspan: '7',
20804                     cls: '',
20805                     cn: [
20806                         {
20807                             tag: 'button',
20808                             cls: 'btn btn-info ok',
20809                             html: 'OK'
20810                         }
20811                     ]
20812                 }
20813
20814                 ]
20815             }
20816         ]
20817     }
20818 });
20819
20820 Roo.apply(Roo.bootstrap.TimeField,  {
20821   
20822     template : {
20823         tag: 'div',
20824         cls: 'datepicker dropdown-menu',
20825         cn: [
20826             {
20827                 tag: 'div',
20828                 cls: 'datepicker-time',
20829                 cn: [
20830                 {
20831                     tag: 'table',
20832                     cls: 'table-condensed',
20833                     cn:[
20834                     Roo.bootstrap.TimeField.content,
20835                     Roo.bootstrap.TimeField.footer
20836                     ]
20837                 }
20838                 ]
20839             }
20840         ]
20841     }
20842 });
20843
20844  
20845
20846  /*
20847  * - LGPL
20848  *
20849  * MonthField
20850  * 
20851  */
20852
20853 /**
20854  * @class Roo.bootstrap.MonthField
20855  * @extends Roo.bootstrap.Input
20856  * Bootstrap MonthField class
20857  * 
20858  * @cfg {String} language default en
20859  * 
20860  * @constructor
20861  * Create a new MonthField
20862  * @param {Object} config The config object
20863  */
20864
20865 Roo.bootstrap.MonthField = function(config){
20866     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20867     
20868     this.addEvents({
20869         /**
20870          * @event show
20871          * Fires when this field show.
20872          * @param {Roo.bootstrap.MonthField} this
20873          * @param {Mixed} date The date value
20874          */
20875         show : true,
20876         /**
20877          * @event show
20878          * Fires when this field hide.
20879          * @param {Roo.bootstrap.MonthField} this
20880          * @param {Mixed} date The date value
20881          */
20882         hide : true,
20883         /**
20884          * @event select
20885          * Fires when select a date.
20886          * @param {Roo.bootstrap.MonthField} this
20887          * @param {String} oldvalue The old value
20888          * @param {String} newvalue The new value
20889          */
20890         select : true
20891     });
20892 };
20893
20894 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20895     
20896     onRender: function(ct, position)
20897     {
20898         
20899         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20900         
20901         this.language = this.language || 'en';
20902         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20903         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20904         
20905         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20906         this.isInline = false;
20907         this.isInput = true;
20908         this.component = this.el.select('.add-on', true).first() || false;
20909         this.component = (this.component && this.component.length === 0) ? false : this.component;
20910         this.hasInput = this.component && this.inputEL().length;
20911         
20912         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20913         
20914         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20915         
20916         this.picker().on('mousedown', this.onMousedown, this);
20917         this.picker().on('click', this.onClick, this);
20918         
20919         this.picker().addClass('datepicker-dropdown');
20920         
20921         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20922             v.setStyle('width', '189px');
20923         });
20924         
20925         this.fillMonths();
20926         
20927         this.update();
20928         
20929         if(this.isInline) {
20930             this.show();
20931         }
20932         
20933     },
20934     
20935     setValue: function(v, suppressEvent)
20936     {   
20937         var o = this.getValue();
20938         
20939         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20940         
20941         this.update();
20942
20943         if(suppressEvent !== true){
20944             this.fireEvent('select', this, o, v);
20945         }
20946         
20947     },
20948     
20949     getValue: function()
20950     {
20951         return this.value;
20952     },
20953     
20954     onClick: function(e) 
20955     {
20956         e.stopPropagation();
20957         e.preventDefault();
20958         
20959         var target = e.getTarget();
20960         
20961         if(target.nodeName.toLowerCase() === 'i'){
20962             target = Roo.get(target).dom.parentNode;
20963         }
20964         
20965         var nodeName = target.nodeName;
20966         var className = target.className;
20967         var html = target.innerHTML;
20968         
20969         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20970             return;
20971         }
20972         
20973         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20974         
20975         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20976         
20977         this.hide();
20978                         
20979     },
20980     
20981     picker : function()
20982     {
20983         return this.pickerEl;
20984     },
20985     
20986     fillMonths: function()
20987     {    
20988         var i = 0;
20989         var months = this.picker().select('>.datepicker-months td', true).first();
20990         
20991         months.dom.innerHTML = '';
20992         
20993         while (i < 12) {
20994             var month = {
20995                 tag: 'span',
20996                 cls: 'month',
20997                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20998             };
20999             
21000             months.createChild(month);
21001         }
21002         
21003     },
21004     
21005     update: function()
21006     {
21007         var _this = this;
21008         
21009         if(typeof(this.vIndex) == 'undefined' && this.value.length){
21010             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
21011         }
21012         
21013         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
21014             e.removeClass('active');
21015             
21016             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
21017                 e.addClass('active');
21018             }
21019         })
21020     },
21021     
21022     place: function()
21023     {
21024         if(this.isInline) {
21025             return;
21026         }
21027         
21028         this.picker().removeClass(['bottom', 'top']);
21029         
21030         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21031             /*
21032              * place to the top of element!
21033              *
21034              */
21035             
21036             this.picker().addClass('top');
21037             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21038             
21039             return;
21040         }
21041         
21042         this.picker().addClass('bottom');
21043         
21044         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21045     },
21046     
21047     onFocus : function()
21048     {
21049         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
21050         this.show();
21051     },
21052     
21053     onBlur : function()
21054     {
21055         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
21056         
21057         var d = this.inputEl().getValue();
21058         
21059         this.setValue(d);
21060                 
21061         this.hide();
21062     },
21063     
21064     show : function()
21065     {
21066         this.picker().show();
21067         this.picker().select('>.datepicker-months', true).first().show();
21068         this.update();
21069         this.place();
21070         
21071         this.fireEvent('show', this, this.date);
21072     },
21073     
21074     hide : function()
21075     {
21076         if(this.isInline) {
21077             return;
21078         }
21079         this.picker().hide();
21080         this.fireEvent('hide', this, this.date);
21081         
21082     },
21083     
21084     onMousedown: function(e)
21085     {
21086         e.stopPropagation();
21087         e.preventDefault();
21088     },
21089     
21090     keyup: function(e)
21091     {
21092         Roo.bootstrap.MonthField.superclass.keyup.call(this);
21093         this.update();
21094     },
21095
21096     fireKey: function(e)
21097     {
21098         if (!this.picker().isVisible()){
21099             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
21100                 this.show();
21101             }
21102             return;
21103         }
21104         
21105         var dir;
21106         
21107         switch(e.keyCode){
21108             case 27: // escape
21109                 this.hide();
21110                 e.preventDefault();
21111                 break;
21112             case 37: // left
21113             case 39: // right
21114                 dir = e.keyCode == 37 ? -1 : 1;
21115                 
21116                 this.vIndex = this.vIndex + dir;
21117                 
21118                 if(this.vIndex < 0){
21119                     this.vIndex = 0;
21120                 }
21121                 
21122                 if(this.vIndex > 11){
21123                     this.vIndex = 11;
21124                 }
21125                 
21126                 if(isNaN(this.vIndex)){
21127                     this.vIndex = 0;
21128                 }
21129                 
21130                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21131                 
21132                 break;
21133             case 38: // up
21134             case 40: // down
21135                 
21136                 dir = e.keyCode == 38 ? -1 : 1;
21137                 
21138                 this.vIndex = this.vIndex + dir * 4;
21139                 
21140                 if(this.vIndex < 0){
21141                     this.vIndex = 0;
21142                 }
21143                 
21144                 if(this.vIndex > 11){
21145                     this.vIndex = 11;
21146                 }
21147                 
21148                 if(isNaN(this.vIndex)){
21149                     this.vIndex = 0;
21150                 }
21151                 
21152                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21153                 break;
21154                 
21155             case 13: // enter
21156                 
21157                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21158                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21159                 }
21160                 
21161                 this.hide();
21162                 e.preventDefault();
21163                 break;
21164             case 9: // tab
21165                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
21166                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
21167                 }
21168                 this.hide();
21169                 break;
21170             case 16: // shift
21171             case 17: // ctrl
21172             case 18: // alt
21173                 break;
21174             default :
21175                 this.hide();
21176                 
21177         }
21178     },
21179     
21180     remove: function() 
21181     {
21182         this.picker().remove();
21183     }
21184    
21185 });
21186
21187 Roo.apply(Roo.bootstrap.MonthField,  {
21188     
21189     content : {
21190         tag: 'tbody',
21191         cn: [
21192         {
21193             tag: 'tr',
21194             cn: [
21195             {
21196                 tag: 'td',
21197                 colspan: '7'
21198             }
21199             ]
21200         }
21201         ]
21202     },
21203     
21204     dates:{
21205         en: {
21206             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21207             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
21208         }
21209     }
21210 });
21211
21212 Roo.apply(Roo.bootstrap.MonthField,  {
21213   
21214     template : {
21215         tag: 'div',
21216         cls: 'datepicker dropdown-menu roo-dynamic',
21217         cn: [
21218             {
21219                 tag: 'div',
21220                 cls: 'datepicker-months',
21221                 cn: [
21222                 {
21223                     tag: 'table',
21224                     cls: 'table-condensed',
21225                     cn:[
21226                         Roo.bootstrap.DateField.content
21227                     ]
21228                 }
21229                 ]
21230             }
21231         ]
21232     }
21233 });
21234
21235  
21236
21237  
21238  /*
21239  * - LGPL
21240  *
21241  * CheckBox
21242  * 
21243  */
21244
21245 /**
21246  * @class Roo.bootstrap.CheckBox
21247  * @extends Roo.bootstrap.Input
21248  * Bootstrap CheckBox class
21249  * 
21250  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
21251  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
21252  * @cfg {String} boxLabel The text that appears beside the checkbox
21253  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
21254  * @cfg {Boolean} checked initnal the element
21255  * @cfg {Boolean} inline inline the element (default false)
21256  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
21257  * @cfg {String} tooltip label tooltip
21258  * 
21259  * @constructor
21260  * Create a new CheckBox
21261  * @param {Object} config The config object
21262  */
21263
21264 Roo.bootstrap.CheckBox = function(config){
21265     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
21266    
21267     this.addEvents({
21268         /**
21269         * @event check
21270         * Fires when the element is checked or unchecked.
21271         * @param {Roo.bootstrap.CheckBox} this This input
21272         * @param {Boolean} checked The new checked value
21273         */
21274        check : true,
21275        /**
21276         * @event click
21277         * Fires when the element is click.
21278         * @param {Roo.bootstrap.CheckBox} this This input
21279         */
21280        click : true
21281     });
21282     
21283 };
21284
21285 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
21286   
21287     inputType: 'checkbox',
21288     inputValue: 1,
21289     valueOff: 0,
21290     boxLabel: false,
21291     checked: false,
21292     weight : false,
21293     inline: false,
21294     tooltip : '',
21295     
21296     // checkbox success does not make any sense really.. 
21297     invalidClass : "",
21298     validClass : "",
21299     
21300     
21301     getAutoCreate : function()
21302     {
21303         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
21304         
21305         var id = Roo.id();
21306         
21307         var cfg = {};
21308         
21309         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
21310         
21311         if(this.inline){
21312             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
21313         }
21314         
21315         var input =  {
21316             tag: 'input',
21317             id : id,
21318             type : this.inputType,
21319             value : this.inputValue,
21320             cls : 'roo-' + this.inputType, //'form-box',
21321             placeholder : this.placeholder || ''
21322             
21323         };
21324         
21325         if(this.inputType != 'radio'){
21326             var hidden =  {
21327                 tag: 'input',
21328                 type : 'hidden',
21329                 cls : 'roo-hidden-value',
21330                 value : this.checked ? this.inputValue : this.valueOff
21331             };
21332         }
21333         
21334             
21335         if (this.weight) { // Validity check?
21336             cfg.cls += " " + this.inputType + "-" + this.weight;
21337         }
21338         
21339         if (this.disabled) {
21340             input.disabled=true;
21341         }
21342         
21343         if(this.checked){
21344             input.checked = this.checked;
21345         }
21346         
21347         if (this.name) {
21348             
21349             input.name = this.name;
21350             
21351             if(this.inputType != 'radio'){
21352                 hidden.name = this.name;
21353                 input.name = '_hidden_' + this.name;
21354             }
21355         }
21356         
21357         if (this.size) {
21358             input.cls += ' input-' + this.size;
21359         }
21360         
21361         var settings=this;
21362         
21363         ['xs','sm','md','lg'].map(function(size){
21364             if (settings[size]) {
21365                 cfg.cls += ' col-' + size + '-' + settings[size];
21366             }
21367         });
21368         
21369         var inputblock = input;
21370          
21371         if (this.before || this.after) {
21372             
21373             inputblock = {
21374                 cls : 'input-group',
21375                 cn :  [] 
21376             };
21377             
21378             if (this.before) {
21379                 inputblock.cn.push({
21380                     tag :'span',
21381                     cls : 'input-group-addon',
21382                     html : this.before
21383                 });
21384             }
21385             
21386             inputblock.cn.push(input);
21387             
21388             if(this.inputType != 'radio'){
21389                 inputblock.cn.push(hidden);
21390             }
21391             
21392             if (this.after) {
21393                 inputblock.cn.push({
21394                     tag :'span',
21395                     cls : 'input-group-addon',
21396                     html : this.after
21397                 });
21398             }
21399             
21400         }
21401         var boxLabelCfg = false;
21402         
21403         if(this.boxLabel){
21404            
21405             boxLabelCfg = {
21406                 tag: 'label',
21407                 //'for': id, // box label is handled by onclick - so no for...
21408                 cls: 'box-label',
21409                 html: this.boxLabel
21410             };
21411             if(this.tooltip){
21412                 boxLabelCfg.tooltip = this.tooltip;
21413             }
21414              
21415         }
21416         
21417         
21418         if (align ==='left' && this.fieldLabel.length) {
21419 //                Roo.log("left and has label");
21420             cfg.cn = [
21421                 {
21422                     tag: 'label',
21423                     'for' :  id,
21424                     cls : 'control-label',
21425                     html : this.fieldLabel
21426                 },
21427                 {
21428                     cls : "", 
21429                     cn: [
21430                         inputblock
21431                     ]
21432                 }
21433             ];
21434             
21435             if (boxLabelCfg) {
21436                 cfg.cn[1].cn.push(boxLabelCfg);
21437             }
21438             
21439             if(this.labelWidth > 12){
21440                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21441             }
21442             
21443             if(this.labelWidth < 13 && this.labelmd == 0){
21444                 this.labelmd = this.labelWidth;
21445             }
21446             
21447             if(this.labellg > 0){
21448                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21449                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21450             }
21451             
21452             if(this.labelmd > 0){
21453                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21454                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21455             }
21456             
21457             if(this.labelsm > 0){
21458                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21459                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21460             }
21461             
21462             if(this.labelxs > 0){
21463                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21464                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21465             }
21466             
21467         } else if ( this.fieldLabel.length) {
21468 //                Roo.log(" label");
21469                 cfg.cn = [
21470                    
21471                     {
21472                         tag: this.boxLabel ? 'span' : 'label',
21473                         'for': id,
21474                         cls: 'control-label box-input-label',
21475                         //cls : 'input-group-addon',
21476                         html : this.fieldLabel
21477                     },
21478                     
21479                     inputblock
21480                     
21481                 ];
21482                 if (boxLabelCfg) {
21483                     cfg.cn.push(boxLabelCfg);
21484                 }
21485
21486         } else {
21487             
21488 //                Roo.log(" no label && no align");
21489                 cfg.cn = [  inputblock ] ;
21490                 if (boxLabelCfg) {
21491                     cfg.cn.push(boxLabelCfg);
21492                 }
21493
21494                 
21495         }
21496         
21497        
21498         
21499         if(this.inputType != 'radio'){
21500             cfg.cn.push(hidden);
21501         }
21502         
21503         return cfg;
21504         
21505     },
21506     
21507     /**
21508      * return the real input element.
21509      */
21510     inputEl: function ()
21511     {
21512         return this.el.select('input.roo-' + this.inputType,true).first();
21513     },
21514     hiddenEl: function ()
21515     {
21516         return this.el.select('input.roo-hidden-value',true).first();
21517     },
21518     
21519     labelEl: function()
21520     {
21521         return this.el.select('label.control-label',true).first();
21522     },
21523     /* depricated... */
21524     
21525     label: function()
21526     {
21527         return this.labelEl();
21528     },
21529     
21530     boxLabelEl: function()
21531     {
21532         return this.el.select('label.box-label',true).first();
21533     },
21534     
21535     initEvents : function()
21536     {
21537 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21538         
21539         this.inputEl().on('click', this.onClick,  this);
21540         
21541         if (this.boxLabel) { 
21542             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21543         }
21544         
21545         this.startValue = this.getValue();
21546         
21547         if(this.groupId){
21548             Roo.bootstrap.CheckBox.register(this);
21549         }
21550     },
21551     
21552     onClick : function(e)
21553     {   
21554         if(this.fireEvent('click', this, e) !== false){
21555             this.setChecked(!this.checked);
21556         }
21557         
21558     },
21559     
21560     setChecked : function(state,suppressEvent)
21561     {
21562         this.startValue = this.getValue();
21563
21564         if(this.inputType == 'radio'){
21565             
21566             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21567                 e.dom.checked = false;
21568             });
21569             
21570             this.inputEl().dom.checked = true;
21571             
21572             this.inputEl().dom.value = this.inputValue;
21573             
21574             if(suppressEvent !== true){
21575                 this.fireEvent('check', this, true);
21576             }
21577             
21578             this.validate();
21579             
21580             return;
21581         }
21582         
21583         this.checked = state;
21584         
21585         this.inputEl().dom.checked = state;
21586         
21587         
21588         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21589         
21590         if(suppressEvent !== true){
21591             this.fireEvent('check', this, state);
21592         }
21593         
21594         this.validate();
21595     },
21596     
21597     getValue : function()
21598     {
21599         if(this.inputType == 'radio'){
21600             return this.getGroupValue();
21601         }
21602         
21603         return this.hiddenEl().dom.value;
21604         
21605     },
21606     
21607     getGroupValue : function()
21608     {
21609         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21610             return '';
21611         }
21612         
21613         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21614     },
21615     
21616     setValue : function(v,suppressEvent)
21617     {
21618         if(this.inputType == 'radio'){
21619             this.setGroupValue(v, suppressEvent);
21620             return;
21621         }
21622         
21623         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21624         
21625         this.validate();
21626     },
21627     
21628     setGroupValue : function(v, suppressEvent)
21629     {
21630         this.startValue = this.getValue();
21631         
21632         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21633             e.dom.checked = false;
21634             
21635             if(e.dom.value == v){
21636                 e.dom.checked = true;
21637             }
21638         });
21639         
21640         if(suppressEvent !== true){
21641             this.fireEvent('check', this, true);
21642         }
21643
21644         this.validate();
21645         
21646         return;
21647     },
21648     
21649     validate : function()
21650     {
21651         if(this.getVisibilityEl().hasClass('hidden')){
21652             return true;
21653         }
21654         
21655         if(
21656                 this.disabled || 
21657                 (this.inputType == 'radio' && this.validateRadio()) ||
21658                 (this.inputType == 'checkbox' && this.validateCheckbox())
21659         ){
21660             this.markValid();
21661             return true;
21662         }
21663         
21664         this.markInvalid();
21665         return false;
21666     },
21667     
21668     validateRadio : function()
21669     {
21670         if(this.getVisibilityEl().hasClass('hidden')){
21671             return true;
21672         }
21673         
21674         if(this.allowBlank){
21675             return true;
21676         }
21677         
21678         var valid = false;
21679         
21680         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21681             if(!e.dom.checked){
21682                 return;
21683             }
21684             
21685             valid = true;
21686             
21687             return false;
21688         });
21689         
21690         return valid;
21691     },
21692     
21693     validateCheckbox : function()
21694     {
21695         if(!this.groupId){
21696             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21697             //return (this.getValue() == this.inputValue) ? true : false;
21698         }
21699         
21700         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21701         
21702         if(!group){
21703             return false;
21704         }
21705         
21706         var r = false;
21707         
21708         for(var i in group){
21709             if(group[i].el.isVisible(true)){
21710                 r = false;
21711                 break;
21712             }
21713             
21714             r = true;
21715         }
21716         
21717         for(var i in group){
21718             if(r){
21719                 break;
21720             }
21721             
21722             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21723         }
21724         
21725         return r;
21726     },
21727     
21728     /**
21729      * Mark this field as valid
21730      */
21731     markValid : function()
21732     {
21733         var _this = this;
21734         
21735         this.fireEvent('valid', this);
21736         
21737         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21738         
21739         if(this.groupId){
21740             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21741         }
21742         
21743         if(label){
21744             label.markValid();
21745         }
21746
21747         if(this.inputType == 'radio'){
21748             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21749                 var fg = e.findParent('.form-group', false, true);
21750                 if (Roo.bootstrap.version == 3) {
21751                     fg.removeClass([_this.invalidClass, _this.validClass]);
21752                     fg.addClass(_this.validClass);
21753                 } else {
21754                     fg.removeClass(['is-valid', 'is-invalid']);
21755                     fg.addClass('is-valid');
21756                 }
21757             });
21758             
21759             return;
21760         }
21761
21762         if(!this.groupId){
21763             var fg = this.el.findParent('.form-group', false, true);
21764             if (Roo.bootstrap.version == 3) {
21765                 fg.removeClass([this.invalidClass, this.validClass]);
21766                 fg.addClass(this.validClass);
21767             } else {
21768                 fg.removeClass(['is-valid', 'is-invalid']);
21769                 fg.addClass('is-valid');
21770             }
21771             return;
21772         }
21773         
21774         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21775         
21776         if(!group){
21777             return;
21778         }
21779         
21780         for(var i in group){
21781             var fg = group[i].el.findParent('.form-group', false, true);
21782             if (Roo.bootstrap.version == 3) {
21783                 fg.removeClass([this.invalidClass, this.validClass]);
21784                 fg.addClass(this.validClass);
21785             } else {
21786                 fg.removeClass(['is-valid', 'is-invalid']);
21787                 fg.addClass('is-valid');
21788             }
21789         }
21790     },
21791     
21792      /**
21793      * Mark this field as invalid
21794      * @param {String} msg The validation message
21795      */
21796     markInvalid : function(msg)
21797     {
21798         if(this.allowBlank){
21799             return;
21800         }
21801         
21802         var _this = this;
21803         
21804         this.fireEvent('invalid', this, msg);
21805         
21806         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21807         
21808         if(this.groupId){
21809             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21810         }
21811         
21812         if(label){
21813             label.markInvalid();
21814         }
21815             
21816         if(this.inputType == 'radio'){
21817             
21818             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21819                 var fg = e.findParent('.form-group', false, true);
21820                 if (Roo.bootstrap.version == 3) {
21821                     fg.removeClass([_this.invalidClass, _this.validClass]);
21822                     fg.addClass(_this.invalidClass);
21823                 } else {
21824                     fg.removeClass(['is-invalid', 'is-valid']);
21825                     fg.addClass('is-invalid');
21826                 }
21827             });
21828             
21829             return;
21830         }
21831         
21832         if(!this.groupId){
21833             var fg = this.el.findParent('.form-group', false, true);
21834             if (Roo.bootstrap.version == 3) {
21835                 fg.removeClass([_this.invalidClass, _this.validClass]);
21836                 fg.addClass(_this.invalidClass);
21837             } else {
21838                 fg.removeClass(['is-invalid', 'is-valid']);
21839                 fg.addClass('is-invalid');
21840             }
21841             return;
21842         }
21843         
21844         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21845         
21846         if(!group){
21847             return;
21848         }
21849         
21850         for(var i in group){
21851             var fg = group[i].el.findParent('.form-group', false, true);
21852             if (Roo.bootstrap.version == 3) {
21853                 fg.removeClass([_this.invalidClass, _this.validClass]);
21854                 fg.addClass(_this.invalidClass);
21855             } else {
21856                 fg.removeClass(['is-invalid', 'is-valid']);
21857                 fg.addClass('is-invalid');
21858             }
21859         }
21860         
21861     },
21862     
21863     clearInvalid : function()
21864     {
21865         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21866         
21867         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21868         
21869         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21870         
21871         if (label && label.iconEl) {
21872             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21873             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21874         }
21875     },
21876     
21877     disable : function()
21878     {
21879         if(this.inputType != 'radio'){
21880             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21881             return;
21882         }
21883         
21884         var _this = this;
21885         
21886         if(this.rendered){
21887             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21888                 _this.getActionEl().addClass(this.disabledClass);
21889                 e.dom.disabled = true;
21890             });
21891         }
21892         
21893         this.disabled = true;
21894         this.fireEvent("disable", this);
21895         return this;
21896     },
21897
21898     enable : function()
21899     {
21900         if(this.inputType != 'radio'){
21901             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21902             return;
21903         }
21904         
21905         var _this = this;
21906         
21907         if(this.rendered){
21908             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21909                 _this.getActionEl().removeClass(this.disabledClass);
21910                 e.dom.disabled = false;
21911             });
21912         }
21913         
21914         this.disabled = false;
21915         this.fireEvent("enable", this);
21916         return this;
21917     },
21918     
21919     setBoxLabel : function(v)
21920     {
21921         this.boxLabel = v;
21922         
21923         if(this.rendered){
21924             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21925         }
21926     }
21927
21928 });
21929
21930 Roo.apply(Roo.bootstrap.CheckBox, {
21931     
21932     groups: {},
21933     
21934      /**
21935     * register a CheckBox Group
21936     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21937     */
21938     register : function(checkbox)
21939     {
21940         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21941             this.groups[checkbox.groupId] = {};
21942         }
21943         
21944         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21945             return;
21946         }
21947         
21948         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21949         
21950     },
21951     /**
21952     * fetch a CheckBox Group based on the group ID
21953     * @param {string} the group ID
21954     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21955     */
21956     get: function(groupId) {
21957         if (typeof(this.groups[groupId]) == 'undefined') {
21958             return false;
21959         }
21960         
21961         return this.groups[groupId] ;
21962     }
21963     
21964     
21965 });
21966 /*
21967  * - LGPL
21968  *
21969  * RadioItem
21970  * 
21971  */
21972
21973 /**
21974  * @class Roo.bootstrap.Radio
21975  * @extends Roo.bootstrap.Component
21976  * Bootstrap Radio class
21977  * @cfg {String} boxLabel - the label associated
21978  * @cfg {String} value - the value of radio
21979  * 
21980  * @constructor
21981  * Create a new Radio
21982  * @param {Object} config The config object
21983  */
21984 Roo.bootstrap.Radio = function(config){
21985     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21986     
21987 };
21988
21989 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21990     
21991     boxLabel : '',
21992     
21993     value : '',
21994     
21995     getAutoCreate : function()
21996     {
21997         var cfg = {
21998             tag : 'div',
21999             cls : 'form-group radio',
22000             cn : [
22001                 {
22002                     tag : 'label',
22003                     cls : 'box-label',
22004                     html : this.boxLabel
22005                 }
22006             ]
22007         };
22008         
22009         return cfg;
22010     },
22011     
22012     initEvents : function() 
22013     {
22014         this.parent().register(this);
22015         
22016         this.el.on('click', this.onClick, this);
22017         
22018     },
22019     
22020     onClick : function(e)
22021     {
22022         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
22023             this.setChecked(true);
22024         }
22025     },
22026     
22027     setChecked : function(state, suppressEvent)
22028     {
22029         this.parent().setValue(this.value, suppressEvent);
22030         
22031     },
22032     
22033     setBoxLabel : function(v)
22034     {
22035         this.boxLabel = v;
22036         
22037         if(this.rendered){
22038             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
22039         }
22040     }
22041     
22042 });
22043  
22044
22045  /*
22046  * - LGPL
22047  *
22048  * Input
22049  * 
22050  */
22051
22052 /**
22053  * @class Roo.bootstrap.SecurePass
22054  * @extends Roo.bootstrap.Input
22055  * Bootstrap SecurePass class
22056  *
22057  * 
22058  * @constructor
22059  * Create a new SecurePass
22060  * @param {Object} config The config object
22061  */
22062  
22063 Roo.bootstrap.SecurePass = function (config) {
22064     // these go here, so the translation tool can replace them..
22065     this.errors = {
22066         PwdEmpty: "Please type a password, and then retype it to confirm.",
22067         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22068         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22069         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22070         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22071         FNInPwd: "Your password can't contain your first name. Please type a different password.",
22072         LNInPwd: "Your password can't contain your last name. Please type a different password.",
22073         TooWeak: "Your password is Too Weak."
22074     },
22075     this.meterLabel = "Password strength:";
22076     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
22077     this.meterClass = [
22078         "roo-password-meter-tooweak", 
22079         "roo-password-meter-weak", 
22080         "roo-password-meter-medium", 
22081         "roo-password-meter-strong", 
22082         "roo-password-meter-grey"
22083     ];
22084     
22085     this.errors = {};
22086     
22087     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
22088 }
22089
22090 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
22091     /**
22092      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
22093      * {
22094      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
22095      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
22096      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
22097      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
22098      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
22099      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
22100      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
22101      * })
22102      */
22103     // private
22104     
22105     meterWidth: 300,
22106     errorMsg :'',    
22107     errors: false,
22108     imageRoot: '/',
22109     /**
22110      * @cfg {String/Object} Label for the strength meter (defaults to
22111      * 'Password strength:')
22112      */
22113     // private
22114     meterLabel: '',
22115     /**
22116      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
22117      * ['Weak', 'Medium', 'Strong'])
22118      */
22119     // private    
22120     pwdStrengths: false,    
22121     // private
22122     strength: 0,
22123     // private
22124     _lastPwd: null,
22125     // private
22126     kCapitalLetter: 0,
22127     kSmallLetter: 1,
22128     kDigit: 2,
22129     kPunctuation: 3,
22130     
22131     insecure: false,
22132     // private
22133     initEvents: function ()
22134     {
22135         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
22136
22137         if (this.el.is('input[type=password]') && Roo.isSafari) {
22138             this.el.on('keydown', this.SafariOnKeyDown, this);
22139         }
22140
22141         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
22142     },
22143     // private
22144     onRender: function (ct, position)
22145     {
22146         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
22147         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
22148         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
22149
22150         this.trigger.createChild({
22151                    cn: [
22152                     {
22153                     //id: 'PwdMeter',
22154                     tag: 'div',
22155                     cls: 'roo-password-meter-grey col-xs-12',
22156                     style: {
22157                         //width: 0,
22158                         //width: this.meterWidth + 'px'                                                
22159                         }
22160                     },
22161                     {                            
22162                          cls: 'roo-password-meter-text'                          
22163                     }
22164                 ]            
22165         });
22166
22167          
22168         if (this.hideTrigger) {
22169             this.trigger.setDisplayed(false);
22170         }
22171         this.setSize(this.width || '', this.height || '');
22172     },
22173     // private
22174     onDestroy: function ()
22175     {
22176         if (this.trigger) {
22177             this.trigger.removeAllListeners();
22178             this.trigger.remove();
22179         }
22180         if (this.wrap) {
22181             this.wrap.remove();
22182         }
22183         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
22184     },
22185     // private
22186     checkStrength: function ()
22187     {
22188         var pwd = this.inputEl().getValue();
22189         if (pwd == this._lastPwd) {
22190             return;
22191         }
22192
22193         var strength;
22194         if (this.ClientSideStrongPassword(pwd)) {
22195             strength = 3;
22196         } else if (this.ClientSideMediumPassword(pwd)) {
22197             strength = 2;
22198         } else if (this.ClientSideWeakPassword(pwd)) {
22199             strength = 1;
22200         } else {
22201             strength = 0;
22202         }
22203         
22204         Roo.log('strength1: ' + strength);
22205         
22206         //var pm = this.trigger.child('div/div/div').dom;
22207         var pm = this.trigger.child('div/div');
22208         pm.removeClass(this.meterClass);
22209         pm.addClass(this.meterClass[strength]);
22210                 
22211         
22212         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22213                 
22214         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
22215         
22216         this._lastPwd = pwd;
22217     },
22218     reset: function ()
22219     {
22220         Roo.bootstrap.SecurePass.superclass.reset.call(this);
22221         
22222         this._lastPwd = '';
22223         
22224         var pm = this.trigger.child('div/div');
22225         pm.removeClass(this.meterClass);
22226         pm.addClass('roo-password-meter-grey');        
22227         
22228         
22229         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22230         
22231         pt.innerHTML = '';
22232         this.inputEl().dom.type='password';
22233     },
22234     // private
22235     validateValue: function (value)
22236     {
22237         
22238         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
22239             return false;
22240         }
22241         if (value.length == 0) {
22242             if (this.allowBlank) {
22243                 this.clearInvalid();
22244                 return true;
22245             }
22246
22247             this.markInvalid(this.errors.PwdEmpty);
22248             this.errorMsg = this.errors.PwdEmpty;
22249             return false;
22250         }
22251         
22252         if(this.insecure){
22253             return true;
22254         }
22255         
22256         if ('[\x21-\x7e]*'.match(value)) {
22257             this.markInvalid(this.errors.PwdBadChar);
22258             this.errorMsg = this.errors.PwdBadChar;
22259             return false;
22260         }
22261         if (value.length < 6) {
22262             this.markInvalid(this.errors.PwdShort);
22263             this.errorMsg = this.errors.PwdShort;
22264             return false;
22265         }
22266         if (value.length > 16) {
22267             this.markInvalid(this.errors.PwdLong);
22268             this.errorMsg = this.errors.PwdLong;
22269             return false;
22270         }
22271         var strength;
22272         if (this.ClientSideStrongPassword(value)) {
22273             strength = 3;
22274         } else if (this.ClientSideMediumPassword(value)) {
22275             strength = 2;
22276         } else if (this.ClientSideWeakPassword(value)) {
22277             strength = 1;
22278         } else {
22279             strength = 0;
22280         }
22281
22282         
22283         if (strength < 2) {
22284             //this.markInvalid(this.errors.TooWeak);
22285             this.errorMsg = this.errors.TooWeak;
22286             //return false;
22287         }
22288         
22289         
22290         console.log('strength2: ' + strength);
22291         
22292         //var pm = this.trigger.child('div/div/div').dom;
22293         
22294         var pm = this.trigger.child('div/div');
22295         pm.removeClass(this.meterClass);
22296         pm.addClass(this.meterClass[strength]);
22297                 
22298         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
22299                 
22300         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
22301         
22302         this.errorMsg = ''; 
22303         return true;
22304     },
22305     // private
22306     CharacterSetChecks: function (type)
22307     {
22308         this.type = type;
22309         this.fResult = false;
22310     },
22311     // private
22312     isctype: function (character, type)
22313     {
22314         switch (type) {  
22315             case this.kCapitalLetter:
22316                 if (character >= 'A' && character <= 'Z') {
22317                     return true;
22318                 }
22319                 break;
22320             
22321             case this.kSmallLetter:
22322                 if (character >= 'a' && character <= 'z') {
22323                     return true;
22324                 }
22325                 break;
22326             
22327             case this.kDigit:
22328                 if (character >= '0' && character <= '9') {
22329                     return true;
22330                 }
22331                 break;
22332             
22333             case this.kPunctuation:
22334                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
22335                     return true;
22336                 }
22337                 break;
22338             
22339             default:
22340                 return false;
22341         }
22342
22343     },
22344     // private
22345     IsLongEnough: function (pwd, size)
22346     {
22347         return !(pwd == null || isNaN(size) || pwd.length < size);
22348     },
22349     // private
22350     SpansEnoughCharacterSets: function (word, nb)
22351     {
22352         if (!this.IsLongEnough(word, nb))
22353         {
22354             return false;
22355         }
22356
22357         var characterSetChecks = new Array(
22358             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
22359             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
22360         );
22361         
22362         for (var index = 0; index < word.length; ++index) {
22363             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22364                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
22365                     characterSetChecks[nCharSet].fResult = true;
22366                     break;
22367                 }
22368             }
22369         }
22370
22371         var nCharSets = 0;
22372         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22373             if (characterSetChecks[nCharSet].fResult) {
22374                 ++nCharSets;
22375             }
22376         }
22377
22378         if (nCharSets < nb) {
22379             return false;
22380         }
22381         return true;
22382     },
22383     // private
22384     ClientSideStrongPassword: function (pwd)
22385     {
22386         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22387     },
22388     // private
22389     ClientSideMediumPassword: function (pwd)
22390     {
22391         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22392     },
22393     // private
22394     ClientSideWeakPassword: function (pwd)
22395     {
22396         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22397     }
22398           
22399 })//<script type="text/javascript">
22400
22401 /*
22402  * Based  Ext JS Library 1.1.1
22403  * Copyright(c) 2006-2007, Ext JS, LLC.
22404  * LGPL
22405  *
22406  */
22407  
22408 /**
22409  * @class Roo.HtmlEditorCore
22410  * @extends Roo.Component
22411  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22412  *
22413  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22414  */
22415
22416 Roo.HtmlEditorCore = function(config){
22417     
22418     
22419     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22420     
22421     
22422     this.addEvents({
22423         /**
22424          * @event initialize
22425          * Fires when the editor is fully initialized (including the iframe)
22426          * @param {Roo.HtmlEditorCore} this
22427          */
22428         initialize: true,
22429         /**
22430          * @event activate
22431          * Fires when the editor is first receives the focus. Any insertion must wait
22432          * until after this event.
22433          * @param {Roo.HtmlEditorCore} this
22434          */
22435         activate: true,
22436          /**
22437          * @event beforesync
22438          * Fires before the textarea is updated with content from the editor iframe. Return false
22439          * to cancel the sync.
22440          * @param {Roo.HtmlEditorCore} this
22441          * @param {String} html
22442          */
22443         beforesync: true,
22444          /**
22445          * @event beforepush
22446          * Fires before the iframe editor is updated with content from the textarea. Return false
22447          * to cancel the push.
22448          * @param {Roo.HtmlEditorCore} this
22449          * @param {String} html
22450          */
22451         beforepush: true,
22452          /**
22453          * @event sync
22454          * Fires when the textarea is updated with content from the editor iframe.
22455          * @param {Roo.HtmlEditorCore} this
22456          * @param {String} html
22457          */
22458         sync: true,
22459          /**
22460          * @event push
22461          * Fires when the iframe editor is updated with content from the textarea.
22462          * @param {Roo.HtmlEditorCore} this
22463          * @param {String} html
22464          */
22465         push: true,
22466         
22467         /**
22468          * @event editorevent
22469          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22470          * @param {Roo.HtmlEditorCore} this
22471          */
22472         editorevent: true
22473         
22474     });
22475     
22476     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22477     
22478     // defaults : white / black...
22479     this.applyBlacklists();
22480     
22481     
22482     
22483 };
22484
22485
22486 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22487
22488
22489      /**
22490      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22491      */
22492     
22493     owner : false,
22494     
22495      /**
22496      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22497      *                        Roo.resizable.
22498      */
22499     resizable : false,
22500      /**
22501      * @cfg {Number} height (in pixels)
22502      */   
22503     height: 300,
22504    /**
22505      * @cfg {Number} width (in pixels)
22506      */   
22507     width: 500,
22508     
22509     /**
22510      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22511      * 
22512      */
22513     stylesheets: false,
22514     
22515     // id of frame..
22516     frameId: false,
22517     
22518     // private properties
22519     validationEvent : false,
22520     deferHeight: true,
22521     initialized : false,
22522     activated : false,
22523     sourceEditMode : false,
22524     onFocus : Roo.emptyFn,
22525     iframePad:3,
22526     hideMode:'offsets',
22527     
22528     clearUp: true,
22529     
22530     // blacklist + whitelisted elements..
22531     black: false,
22532     white: false,
22533      
22534     bodyCls : '',
22535
22536     /**
22537      * Protected method that will not generally be called directly. It
22538      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22539      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22540      */
22541     getDocMarkup : function(){
22542         // body styles..
22543         var st = '';
22544         
22545         // inherit styels from page...?? 
22546         if (this.stylesheets === false) {
22547             
22548             Roo.get(document.head).select('style').each(function(node) {
22549                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22550             });
22551             
22552             Roo.get(document.head).select('link').each(function(node) { 
22553                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22554             });
22555             
22556         } else if (!this.stylesheets.length) {
22557                 // simple..
22558                 st = '<style type="text/css">' +
22559                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22560                    '</style>';
22561         } else { 
22562             st = '<style type="text/css">' +
22563                     this.stylesheets +
22564                 '</style>';
22565         }
22566         
22567         st +=  '<style type="text/css">' +
22568             'IMG { cursor: pointer } ' +
22569         '</style>';
22570
22571         var cls = 'roo-htmleditor-body';
22572         
22573         if(this.bodyCls.length){
22574             cls += ' ' + this.bodyCls;
22575         }
22576         
22577         return '<html><head>' + st  +
22578             //<style type="text/css">' +
22579             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22580             //'</style>' +
22581             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
22582     },
22583
22584     // private
22585     onRender : function(ct, position)
22586     {
22587         var _t = this;
22588         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22589         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22590         
22591         
22592         this.el.dom.style.border = '0 none';
22593         this.el.dom.setAttribute('tabIndex', -1);
22594         this.el.addClass('x-hidden hide');
22595         
22596         
22597         
22598         if(Roo.isIE){ // fix IE 1px bogus margin
22599             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22600         }
22601        
22602         
22603         this.frameId = Roo.id();
22604         
22605          
22606         
22607         var iframe = this.owner.wrap.createChild({
22608             tag: 'iframe',
22609             cls: 'form-control', // bootstrap..
22610             id: this.frameId,
22611             name: this.frameId,
22612             frameBorder : 'no',
22613             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22614         }, this.el
22615         );
22616         
22617         
22618         this.iframe = iframe.dom;
22619
22620          this.assignDocWin();
22621         
22622         this.doc.designMode = 'on';
22623        
22624         this.doc.open();
22625         this.doc.write(this.getDocMarkup());
22626         this.doc.close();
22627
22628         
22629         var task = { // must defer to wait for browser to be ready
22630             run : function(){
22631                 //console.log("run task?" + this.doc.readyState);
22632                 this.assignDocWin();
22633                 if(this.doc.body || this.doc.readyState == 'complete'){
22634                     try {
22635                         this.doc.designMode="on";
22636                     } catch (e) {
22637                         return;
22638                     }
22639                     Roo.TaskMgr.stop(task);
22640                     this.initEditor.defer(10, this);
22641                 }
22642             },
22643             interval : 10,
22644             duration: 10000,
22645             scope: this
22646         };
22647         Roo.TaskMgr.start(task);
22648
22649     },
22650
22651     // private
22652     onResize : function(w, h)
22653     {
22654          Roo.log('resize: ' +w + ',' + h );
22655         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22656         if(!this.iframe){
22657             return;
22658         }
22659         if(typeof w == 'number'){
22660             
22661             this.iframe.style.width = w + 'px';
22662         }
22663         if(typeof h == 'number'){
22664             
22665             this.iframe.style.height = h + 'px';
22666             if(this.doc){
22667                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22668             }
22669         }
22670         
22671     },
22672
22673     /**
22674      * Toggles the editor between standard and source edit mode.
22675      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22676      */
22677     toggleSourceEdit : function(sourceEditMode){
22678         
22679         this.sourceEditMode = sourceEditMode === true;
22680         
22681         if(this.sourceEditMode){
22682  
22683             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22684             
22685         }else{
22686             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22687             //this.iframe.className = '';
22688             this.deferFocus();
22689         }
22690         //this.setSize(this.owner.wrap.getSize());
22691         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22692     },
22693
22694     
22695   
22696
22697     /**
22698      * Protected method that will not generally be called directly. If you need/want
22699      * custom HTML cleanup, this is the method you should override.
22700      * @param {String} html The HTML to be cleaned
22701      * return {String} The cleaned HTML
22702      */
22703     cleanHtml : function(html){
22704         html = String(html);
22705         if(html.length > 5){
22706             if(Roo.isSafari){ // strip safari nonsense
22707                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22708             }
22709         }
22710         if(html == '&nbsp;'){
22711             html = '';
22712         }
22713         return html;
22714     },
22715
22716     /**
22717      * HTML Editor -> Textarea
22718      * Protected method that will not generally be called directly. Syncs the contents
22719      * of the editor iframe with the textarea.
22720      */
22721     syncValue : function(){
22722         if(this.initialized){
22723             var bd = (this.doc.body || this.doc.documentElement);
22724             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22725             var html = bd.innerHTML;
22726             if(Roo.isSafari){
22727                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22728                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22729                 if(m && m[1]){
22730                     html = '<div style="'+m[0]+'">' + html + '</div>';
22731                 }
22732             }
22733             html = this.cleanHtml(html);
22734             // fix up the special chars.. normaly like back quotes in word...
22735             // however we do not want to do this with chinese..
22736             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
22737                 
22738                 var cc = match.charCodeAt();
22739
22740                 // Get the character value, handling surrogate pairs
22741                 if (match.length == 2) {
22742                     // It's a surrogate pair, calculate the Unicode code point
22743                     var high = match.charCodeAt(0) - 0xD800;
22744                     var low  = match.charCodeAt(1) - 0xDC00;
22745                     cc = (high * 0x400) + low + 0x10000;
22746                 }  else if (
22747                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22748                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22749                     (cc >= 0xf900 && cc < 0xfb00 )
22750                 ) {
22751                         return match;
22752                 }  
22753          
22754                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
22755                 return "&#" + cc + ";";
22756                 
22757                 
22758             });
22759             
22760             
22761              
22762             if(this.owner.fireEvent('beforesync', this, html) !== false){
22763                 this.el.dom.value = html;
22764                 this.owner.fireEvent('sync', this, html);
22765             }
22766         }
22767     },
22768
22769     /**
22770      * Protected method that will not generally be called directly. Pushes the value of the textarea
22771      * into the iframe editor.
22772      */
22773     pushValue : function(){
22774         if(this.initialized){
22775             var v = this.el.dom.value.trim();
22776             
22777 //            if(v.length < 1){
22778 //                v = '&#160;';
22779 //            }
22780             
22781             if(this.owner.fireEvent('beforepush', this, v) !== false){
22782                 var d = (this.doc.body || this.doc.documentElement);
22783                 d.innerHTML = v;
22784                 this.cleanUpPaste();
22785                 this.el.dom.value = d.innerHTML;
22786                 this.owner.fireEvent('push', this, v);
22787             }
22788         }
22789     },
22790
22791     // private
22792     deferFocus : function(){
22793         this.focus.defer(10, this);
22794     },
22795
22796     // doc'ed in Field
22797     focus : function(){
22798         if(this.win && !this.sourceEditMode){
22799             this.win.focus();
22800         }else{
22801             this.el.focus();
22802         }
22803     },
22804     
22805     assignDocWin: function()
22806     {
22807         var iframe = this.iframe;
22808         
22809          if(Roo.isIE){
22810             this.doc = iframe.contentWindow.document;
22811             this.win = iframe.contentWindow;
22812         } else {
22813 //            if (!Roo.get(this.frameId)) {
22814 //                return;
22815 //            }
22816 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22817 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22818             
22819             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22820                 return;
22821             }
22822             
22823             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22824             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22825         }
22826     },
22827     
22828     // private
22829     initEditor : function(){
22830         //console.log("INIT EDITOR");
22831         this.assignDocWin();
22832         
22833         
22834         
22835         this.doc.designMode="on";
22836         this.doc.open();
22837         this.doc.write(this.getDocMarkup());
22838         this.doc.close();
22839         
22840         var dbody = (this.doc.body || this.doc.documentElement);
22841         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22842         // this copies styles from the containing element into thsi one..
22843         // not sure why we need all of this..
22844         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22845         
22846         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22847         //ss['background-attachment'] = 'fixed'; // w3c
22848         dbody.bgProperties = 'fixed'; // ie
22849         //Roo.DomHelper.applyStyles(dbody, ss);
22850         Roo.EventManager.on(this.doc, {
22851             //'mousedown': this.onEditorEvent,
22852             'mouseup': this.onEditorEvent,
22853             'dblclick': this.onEditorEvent,
22854             'click': this.onEditorEvent,
22855             'keyup': this.onEditorEvent,
22856             buffer:100,
22857             scope: this
22858         });
22859         if(Roo.isGecko){
22860             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22861         }
22862         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22863             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22864         }
22865         this.initialized = true;
22866
22867         this.owner.fireEvent('initialize', this);
22868         this.pushValue();
22869     },
22870
22871     // private
22872     onDestroy : function(){
22873         
22874         
22875         
22876         if(this.rendered){
22877             
22878             //for (var i =0; i < this.toolbars.length;i++) {
22879             //    // fixme - ask toolbars for heights?
22880             //    this.toolbars[i].onDestroy();
22881            // }
22882             
22883             //this.wrap.dom.innerHTML = '';
22884             //this.wrap.remove();
22885         }
22886     },
22887
22888     // private
22889     onFirstFocus : function(){
22890         
22891         this.assignDocWin();
22892         
22893         
22894         this.activated = true;
22895          
22896     
22897         if(Roo.isGecko){ // prevent silly gecko errors
22898             this.win.focus();
22899             var s = this.win.getSelection();
22900             if(!s.focusNode || s.focusNode.nodeType != 3){
22901                 var r = s.getRangeAt(0);
22902                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22903                 r.collapse(true);
22904                 this.deferFocus();
22905             }
22906             try{
22907                 this.execCmd('useCSS', true);
22908                 this.execCmd('styleWithCSS', false);
22909             }catch(e){}
22910         }
22911         this.owner.fireEvent('activate', this);
22912     },
22913
22914     // private
22915     adjustFont: function(btn){
22916         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22917         //if(Roo.isSafari){ // safari
22918         //    adjust *= 2;
22919        // }
22920         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22921         if(Roo.isSafari){ // safari
22922             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22923             v =  (v < 10) ? 10 : v;
22924             v =  (v > 48) ? 48 : v;
22925             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22926             
22927         }
22928         
22929         
22930         v = Math.max(1, v+adjust);
22931         
22932         this.execCmd('FontSize', v  );
22933     },
22934
22935     onEditorEvent : function(e)
22936     {
22937         this.owner.fireEvent('editorevent', this, e);
22938       //  this.updateToolbar();
22939         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22940     },
22941
22942     insertTag : function(tg)
22943     {
22944         // could be a bit smarter... -> wrap the current selected tRoo..
22945         if (tg.toLowerCase() == 'span' ||
22946             tg.toLowerCase() == 'code' ||
22947             tg.toLowerCase() == 'sup' ||
22948             tg.toLowerCase() == 'sub' 
22949             ) {
22950             
22951             range = this.createRange(this.getSelection());
22952             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22953             wrappingNode.appendChild(range.extractContents());
22954             range.insertNode(wrappingNode);
22955
22956             return;
22957             
22958             
22959             
22960         }
22961         this.execCmd("formatblock",   tg);
22962         
22963     },
22964     
22965     insertText : function(txt)
22966     {
22967         
22968         
22969         var range = this.createRange();
22970         range.deleteContents();
22971                //alert(Sender.getAttribute('label'));
22972                
22973         range.insertNode(this.doc.createTextNode(txt));
22974     } ,
22975     
22976      
22977
22978     /**
22979      * Executes a Midas editor command on the editor document and performs necessary focus and
22980      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22981      * @param {String} cmd The Midas command
22982      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22983      */
22984     relayCmd : function(cmd, value){
22985         this.win.focus();
22986         this.execCmd(cmd, value);
22987         this.owner.fireEvent('editorevent', this);
22988         //this.updateToolbar();
22989         this.owner.deferFocus();
22990     },
22991
22992     /**
22993      * Executes a Midas editor command directly on the editor document.
22994      * For visual commands, you should use {@link #relayCmd} instead.
22995      * <b>This should only be called after the editor is initialized.</b>
22996      * @param {String} cmd The Midas command
22997      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22998      */
22999     execCmd : function(cmd, value){
23000         this.doc.execCommand(cmd, false, value === undefined ? null : value);
23001         this.syncValue();
23002     },
23003  
23004  
23005    
23006     /**
23007      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
23008      * to insert tRoo.
23009      * @param {String} text | dom node.. 
23010      */
23011     insertAtCursor : function(text)
23012     {
23013         
23014         if(!this.activated){
23015             return;
23016         }
23017         /*
23018         if(Roo.isIE){
23019             this.win.focus();
23020             var r = this.doc.selection.createRange();
23021             if(r){
23022                 r.collapse(true);
23023                 r.pasteHTML(text);
23024                 this.syncValue();
23025                 this.deferFocus();
23026             
23027             }
23028             return;
23029         }
23030         */
23031         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
23032             this.win.focus();
23033             
23034             
23035             // from jquery ui (MIT licenced)
23036             var range, node;
23037             var win = this.win;
23038             
23039             if (win.getSelection && win.getSelection().getRangeAt) {
23040                 range = win.getSelection().getRangeAt(0);
23041                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
23042                 range.insertNode(node);
23043             } else if (win.document.selection && win.document.selection.createRange) {
23044                 // no firefox support
23045                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23046                 win.document.selection.createRange().pasteHTML(txt);
23047             } else {
23048                 // no firefox support
23049                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
23050                 this.execCmd('InsertHTML', txt);
23051             } 
23052             
23053             this.syncValue();
23054             
23055             this.deferFocus();
23056         }
23057     },
23058  // private
23059     mozKeyPress : function(e){
23060         if(e.ctrlKey){
23061             var c = e.getCharCode(), cmd;
23062           
23063             if(c > 0){
23064                 c = String.fromCharCode(c).toLowerCase();
23065                 switch(c){
23066                     case 'b':
23067                         cmd = 'bold';
23068                         break;
23069                     case 'i':
23070                         cmd = 'italic';
23071                         break;
23072                     
23073                     case 'u':
23074                         cmd = 'underline';
23075                         break;
23076                     
23077                     case 'v':
23078                         this.cleanUpPaste.defer(100, this);
23079                         return;
23080                         
23081                 }
23082                 if(cmd){
23083                     this.win.focus();
23084                     this.execCmd(cmd);
23085                     this.deferFocus();
23086                     e.preventDefault();
23087                 }
23088                 
23089             }
23090         }
23091     },
23092
23093     // private
23094     fixKeys : function(){ // load time branching for fastest keydown performance
23095         if(Roo.isIE){
23096             return function(e){
23097                 var k = e.getKey(), r;
23098                 if(k == e.TAB){
23099                     e.stopEvent();
23100                     r = this.doc.selection.createRange();
23101                     if(r){
23102                         r.collapse(true);
23103                         r.pasteHTML('&#160;&#160;&#160;&#160;');
23104                         this.deferFocus();
23105                     }
23106                     return;
23107                 }
23108                 
23109                 if(k == e.ENTER){
23110                     r = this.doc.selection.createRange();
23111                     if(r){
23112                         var target = r.parentElement();
23113                         if(!target || target.tagName.toLowerCase() != 'li'){
23114                             e.stopEvent();
23115                             r.pasteHTML('<br />');
23116                             r.collapse(false);
23117                             r.select();
23118                         }
23119                     }
23120                 }
23121                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23122                     this.cleanUpPaste.defer(100, this);
23123                     return;
23124                 }
23125                 
23126                 
23127             };
23128         }else if(Roo.isOpera){
23129             return function(e){
23130                 var k = e.getKey();
23131                 if(k == e.TAB){
23132                     e.stopEvent();
23133                     this.win.focus();
23134                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
23135                     this.deferFocus();
23136                 }
23137                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23138                     this.cleanUpPaste.defer(100, this);
23139                     return;
23140                 }
23141                 
23142             };
23143         }else if(Roo.isSafari){
23144             return function(e){
23145                 var k = e.getKey();
23146                 
23147                 if(k == e.TAB){
23148                     e.stopEvent();
23149                     this.execCmd('InsertText','\t');
23150                     this.deferFocus();
23151                     return;
23152                 }
23153                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
23154                     this.cleanUpPaste.defer(100, this);
23155                     return;
23156                 }
23157                 
23158              };
23159         }
23160     }(),
23161     
23162     getAllAncestors: function()
23163     {
23164         var p = this.getSelectedNode();
23165         var a = [];
23166         if (!p) {
23167             a.push(p); // push blank onto stack..
23168             p = this.getParentElement();
23169         }
23170         
23171         
23172         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
23173             a.push(p);
23174             p = p.parentNode;
23175         }
23176         a.push(this.doc.body);
23177         return a;
23178     },
23179     lastSel : false,
23180     lastSelNode : false,
23181     
23182     
23183     getSelection : function() 
23184     {
23185         this.assignDocWin();
23186         return Roo.isIE ? this.doc.selection : this.win.getSelection();
23187     },
23188     
23189     getSelectedNode: function() 
23190     {
23191         // this may only work on Gecko!!!
23192         
23193         // should we cache this!!!!
23194         
23195         
23196         
23197          
23198         var range = this.createRange(this.getSelection()).cloneRange();
23199         
23200         if (Roo.isIE) {
23201             var parent = range.parentElement();
23202             while (true) {
23203                 var testRange = range.duplicate();
23204                 testRange.moveToElementText(parent);
23205                 if (testRange.inRange(range)) {
23206                     break;
23207                 }
23208                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
23209                     break;
23210                 }
23211                 parent = parent.parentElement;
23212             }
23213             return parent;
23214         }
23215         
23216         // is ancestor a text element.
23217         var ac =  range.commonAncestorContainer;
23218         if (ac.nodeType == 3) {
23219             ac = ac.parentNode;
23220         }
23221         
23222         var ar = ac.childNodes;
23223          
23224         var nodes = [];
23225         var other_nodes = [];
23226         var has_other_nodes = false;
23227         for (var i=0;i<ar.length;i++) {
23228             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
23229                 continue;
23230             }
23231             // fullly contained node.
23232             
23233             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
23234                 nodes.push(ar[i]);
23235                 continue;
23236             }
23237             
23238             // probably selected..
23239             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
23240                 other_nodes.push(ar[i]);
23241                 continue;
23242             }
23243             // outer..
23244             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
23245                 continue;
23246             }
23247             
23248             
23249             has_other_nodes = true;
23250         }
23251         if (!nodes.length && other_nodes.length) {
23252             nodes= other_nodes;
23253         }
23254         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
23255             return false;
23256         }
23257         
23258         return nodes[0];
23259     },
23260     createRange: function(sel)
23261     {
23262         // this has strange effects when using with 
23263         // top toolbar - not sure if it's a great idea.
23264         //this.editor.contentWindow.focus();
23265         if (typeof sel != "undefined") {
23266             try {
23267                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
23268             } catch(e) {
23269                 return this.doc.createRange();
23270             }
23271         } else {
23272             return this.doc.createRange();
23273         }
23274     },
23275     getParentElement: function()
23276     {
23277         
23278         this.assignDocWin();
23279         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
23280         
23281         var range = this.createRange(sel);
23282          
23283         try {
23284             var p = range.commonAncestorContainer;
23285             while (p.nodeType == 3) { // text node
23286                 p = p.parentNode;
23287             }
23288             return p;
23289         } catch (e) {
23290             return null;
23291         }
23292     
23293     },
23294     /***
23295      *
23296      * Range intersection.. the hard stuff...
23297      *  '-1' = before
23298      *  '0' = hits..
23299      *  '1' = after.
23300      *         [ -- selected range --- ]
23301      *   [fail]                        [fail]
23302      *
23303      *    basically..
23304      *      if end is before start or  hits it. fail.
23305      *      if start is after end or hits it fail.
23306      *
23307      *   if either hits (but other is outside. - then it's not 
23308      *   
23309      *    
23310      **/
23311     
23312     
23313     // @see http://www.thismuchiknow.co.uk/?p=64.
23314     rangeIntersectsNode : function(range, node)
23315     {
23316         var nodeRange = node.ownerDocument.createRange();
23317         try {
23318             nodeRange.selectNode(node);
23319         } catch (e) {
23320             nodeRange.selectNodeContents(node);
23321         }
23322     
23323         var rangeStartRange = range.cloneRange();
23324         rangeStartRange.collapse(true);
23325     
23326         var rangeEndRange = range.cloneRange();
23327         rangeEndRange.collapse(false);
23328     
23329         var nodeStartRange = nodeRange.cloneRange();
23330         nodeStartRange.collapse(true);
23331     
23332         var nodeEndRange = nodeRange.cloneRange();
23333         nodeEndRange.collapse(false);
23334     
23335         return rangeStartRange.compareBoundaryPoints(
23336                  Range.START_TO_START, nodeEndRange) == -1 &&
23337                rangeEndRange.compareBoundaryPoints(
23338                  Range.START_TO_START, nodeStartRange) == 1;
23339         
23340          
23341     },
23342     rangeCompareNode : function(range, node)
23343     {
23344         var nodeRange = node.ownerDocument.createRange();
23345         try {
23346             nodeRange.selectNode(node);
23347         } catch (e) {
23348             nodeRange.selectNodeContents(node);
23349         }
23350         
23351         
23352         range.collapse(true);
23353     
23354         nodeRange.collapse(true);
23355      
23356         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23357         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
23358          
23359         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23360         
23361         var nodeIsBefore   =  ss == 1;
23362         var nodeIsAfter    = ee == -1;
23363         
23364         if (nodeIsBefore && nodeIsAfter) {
23365             return 0; // outer
23366         }
23367         if (!nodeIsBefore && nodeIsAfter) {
23368             return 1; //right trailed.
23369         }
23370         
23371         if (nodeIsBefore && !nodeIsAfter) {
23372             return 2;  // left trailed.
23373         }
23374         // fully contined.
23375         return 3;
23376     },
23377
23378     // private? - in a new class?
23379     cleanUpPaste :  function()
23380     {
23381         // cleans up the whole document..
23382         Roo.log('cleanuppaste');
23383         
23384         this.cleanUpChildren(this.doc.body);
23385         var clean = this.cleanWordChars(this.doc.body.innerHTML);
23386         if (clean != this.doc.body.innerHTML) {
23387             this.doc.body.innerHTML = clean;
23388         }
23389         
23390     },
23391     
23392     cleanWordChars : function(input) {// change the chars to hex code
23393         var he = Roo.HtmlEditorCore;
23394         
23395         var output = input;
23396         Roo.each(he.swapCodes, function(sw) { 
23397             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23398             
23399             output = output.replace(swapper, sw[1]);
23400         });
23401         
23402         return output;
23403     },
23404     
23405     
23406     cleanUpChildren : function (n)
23407     {
23408         if (!n.childNodes.length) {
23409             return;
23410         }
23411         for (var i = n.childNodes.length-1; i > -1 ; i--) {
23412            this.cleanUpChild(n.childNodes[i]);
23413         }
23414     },
23415     
23416     
23417         
23418     
23419     cleanUpChild : function (node)
23420     {
23421         var ed = this;
23422         //console.log(node);
23423         if (node.nodeName == "#text") {
23424             // clean up silly Windows -- stuff?
23425             return; 
23426         }
23427         if (node.nodeName == "#comment") {
23428             node.parentNode.removeChild(node);
23429             // clean up silly Windows -- stuff?
23430             return; 
23431         }
23432         var lcname = node.tagName.toLowerCase();
23433         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23434         // whitelist of tags..
23435         
23436         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23437             // remove node.
23438             node.parentNode.removeChild(node);
23439             return;
23440             
23441         }
23442         
23443         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23444         
23445         // spans with no attributes - just remove them..
23446         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
23447             remove_keep_children = true;
23448         }
23449         
23450         // remove <a name=....> as rendering on yahoo mailer is borked with this.
23451         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23452         
23453         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23454         //    remove_keep_children = true;
23455         //}
23456         
23457         if (remove_keep_children) {
23458             this.cleanUpChildren(node);
23459             // inserts everything just before this node...
23460             while (node.childNodes.length) {
23461                 var cn = node.childNodes[0];
23462                 node.removeChild(cn);
23463                 node.parentNode.insertBefore(cn, node);
23464             }
23465             node.parentNode.removeChild(node);
23466             return;
23467         }
23468         
23469         if (!node.attributes || !node.attributes.length) {
23470             
23471           
23472             
23473             
23474             this.cleanUpChildren(node);
23475             return;
23476         }
23477         
23478         function cleanAttr(n,v)
23479         {
23480             
23481             if (v.match(/^\./) || v.match(/^\//)) {
23482                 return;
23483             }
23484             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23485                 return;
23486             }
23487             if (v.match(/^#/)) {
23488                 return;
23489             }
23490 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23491             node.removeAttribute(n);
23492             
23493         }
23494         
23495         var cwhite = this.cwhite;
23496         var cblack = this.cblack;
23497             
23498         function cleanStyle(n,v)
23499         {
23500             if (v.match(/expression/)) { //XSS?? should we even bother..
23501                 node.removeAttribute(n);
23502                 return;
23503             }
23504             
23505             var parts = v.split(/;/);
23506             var clean = [];
23507             
23508             Roo.each(parts, function(p) {
23509                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23510                 if (!p.length) {
23511                     return true;
23512                 }
23513                 var l = p.split(':').shift().replace(/\s+/g,'');
23514                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23515                 
23516                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23517 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23518                     //node.removeAttribute(n);
23519                     return true;
23520                 }
23521                 //Roo.log()
23522                 // only allow 'c whitelisted system attributes'
23523                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23524 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23525                     //node.removeAttribute(n);
23526                     return true;
23527                 }
23528                 
23529                 
23530                  
23531                 
23532                 clean.push(p);
23533                 return true;
23534             });
23535             if (clean.length) { 
23536                 node.setAttribute(n, clean.join(';'));
23537             } else {
23538                 node.removeAttribute(n);
23539             }
23540             
23541         }
23542         
23543         
23544         for (var i = node.attributes.length-1; i > -1 ; i--) {
23545             var a = node.attributes[i];
23546             //console.log(a);
23547             
23548             if (a.name.toLowerCase().substr(0,2)=='on')  {
23549                 node.removeAttribute(a.name);
23550                 continue;
23551             }
23552             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23553                 node.removeAttribute(a.name);
23554                 continue;
23555             }
23556             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23557                 cleanAttr(a.name,a.value); // fixme..
23558                 continue;
23559             }
23560             if (a.name == 'style') {
23561                 cleanStyle(a.name,a.value);
23562                 continue;
23563             }
23564             /// clean up MS crap..
23565             // tecnically this should be a list of valid class'es..
23566             
23567             
23568             if (a.name == 'class') {
23569                 if (a.value.match(/^Mso/)) {
23570                     node.removeAttribute('class');
23571                 }
23572                 
23573                 if (a.value.match(/^body$/)) {
23574                     node.removeAttribute('class');
23575                 }
23576                 continue;
23577             }
23578             
23579             // style cleanup!?
23580             // class cleanup?
23581             
23582         }
23583         
23584         
23585         this.cleanUpChildren(node);
23586         
23587         
23588     },
23589     
23590     /**
23591      * Clean up MS wordisms...
23592      */
23593     cleanWord : function(node)
23594     {
23595         if (!node) {
23596             this.cleanWord(this.doc.body);
23597             return;
23598         }
23599         
23600         if(
23601                 node.nodeName == 'SPAN' &&
23602                 !node.hasAttributes() &&
23603                 node.childNodes.length == 1 &&
23604                 node.firstChild.nodeName == "#text"  
23605         ) {
23606             var textNode = node.firstChild;
23607             node.removeChild(textNode);
23608             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
23609                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
23610             }
23611             node.parentNode.insertBefore(textNode, node);
23612             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
23613                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
23614             }
23615             node.parentNode.removeChild(node);
23616         }
23617         
23618         if (node.nodeName == "#text") {
23619             // clean up silly Windows -- stuff?
23620             return; 
23621         }
23622         if (node.nodeName == "#comment") {
23623             node.parentNode.removeChild(node);
23624             // clean up silly Windows -- stuff?
23625             return; 
23626         }
23627         
23628         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23629             node.parentNode.removeChild(node);
23630             return;
23631         }
23632         //Roo.log(node.tagName);
23633         // remove - but keep children..
23634         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
23635             //Roo.log('-- removed');
23636             while (node.childNodes.length) {
23637                 var cn = node.childNodes[0];
23638                 node.removeChild(cn);
23639                 node.parentNode.insertBefore(cn, node);
23640                 // move node to parent - and clean it..
23641                 this.cleanWord(cn);
23642             }
23643             node.parentNode.removeChild(node);
23644             /// no need to iterate chidlren = it's got none..
23645             //this.iterateChildren(node, this.cleanWord);
23646             return;
23647         }
23648         // clean styles
23649         if (node.className.length) {
23650             
23651             var cn = node.className.split(/\W+/);
23652             var cna = [];
23653             Roo.each(cn, function(cls) {
23654                 if (cls.match(/Mso[a-zA-Z]+/)) {
23655                     return;
23656                 }
23657                 cna.push(cls);
23658             });
23659             node.className = cna.length ? cna.join(' ') : '';
23660             if (!cna.length) {
23661                 node.removeAttribute("class");
23662             }
23663         }
23664         
23665         if (node.hasAttribute("lang")) {
23666             node.removeAttribute("lang");
23667         }
23668         
23669         if (node.hasAttribute("style")) {
23670             
23671             var styles = node.getAttribute("style").split(";");
23672             var nstyle = [];
23673             Roo.each(styles, function(s) {
23674                 if (!s.match(/:/)) {
23675                     return;
23676                 }
23677                 var kv = s.split(":");
23678                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23679                     return;
23680                 }
23681                 // what ever is left... we allow.
23682                 nstyle.push(s);
23683             });
23684             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23685             if (!nstyle.length) {
23686                 node.removeAttribute('style');
23687             }
23688         }
23689         this.iterateChildren(node, this.cleanWord);
23690         
23691         
23692         
23693     },
23694     /**
23695      * iterateChildren of a Node, calling fn each time, using this as the scole..
23696      * @param {DomNode} node node to iterate children of.
23697      * @param {Function} fn method of this class to call on each item.
23698      */
23699     iterateChildren : function(node, fn)
23700     {
23701         if (!node.childNodes.length) {
23702                 return;
23703         }
23704         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23705            fn.call(this, node.childNodes[i])
23706         }
23707     },
23708     
23709     
23710     /**
23711      * cleanTableWidths.
23712      *
23713      * Quite often pasting from word etc.. results in tables with column and widths.
23714      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23715      *
23716      */
23717     cleanTableWidths : function(node)
23718     {
23719          
23720          
23721         if (!node) {
23722             this.cleanTableWidths(this.doc.body);
23723             return;
23724         }
23725         
23726         // ignore list...
23727         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23728             return; 
23729         }
23730         Roo.log(node.tagName);
23731         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23732             this.iterateChildren(node, this.cleanTableWidths);
23733             return;
23734         }
23735         if (node.hasAttribute('width')) {
23736             node.removeAttribute('width');
23737         }
23738         
23739          
23740         if (node.hasAttribute("style")) {
23741             // pretty basic...
23742             
23743             var styles = node.getAttribute("style").split(";");
23744             var nstyle = [];
23745             Roo.each(styles, function(s) {
23746                 if (!s.match(/:/)) {
23747                     return;
23748                 }
23749                 var kv = s.split(":");
23750                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23751                     return;
23752                 }
23753                 // what ever is left... we allow.
23754                 nstyle.push(s);
23755             });
23756             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23757             if (!nstyle.length) {
23758                 node.removeAttribute('style');
23759             }
23760         }
23761         
23762         this.iterateChildren(node, this.cleanTableWidths);
23763         
23764         
23765     },
23766     
23767     
23768     
23769     
23770     domToHTML : function(currentElement, depth, nopadtext) {
23771         
23772         depth = depth || 0;
23773         nopadtext = nopadtext || false;
23774     
23775         if (!currentElement) {
23776             return this.domToHTML(this.doc.body);
23777         }
23778         
23779         //Roo.log(currentElement);
23780         var j;
23781         var allText = false;
23782         var nodeName = currentElement.nodeName;
23783         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23784         
23785         if  (nodeName == '#text') {
23786             
23787             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23788         }
23789         
23790         
23791         var ret = '';
23792         if (nodeName != 'BODY') {
23793              
23794             var i = 0;
23795             // Prints the node tagName, such as <A>, <IMG>, etc
23796             if (tagName) {
23797                 var attr = [];
23798                 for(i = 0; i < currentElement.attributes.length;i++) {
23799                     // quoting?
23800                     var aname = currentElement.attributes.item(i).name;
23801                     if (!currentElement.attributes.item(i).value.length) {
23802                         continue;
23803                     }
23804                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23805                 }
23806                 
23807                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23808             } 
23809             else {
23810                 
23811                 // eack
23812             }
23813         } else {
23814             tagName = false;
23815         }
23816         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23817             return ret;
23818         }
23819         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23820             nopadtext = true;
23821         }
23822         
23823         
23824         // Traverse the tree
23825         i = 0;
23826         var currentElementChild = currentElement.childNodes.item(i);
23827         var allText = true;
23828         var innerHTML  = '';
23829         lastnode = '';
23830         while (currentElementChild) {
23831             // Formatting code (indent the tree so it looks nice on the screen)
23832             var nopad = nopadtext;
23833             if (lastnode == 'SPAN') {
23834                 nopad  = true;
23835             }
23836             // text
23837             if  (currentElementChild.nodeName == '#text') {
23838                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23839                 toadd = nopadtext ? toadd : toadd.trim();
23840                 if (!nopad && toadd.length > 80) {
23841                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23842                 }
23843                 innerHTML  += toadd;
23844                 
23845                 i++;
23846                 currentElementChild = currentElement.childNodes.item(i);
23847                 lastNode = '';
23848                 continue;
23849             }
23850             allText = false;
23851             
23852             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23853                 
23854             // Recursively traverse the tree structure of the child node
23855             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23856             lastnode = currentElementChild.nodeName;
23857             i++;
23858             currentElementChild=currentElement.childNodes.item(i);
23859         }
23860         
23861         ret += innerHTML;
23862         
23863         if (!allText) {
23864                 // The remaining code is mostly for formatting the tree
23865             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23866         }
23867         
23868         
23869         if (tagName) {
23870             ret+= "</"+tagName+">";
23871         }
23872         return ret;
23873         
23874     },
23875         
23876     applyBlacklists : function()
23877     {
23878         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23879         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23880         
23881         this.white = [];
23882         this.black = [];
23883         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23884             if (b.indexOf(tag) > -1) {
23885                 return;
23886             }
23887             this.white.push(tag);
23888             
23889         }, this);
23890         
23891         Roo.each(w, function(tag) {
23892             if (b.indexOf(tag) > -1) {
23893                 return;
23894             }
23895             if (this.white.indexOf(tag) > -1) {
23896                 return;
23897             }
23898             this.white.push(tag);
23899             
23900         }, this);
23901         
23902         
23903         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23904             if (w.indexOf(tag) > -1) {
23905                 return;
23906             }
23907             this.black.push(tag);
23908             
23909         }, this);
23910         
23911         Roo.each(b, function(tag) {
23912             if (w.indexOf(tag) > -1) {
23913                 return;
23914             }
23915             if (this.black.indexOf(tag) > -1) {
23916                 return;
23917             }
23918             this.black.push(tag);
23919             
23920         }, this);
23921         
23922         
23923         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23924         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23925         
23926         this.cwhite = [];
23927         this.cblack = [];
23928         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23929             if (b.indexOf(tag) > -1) {
23930                 return;
23931             }
23932             this.cwhite.push(tag);
23933             
23934         }, this);
23935         
23936         Roo.each(w, function(tag) {
23937             if (b.indexOf(tag) > -1) {
23938                 return;
23939             }
23940             if (this.cwhite.indexOf(tag) > -1) {
23941                 return;
23942             }
23943             this.cwhite.push(tag);
23944             
23945         }, this);
23946         
23947         
23948         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23949             if (w.indexOf(tag) > -1) {
23950                 return;
23951             }
23952             this.cblack.push(tag);
23953             
23954         }, this);
23955         
23956         Roo.each(b, function(tag) {
23957             if (w.indexOf(tag) > -1) {
23958                 return;
23959             }
23960             if (this.cblack.indexOf(tag) > -1) {
23961                 return;
23962             }
23963             this.cblack.push(tag);
23964             
23965         }, this);
23966     },
23967     
23968     setStylesheets : function(stylesheets)
23969     {
23970         if(typeof(stylesheets) == 'string'){
23971             Roo.get(this.iframe.contentDocument.head).createChild({
23972                 tag : 'link',
23973                 rel : 'stylesheet',
23974                 type : 'text/css',
23975                 href : stylesheets
23976             });
23977             
23978             return;
23979         }
23980         var _this = this;
23981      
23982         Roo.each(stylesheets, function(s) {
23983             if(!s.length){
23984                 return;
23985             }
23986             
23987             Roo.get(_this.iframe.contentDocument.head).createChild({
23988                 tag : 'link',
23989                 rel : 'stylesheet',
23990                 type : 'text/css',
23991                 href : s
23992             });
23993         });
23994
23995         
23996     },
23997     
23998     removeStylesheets : function()
23999     {
24000         var _this = this;
24001         
24002         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
24003             s.remove();
24004         });
24005     },
24006     
24007     setStyle : function(style)
24008     {
24009         Roo.get(this.iframe.contentDocument.head).createChild({
24010             tag : 'style',
24011             type : 'text/css',
24012             html : style
24013         });
24014
24015         return;
24016     }
24017     
24018     // hide stuff that is not compatible
24019     /**
24020      * @event blur
24021      * @hide
24022      */
24023     /**
24024      * @event change
24025      * @hide
24026      */
24027     /**
24028      * @event focus
24029      * @hide
24030      */
24031     /**
24032      * @event specialkey
24033      * @hide
24034      */
24035     /**
24036      * @cfg {String} fieldClass @hide
24037      */
24038     /**
24039      * @cfg {String} focusClass @hide
24040      */
24041     /**
24042      * @cfg {String} autoCreate @hide
24043      */
24044     /**
24045      * @cfg {String} inputType @hide
24046      */
24047     /**
24048      * @cfg {String} invalidClass @hide
24049      */
24050     /**
24051      * @cfg {String} invalidText @hide
24052      */
24053     /**
24054      * @cfg {String} msgFx @hide
24055      */
24056     /**
24057      * @cfg {String} validateOnBlur @hide
24058      */
24059 });
24060
24061 Roo.HtmlEditorCore.white = [
24062         'area', 'br', 'img', 'input', 'hr', 'wbr',
24063         
24064        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24065        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24066        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24067        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24068        'table',   'ul',         'xmp', 
24069        
24070        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
24071       'thead',   'tr', 
24072      
24073       'dir', 'menu', 'ol', 'ul', 'dl',
24074        
24075       'embed',  'object'
24076 ];
24077
24078
24079 Roo.HtmlEditorCore.black = [
24080     //    'embed',  'object', // enable - backend responsiblity to clean thiese
24081         'applet', // 
24082         'base',   'basefont', 'bgsound', 'blink',  'body', 
24083         'frame',  'frameset', 'head',    'html',   'ilayer', 
24084         'iframe', 'layer',  'link',     'meta',    'object',   
24085         'script', 'style' ,'title',  'xml' // clean later..
24086 ];
24087 Roo.HtmlEditorCore.clean = [
24088     'script', 'style', 'title', 'xml'
24089 ];
24090 Roo.HtmlEditorCore.remove = [
24091     'font'
24092 ];
24093 // attributes..
24094
24095 Roo.HtmlEditorCore.ablack = [
24096     'on'
24097 ];
24098     
24099 Roo.HtmlEditorCore.aclean = [ 
24100     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
24101 ];
24102
24103 // protocols..
24104 Roo.HtmlEditorCore.pwhite= [
24105         'http',  'https',  'mailto'
24106 ];
24107
24108 // white listed style attributes.
24109 Roo.HtmlEditorCore.cwhite= [
24110       //  'text-align', /// default is to allow most things..
24111       
24112          
24113 //        'font-size'//??
24114 ];
24115
24116 // black listed style attributes.
24117 Roo.HtmlEditorCore.cblack= [
24118       //  'font-size' -- this can be set by the project 
24119 ];
24120
24121
24122 Roo.HtmlEditorCore.swapCodes   =[ 
24123     [    8211, "--" ], 
24124     [    8212, "--" ], 
24125     [    8216,  "'" ],  
24126     [    8217, "'" ],  
24127     [    8220, '"' ],  
24128     [    8221, '"' ],  
24129     [    8226, "*" ],  
24130     [    8230, "..." ]
24131 ]; 
24132
24133     /*
24134  * - LGPL
24135  *
24136  * HtmlEditor
24137  * 
24138  */
24139
24140 /**
24141  * @class Roo.bootstrap.HtmlEditor
24142  * @extends Roo.bootstrap.TextArea
24143  * Bootstrap HtmlEditor class
24144
24145  * @constructor
24146  * Create a new HtmlEditor
24147  * @param {Object} config The config object
24148  */
24149
24150 Roo.bootstrap.HtmlEditor = function(config){
24151     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
24152     if (!this.toolbars) {
24153         this.toolbars = [];
24154     }
24155     
24156     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
24157     this.addEvents({
24158             /**
24159              * @event initialize
24160              * Fires when the editor is fully initialized (including the iframe)
24161              * @param {HtmlEditor} this
24162              */
24163             initialize: true,
24164             /**
24165              * @event activate
24166              * Fires when the editor is first receives the focus. Any insertion must wait
24167              * until after this event.
24168              * @param {HtmlEditor} this
24169              */
24170             activate: true,
24171              /**
24172              * @event beforesync
24173              * Fires before the textarea is updated with content from the editor iframe. Return false
24174              * to cancel the sync.
24175              * @param {HtmlEditor} this
24176              * @param {String} html
24177              */
24178             beforesync: true,
24179              /**
24180              * @event beforepush
24181              * Fires before the iframe editor is updated with content from the textarea. Return false
24182              * to cancel the push.
24183              * @param {HtmlEditor} this
24184              * @param {String} html
24185              */
24186             beforepush: true,
24187              /**
24188              * @event sync
24189              * Fires when the textarea is updated with content from the editor iframe.
24190              * @param {HtmlEditor} this
24191              * @param {String} html
24192              */
24193             sync: true,
24194              /**
24195              * @event push
24196              * Fires when the iframe editor is updated with content from the textarea.
24197              * @param {HtmlEditor} this
24198              * @param {String} html
24199              */
24200             push: true,
24201              /**
24202              * @event editmodechange
24203              * Fires when the editor switches edit modes
24204              * @param {HtmlEditor} this
24205              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24206              */
24207             editmodechange: true,
24208             /**
24209              * @event editorevent
24210              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24211              * @param {HtmlEditor} this
24212              */
24213             editorevent: true,
24214             /**
24215              * @event firstfocus
24216              * Fires when on first focus - needed by toolbars..
24217              * @param {HtmlEditor} this
24218              */
24219             firstfocus: true,
24220             /**
24221              * @event autosave
24222              * Auto save the htmlEditor value as a file into Events
24223              * @param {HtmlEditor} this
24224              */
24225             autosave: true,
24226             /**
24227              * @event savedpreview
24228              * preview the saved version of htmlEditor
24229              * @param {HtmlEditor} this
24230              */
24231             savedpreview: true
24232         });
24233 };
24234
24235
24236 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
24237     
24238     
24239       /**
24240      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24241      */
24242     toolbars : false,
24243     
24244      /**
24245     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
24246     */
24247     btns : [],
24248    
24249      /**
24250      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24251      *                        Roo.resizable.
24252      */
24253     resizable : false,
24254      /**
24255      * @cfg {Number} height (in pixels)
24256      */   
24257     height: 300,
24258    /**
24259      * @cfg {Number} width (in pixels)
24260      */   
24261     width: false,
24262     
24263     /**
24264      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24265      * 
24266      */
24267     stylesheets: false,
24268     
24269     // id of frame..
24270     frameId: false,
24271     
24272     // private properties
24273     validationEvent : false,
24274     deferHeight: true,
24275     initialized : false,
24276     activated : false,
24277     
24278     onFocus : Roo.emptyFn,
24279     iframePad:3,
24280     hideMode:'offsets',
24281     
24282     tbContainer : false,
24283     
24284     bodyCls : '',
24285     
24286     toolbarContainer :function() {
24287         return this.wrap.select('.x-html-editor-tb',true).first();
24288     },
24289
24290     /**
24291      * Protected method that will not generally be called directly. It
24292      * is called when the editor creates its toolbar. Override this method if you need to
24293      * add custom toolbar buttons.
24294      * @param {HtmlEditor} editor
24295      */
24296     createToolbar : function(){
24297         Roo.log('renewing');
24298         Roo.log("create toolbars");
24299         
24300         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
24301         this.toolbars[0].render(this.toolbarContainer());
24302         
24303         return;
24304         
24305 //        if (!editor.toolbars || !editor.toolbars.length) {
24306 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
24307 //        }
24308 //        
24309 //        for (var i =0 ; i < editor.toolbars.length;i++) {
24310 //            editor.toolbars[i] = Roo.factory(
24311 //                    typeof(editor.toolbars[i]) == 'string' ?
24312 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
24313 //                Roo.bootstrap.HtmlEditor);
24314 //            editor.toolbars[i].init(editor);
24315 //        }
24316     },
24317
24318      
24319     // private
24320     onRender : function(ct, position)
24321     {
24322        // Roo.log("Call onRender: " + this.xtype);
24323         var _t = this;
24324         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
24325       
24326         this.wrap = this.inputEl().wrap({
24327             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24328         });
24329         
24330         this.editorcore.onRender(ct, position);
24331          
24332         if (this.resizable) {
24333             this.resizeEl = new Roo.Resizable(this.wrap, {
24334                 pinned : true,
24335                 wrap: true,
24336                 dynamic : true,
24337                 minHeight : this.height,
24338                 height: this.height,
24339                 handles : this.resizable,
24340                 width: this.width,
24341                 listeners : {
24342                     resize : function(r, w, h) {
24343                         _t.onResize(w,h); // -something
24344                     }
24345                 }
24346             });
24347             
24348         }
24349         this.createToolbar(this);
24350        
24351         
24352         if(!this.width && this.resizable){
24353             this.setSize(this.wrap.getSize());
24354         }
24355         if (this.resizeEl) {
24356             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24357             // should trigger onReize..
24358         }
24359         
24360     },
24361
24362     // private
24363     onResize : function(w, h)
24364     {
24365         Roo.log('resize: ' +w + ',' + h );
24366         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
24367         var ew = false;
24368         var eh = false;
24369         
24370         if(this.inputEl() ){
24371             if(typeof w == 'number'){
24372                 var aw = w - this.wrap.getFrameWidth('lr');
24373                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
24374                 ew = aw;
24375             }
24376             if(typeof h == 'number'){
24377                  var tbh = -11;  // fixme it needs to tool bar size!
24378                 for (var i =0; i < this.toolbars.length;i++) {
24379                     // fixme - ask toolbars for heights?
24380                     tbh += this.toolbars[i].el.getHeight();
24381                     //if (this.toolbars[i].footer) {
24382                     //    tbh += this.toolbars[i].footer.el.getHeight();
24383                     //}
24384                 }
24385               
24386                 
24387                 
24388                 
24389                 
24390                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24391                 ah -= 5; // knock a few pixes off for look..
24392                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24393                 var eh = ah;
24394             }
24395         }
24396         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24397         this.editorcore.onResize(ew,eh);
24398         
24399     },
24400
24401     /**
24402      * Toggles the editor between standard and source edit mode.
24403      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24404      */
24405     toggleSourceEdit : function(sourceEditMode)
24406     {
24407         this.editorcore.toggleSourceEdit(sourceEditMode);
24408         
24409         if(this.editorcore.sourceEditMode){
24410             Roo.log('editor - showing textarea');
24411             
24412 //            Roo.log('in');
24413 //            Roo.log(this.syncValue());
24414             this.syncValue();
24415             this.inputEl().removeClass(['hide', 'x-hidden']);
24416             this.inputEl().dom.removeAttribute('tabIndex');
24417             this.inputEl().focus();
24418         }else{
24419             Roo.log('editor - hiding textarea');
24420 //            Roo.log('out')
24421 //            Roo.log(this.pushValue()); 
24422             this.pushValue();
24423             
24424             this.inputEl().addClass(['hide', 'x-hidden']);
24425             this.inputEl().dom.setAttribute('tabIndex', -1);
24426             //this.deferFocus();
24427         }
24428          
24429         if(this.resizable){
24430             this.setSize(this.wrap.getSize());
24431         }
24432         
24433         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24434     },
24435  
24436     // private (for BoxComponent)
24437     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24438
24439     // private (for BoxComponent)
24440     getResizeEl : function(){
24441         return this.wrap;
24442     },
24443
24444     // private (for BoxComponent)
24445     getPositionEl : function(){
24446         return this.wrap;
24447     },
24448
24449     // private
24450     initEvents : function(){
24451         this.originalValue = this.getValue();
24452     },
24453
24454 //    /**
24455 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24456 //     * @method
24457 //     */
24458 //    markInvalid : Roo.emptyFn,
24459 //    /**
24460 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24461 //     * @method
24462 //     */
24463 //    clearInvalid : Roo.emptyFn,
24464
24465     setValue : function(v){
24466         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24467         this.editorcore.pushValue();
24468     },
24469
24470      
24471     // private
24472     deferFocus : function(){
24473         this.focus.defer(10, this);
24474     },
24475
24476     // doc'ed in Field
24477     focus : function(){
24478         this.editorcore.focus();
24479         
24480     },
24481       
24482
24483     // private
24484     onDestroy : function(){
24485         
24486         
24487         
24488         if(this.rendered){
24489             
24490             for (var i =0; i < this.toolbars.length;i++) {
24491                 // fixme - ask toolbars for heights?
24492                 this.toolbars[i].onDestroy();
24493             }
24494             
24495             this.wrap.dom.innerHTML = '';
24496             this.wrap.remove();
24497         }
24498     },
24499
24500     // private
24501     onFirstFocus : function(){
24502         //Roo.log("onFirstFocus");
24503         this.editorcore.onFirstFocus();
24504          for (var i =0; i < this.toolbars.length;i++) {
24505             this.toolbars[i].onFirstFocus();
24506         }
24507         
24508     },
24509     
24510     // private
24511     syncValue : function()
24512     {   
24513         this.editorcore.syncValue();
24514     },
24515     
24516     pushValue : function()
24517     {   
24518         this.editorcore.pushValue();
24519     }
24520      
24521     
24522     // hide stuff that is not compatible
24523     /**
24524      * @event blur
24525      * @hide
24526      */
24527     /**
24528      * @event change
24529      * @hide
24530      */
24531     /**
24532      * @event focus
24533      * @hide
24534      */
24535     /**
24536      * @event specialkey
24537      * @hide
24538      */
24539     /**
24540      * @cfg {String} fieldClass @hide
24541      */
24542     /**
24543      * @cfg {String} focusClass @hide
24544      */
24545     /**
24546      * @cfg {String} autoCreate @hide
24547      */
24548     /**
24549      * @cfg {String} inputType @hide
24550      */
24551      
24552     /**
24553      * @cfg {String} invalidText @hide
24554      */
24555     /**
24556      * @cfg {String} msgFx @hide
24557      */
24558     /**
24559      * @cfg {String} validateOnBlur @hide
24560      */
24561 });
24562  
24563     
24564    
24565    
24566    
24567       
24568 Roo.namespace('Roo.bootstrap.htmleditor');
24569 /**
24570  * @class Roo.bootstrap.HtmlEditorToolbar1
24571  * Basic Toolbar
24572  * 
24573  * @example
24574  * Usage:
24575  *
24576  new Roo.bootstrap.HtmlEditor({
24577     ....
24578     toolbars : [
24579         new Roo.bootstrap.HtmlEditorToolbar1({
24580             disable : { fonts: 1 , format: 1, ..., ... , ...],
24581             btns : [ .... ]
24582         })
24583     }
24584      
24585  * 
24586  * @cfg {Object} disable List of elements to disable..
24587  * @cfg {Array} btns List of additional buttons.
24588  * 
24589  * 
24590  * NEEDS Extra CSS? 
24591  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24592  */
24593  
24594 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24595 {
24596     
24597     Roo.apply(this, config);
24598     
24599     // default disabled, based on 'good practice'..
24600     this.disable = this.disable || {};
24601     Roo.applyIf(this.disable, {
24602         fontSize : true,
24603         colors : true,
24604         specialElements : true
24605     });
24606     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24607     
24608     this.editor = config.editor;
24609     this.editorcore = config.editor.editorcore;
24610     
24611     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24612     
24613     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24614     // dont call parent... till later.
24615 }
24616 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24617      
24618     bar : true,
24619     
24620     editor : false,
24621     editorcore : false,
24622     
24623     
24624     formats : [
24625         "p" ,  
24626         "h1","h2","h3","h4","h5","h6", 
24627         "pre", "code", 
24628         "abbr", "acronym", "address", "cite", "samp", "var",
24629         'div','span'
24630     ],
24631     
24632     onRender : function(ct, position)
24633     {
24634        // Roo.log("Call onRender: " + this.xtype);
24635         
24636        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24637        Roo.log(this.el);
24638        this.el.dom.style.marginBottom = '0';
24639        var _this = this;
24640        var editorcore = this.editorcore;
24641        var editor= this.editor;
24642        
24643        var children = [];
24644        var btn = function(id,cmd , toggle, handler, html){
24645        
24646             var  event = toggle ? 'toggle' : 'click';
24647        
24648             var a = {
24649                 size : 'sm',
24650                 xtype: 'Button',
24651                 xns: Roo.bootstrap,
24652                 //glyphicon : id,
24653                 fa: id,
24654                 cmd : id || cmd,
24655                 enableToggle:toggle !== false,
24656                 html : html || '',
24657                 pressed : toggle ? false : null,
24658                 listeners : {}
24659             };
24660             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24661                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24662             };
24663             children.push(a);
24664             return a;
24665        }
24666        
24667     //    var cb_box = function...
24668         
24669         var style = {
24670                 xtype: 'Button',
24671                 size : 'sm',
24672                 xns: Roo.bootstrap,
24673                 fa : 'font',
24674                 //html : 'submit'
24675                 menu : {
24676                     xtype: 'Menu',
24677                     xns: Roo.bootstrap,
24678                     items:  []
24679                 }
24680         };
24681         Roo.each(this.formats, function(f) {
24682             style.menu.items.push({
24683                 xtype :'MenuItem',
24684                 xns: Roo.bootstrap,
24685                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24686                 tagname : f,
24687                 listeners : {
24688                     click : function()
24689                     {
24690                         editorcore.insertTag(this.tagname);
24691                         editor.focus();
24692                     }
24693                 }
24694                 
24695             });
24696         });
24697         children.push(style);   
24698         
24699         btn('bold',false,true);
24700         btn('italic',false,true);
24701         btn('align-left', 'justifyleft',true);
24702         btn('align-center', 'justifycenter',true);
24703         btn('align-right' , 'justifyright',true);
24704         btn('link', false, false, function(btn) {
24705             //Roo.log("create link?");
24706             var url = prompt(this.createLinkText, this.defaultLinkValue);
24707             if(url && url != 'http:/'+'/'){
24708                 this.editorcore.relayCmd('createlink', url);
24709             }
24710         }),
24711         btn('list','insertunorderedlist',true);
24712         btn('pencil', false,true, function(btn){
24713                 Roo.log(this);
24714                 this.toggleSourceEdit(btn.pressed);
24715         });
24716         
24717         if (this.editor.btns.length > 0) {
24718             for (var i = 0; i<this.editor.btns.length; i++) {
24719                 children.push(this.editor.btns[i]);
24720             }
24721         }
24722         
24723         /*
24724         var cog = {
24725                 xtype: 'Button',
24726                 size : 'sm',
24727                 xns: Roo.bootstrap,
24728                 glyphicon : 'cog',
24729                 //html : 'submit'
24730                 menu : {
24731                     xtype: 'Menu',
24732                     xns: Roo.bootstrap,
24733                     items:  []
24734                 }
24735         };
24736         
24737         cog.menu.items.push({
24738             xtype :'MenuItem',
24739             xns: Roo.bootstrap,
24740             html : Clean styles,
24741             tagname : f,
24742             listeners : {
24743                 click : function()
24744                 {
24745                     editorcore.insertTag(this.tagname);
24746                     editor.focus();
24747                 }
24748             }
24749             
24750         });
24751        */
24752         
24753          
24754        this.xtype = 'NavSimplebar';
24755         
24756         for(var i=0;i< children.length;i++) {
24757             
24758             this.buttons.add(this.addxtypeChild(children[i]));
24759             
24760         }
24761         
24762         editor.on('editorevent', this.updateToolbar, this);
24763     },
24764     onBtnClick : function(id)
24765     {
24766        this.editorcore.relayCmd(id);
24767        this.editorcore.focus();
24768     },
24769     
24770     /**
24771      * Protected method that will not generally be called directly. It triggers
24772      * a toolbar update by reading the markup state of the current selection in the editor.
24773      */
24774     updateToolbar: function(){
24775
24776         if(!this.editorcore.activated){
24777             this.editor.onFirstFocus(); // is this neeed?
24778             return;
24779         }
24780
24781         var btns = this.buttons; 
24782         var doc = this.editorcore.doc;
24783         btns.get('bold').setActive(doc.queryCommandState('bold'));
24784         btns.get('italic').setActive(doc.queryCommandState('italic'));
24785         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24786         
24787         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24788         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24789         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24790         
24791         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24792         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24793          /*
24794         
24795         var ans = this.editorcore.getAllAncestors();
24796         if (this.formatCombo) {
24797             
24798             
24799             var store = this.formatCombo.store;
24800             this.formatCombo.setValue("");
24801             for (var i =0; i < ans.length;i++) {
24802                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24803                     // select it..
24804                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24805                     break;
24806                 }
24807             }
24808         }
24809         
24810         
24811         
24812         // hides menus... - so this cant be on a menu...
24813         Roo.bootstrap.MenuMgr.hideAll();
24814         */
24815         Roo.bootstrap.MenuMgr.hideAll();
24816         //this.editorsyncValue();
24817     },
24818     onFirstFocus: function() {
24819         this.buttons.each(function(item){
24820            item.enable();
24821         });
24822     },
24823     toggleSourceEdit : function(sourceEditMode){
24824         
24825           
24826         if(sourceEditMode){
24827             Roo.log("disabling buttons");
24828            this.buttons.each( function(item){
24829                 if(item.cmd != 'pencil'){
24830                     item.disable();
24831                 }
24832             });
24833           
24834         }else{
24835             Roo.log("enabling buttons");
24836             if(this.editorcore.initialized){
24837                 this.buttons.each( function(item){
24838                     item.enable();
24839                 });
24840             }
24841             
24842         }
24843         Roo.log("calling toggole on editor");
24844         // tell the editor that it's been pressed..
24845         this.editor.toggleSourceEdit(sourceEditMode);
24846        
24847     }
24848 });
24849
24850
24851
24852
24853
24854 /**
24855  * @class Roo.bootstrap.Table.AbstractSelectionModel
24856  * @extends Roo.util.Observable
24857  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24858  * implemented by descendant classes.  This class should not be directly instantiated.
24859  * @constructor
24860  */
24861 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24862     this.locked = false;
24863     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24864 };
24865
24866
24867 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24868     /** @ignore Called by the grid automatically. Do not call directly. */
24869     init : function(grid){
24870         this.grid = grid;
24871         this.initEvents();
24872     },
24873
24874     /**
24875      * Locks the selections.
24876      */
24877     lock : function(){
24878         this.locked = true;
24879     },
24880
24881     /**
24882      * Unlocks the selections.
24883      */
24884     unlock : function(){
24885         this.locked = false;
24886     },
24887
24888     /**
24889      * Returns true if the selections are locked.
24890      * @return {Boolean}
24891      */
24892     isLocked : function(){
24893         return this.locked;
24894     },
24895     
24896     
24897     initEvents : function ()
24898     {
24899         
24900     }
24901 });
24902 /**
24903  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24904  * @class Roo.bootstrap.Table.RowSelectionModel
24905  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24906  * It supports multiple selections and keyboard selection/navigation. 
24907  * @constructor
24908  * @param {Object} config
24909  */
24910
24911 Roo.bootstrap.Table.RowSelectionModel = function(config){
24912     Roo.apply(this, config);
24913     this.selections = new Roo.util.MixedCollection(false, function(o){
24914         return o.id;
24915     });
24916
24917     this.last = false;
24918     this.lastActive = false;
24919
24920     this.addEvents({
24921         /**
24922              * @event selectionchange
24923              * Fires when the selection changes
24924              * @param {SelectionModel} this
24925              */
24926             "selectionchange" : true,
24927         /**
24928              * @event afterselectionchange
24929              * Fires after the selection changes (eg. by key press or clicking)
24930              * @param {SelectionModel} this
24931              */
24932             "afterselectionchange" : true,
24933         /**
24934              * @event beforerowselect
24935              * Fires when a row is selected being selected, return false to cancel.
24936              * @param {SelectionModel} this
24937              * @param {Number} rowIndex The selected index
24938              * @param {Boolean} keepExisting False if other selections will be cleared
24939              */
24940             "beforerowselect" : true,
24941         /**
24942              * @event rowselect
24943              * Fires when a row is selected.
24944              * @param {SelectionModel} this
24945              * @param {Number} rowIndex The selected index
24946              * @param {Roo.data.Record} r The record
24947              */
24948             "rowselect" : true,
24949         /**
24950              * @event rowdeselect
24951              * Fires when a row is deselected.
24952              * @param {SelectionModel} this
24953              * @param {Number} rowIndex The selected index
24954              */
24955         "rowdeselect" : true
24956     });
24957     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24958     this.locked = false;
24959  };
24960
24961 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24962     /**
24963      * @cfg {Boolean} singleSelect
24964      * True to allow selection of only one row at a time (defaults to false)
24965      */
24966     singleSelect : false,
24967
24968     // private
24969     initEvents : function()
24970     {
24971
24972         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24973         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24974         //}else{ // allow click to work like normal
24975          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24976         //}
24977         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24978         this.grid.on("rowclick", this.handleMouseDown, this);
24979         
24980         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24981             "up" : function(e){
24982                 if(!e.shiftKey){
24983                     this.selectPrevious(e.shiftKey);
24984                 }else if(this.last !== false && this.lastActive !== false){
24985                     var last = this.last;
24986                     this.selectRange(this.last,  this.lastActive-1);
24987                     this.grid.getView().focusRow(this.lastActive);
24988                     if(last !== false){
24989                         this.last = last;
24990                     }
24991                 }else{
24992                     this.selectFirstRow();
24993                 }
24994                 this.fireEvent("afterselectionchange", this);
24995             },
24996             "down" : function(e){
24997                 if(!e.shiftKey){
24998                     this.selectNext(e.shiftKey);
24999                 }else if(this.last !== false && this.lastActive !== false){
25000                     var last = this.last;
25001                     this.selectRange(this.last,  this.lastActive+1);
25002                     this.grid.getView().focusRow(this.lastActive);
25003                     if(last !== false){
25004                         this.last = last;
25005                     }
25006                 }else{
25007                     this.selectFirstRow();
25008                 }
25009                 this.fireEvent("afterselectionchange", this);
25010             },
25011             scope: this
25012         });
25013         this.grid.store.on('load', function(){
25014             this.selections.clear();
25015         },this);
25016         /*
25017         var view = this.grid.view;
25018         view.on("refresh", this.onRefresh, this);
25019         view.on("rowupdated", this.onRowUpdated, this);
25020         view.on("rowremoved", this.onRemove, this);
25021         */
25022     },
25023
25024     // private
25025     onRefresh : function()
25026     {
25027         var ds = this.grid.store, i, v = this.grid.view;
25028         var s = this.selections;
25029         s.each(function(r){
25030             if((i = ds.indexOfId(r.id)) != -1){
25031                 v.onRowSelect(i);
25032             }else{
25033                 s.remove(r);
25034             }
25035         });
25036     },
25037
25038     // private
25039     onRemove : function(v, index, r){
25040         this.selections.remove(r);
25041     },
25042
25043     // private
25044     onRowUpdated : function(v, index, r){
25045         if(this.isSelected(r)){
25046             v.onRowSelect(index);
25047         }
25048     },
25049
25050     /**
25051      * Select records.
25052      * @param {Array} records The records to select
25053      * @param {Boolean} keepExisting (optional) True to keep existing selections
25054      */
25055     selectRecords : function(records, keepExisting)
25056     {
25057         if(!keepExisting){
25058             this.clearSelections();
25059         }
25060             var ds = this.grid.store;
25061         for(var i = 0, len = records.length; i < len; i++){
25062             this.selectRow(ds.indexOf(records[i]), true);
25063         }
25064     },
25065
25066     /**
25067      * Gets the number of selected rows.
25068      * @return {Number}
25069      */
25070     getCount : function(){
25071         return this.selections.length;
25072     },
25073
25074     /**
25075      * Selects the first row in the grid.
25076      */
25077     selectFirstRow : function(){
25078         this.selectRow(0);
25079     },
25080
25081     /**
25082      * Select the last row.
25083      * @param {Boolean} keepExisting (optional) True to keep existing selections
25084      */
25085     selectLastRow : function(keepExisting){
25086         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
25087         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
25088     },
25089
25090     /**
25091      * Selects the row immediately following the last selected row.
25092      * @param {Boolean} keepExisting (optional) True to keep existing selections
25093      */
25094     selectNext : function(keepExisting)
25095     {
25096             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
25097             this.selectRow(this.last+1, keepExisting);
25098             this.grid.getView().focusRow(this.last);
25099         }
25100     },
25101
25102     /**
25103      * Selects the row that precedes the last selected row.
25104      * @param {Boolean} keepExisting (optional) True to keep existing selections
25105      */
25106     selectPrevious : function(keepExisting){
25107         if(this.last){
25108             this.selectRow(this.last-1, keepExisting);
25109             this.grid.getView().focusRow(this.last);
25110         }
25111     },
25112
25113     /**
25114      * Returns the selected records
25115      * @return {Array} Array of selected records
25116      */
25117     getSelections : function(){
25118         return [].concat(this.selections.items);
25119     },
25120
25121     /**
25122      * Returns the first selected record.
25123      * @return {Record}
25124      */
25125     getSelected : function(){
25126         return this.selections.itemAt(0);
25127     },
25128
25129
25130     /**
25131      * Clears all selections.
25132      */
25133     clearSelections : function(fast)
25134     {
25135         if(this.locked) {
25136             return;
25137         }
25138         if(fast !== true){
25139                 var ds = this.grid.store;
25140             var s = this.selections;
25141             s.each(function(r){
25142                 this.deselectRow(ds.indexOfId(r.id));
25143             }, this);
25144             s.clear();
25145         }else{
25146             this.selections.clear();
25147         }
25148         this.last = false;
25149     },
25150
25151
25152     /**
25153      * Selects all rows.
25154      */
25155     selectAll : function(){
25156         if(this.locked) {
25157             return;
25158         }
25159         this.selections.clear();
25160         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
25161             this.selectRow(i, true);
25162         }
25163     },
25164
25165     /**
25166      * Returns True if there is a selection.
25167      * @return {Boolean}
25168      */
25169     hasSelection : function(){
25170         return this.selections.length > 0;
25171     },
25172
25173     /**
25174      * Returns True if the specified row is selected.
25175      * @param {Number/Record} record The record or index of the record to check
25176      * @return {Boolean}
25177      */
25178     isSelected : function(index){
25179             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
25180         return (r && this.selections.key(r.id) ? true : false);
25181     },
25182
25183     /**
25184      * Returns True if the specified record id is selected.
25185      * @param {String} id The id of record to check
25186      * @return {Boolean}
25187      */
25188     isIdSelected : function(id){
25189         return (this.selections.key(id) ? true : false);
25190     },
25191
25192
25193     // private
25194     handleMouseDBClick : function(e, t){
25195         
25196     },
25197     // private
25198     handleMouseDown : function(e, t)
25199     {
25200             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
25201         if(this.isLocked() || rowIndex < 0 ){
25202             return;
25203         };
25204         if(e.shiftKey && this.last !== false){
25205             var last = this.last;
25206             this.selectRange(last, rowIndex, e.ctrlKey);
25207             this.last = last; // reset the last
25208             t.focus();
25209     
25210         }else{
25211             var isSelected = this.isSelected(rowIndex);
25212             //Roo.log("select row:" + rowIndex);
25213             if(isSelected){
25214                 this.deselectRow(rowIndex);
25215             } else {
25216                         this.selectRow(rowIndex, true);
25217             }
25218     
25219             /*
25220                 if(e.button !== 0 && isSelected){
25221                 alert('rowIndex 2: ' + rowIndex);
25222                     view.focusRow(rowIndex);
25223                 }else if(e.ctrlKey && isSelected){
25224                     this.deselectRow(rowIndex);
25225                 }else if(!isSelected){
25226                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
25227                     view.focusRow(rowIndex);
25228                 }
25229             */
25230         }
25231         this.fireEvent("afterselectionchange", this);
25232     },
25233     // private
25234     handleDragableRowClick :  function(grid, rowIndex, e) 
25235     {
25236         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
25237             this.selectRow(rowIndex, false);
25238             grid.view.focusRow(rowIndex);
25239              this.fireEvent("afterselectionchange", this);
25240         }
25241     },
25242     
25243     /**
25244      * Selects multiple rows.
25245      * @param {Array} rows Array of the indexes of the row to select
25246      * @param {Boolean} keepExisting (optional) True to keep existing selections
25247      */
25248     selectRows : function(rows, keepExisting){
25249         if(!keepExisting){
25250             this.clearSelections();
25251         }
25252         for(var i = 0, len = rows.length; i < len; i++){
25253             this.selectRow(rows[i], true);
25254         }
25255     },
25256
25257     /**
25258      * Selects a range of rows. All rows in between startRow and endRow are also selected.
25259      * @param {Number} startRow The index of the first row in the range
25260      * @param {Number} endRow The index of the last row in the range
25261      * @param {Boolean} keepExisting (optional) True to retain existing selections
25262      */
25263     selectRange : function(startRow, endRow, keepExisting){
25264         if(this.locked) {
25265             return;
25266         }
25267         if(!keepExisting){
25268             this.clearSelections();
25269         }
25270         if(startRow <= endRow){
25271             for(var i = startRow; i <= endRow; i++){
25272                 this.selectRow(i, true);
25273             }
25274         }else{
25275             for(var i = startRow; i >= endRow; i--){
25276                 this.selectRow(i, true);
25277             }
25278         }
25279     },
25280
25281     /**
25282      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
25283      * @param {Number} startRow The index of the first row in the range
25284      * @param {Number} endRow The index of the last row in the range
25285      */
25286     deselectRange : function(startRow, endRow, preventViewNotify){
25287         if(this.locked) {
25288             return;
25289         }
25290         for(var i = startRow; i <= endRow; i++){
25291             this.deselectRow(i, preventViewNotify);
25292         }
25293     },
25294
25295     /**
25296      * Selects a row.
25297      * @param {Number} row The index of the row to select
25298      * @param {Boolean} keepExisting (optional) True to keep existing selections
25299      */
25300     selectRow : function(index, keepExisting, preventViewNotify)
25301     {
25302             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
25303             return;
25304         }
25305         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
25306             if(!keepExisting || this.singleSelect){
25307                 this.clearSelections();
25308             }
25309             
25310             var r = this.grid.store.getAt(index);
25311             //console.log('selectRow - record id :' + r.id);
25312             
25313             this.selections.add(r);
25314             this.last = this.lastActive = index;
25315             if(!preventViewNotify){
25316                 var proxy = new Roo.Element(
25317                                 this.grid.getRowDom(index)
25318                 );
25319                 proxy.addClass('bg-info info');
25320             }
25321             this.fireEvent("rowselect", this, index, r);
25322             this.fireEvent("selectionchange", this);
25323         }
25324     },
25325
25326     /**
25327      * Deselects a row.
25328      * @param {Number} row The index of the row to deselect
25329      */
25330     deselectRow : function(index, preventViewNotify)
25331     {
25332         if(this.locked) {
25333             return;
25334         }
25335         if(this.last == index){
25336             this.last = false;
25337         }
25338         if(this.lastActive == index){
25339             this.lastActive = false;
25340         }
25341         
25342         var r = this.grid.store.getAt(index);
25343         if (!r) {
25344             return;
25345         }
25346         
25347         this.selections.remove(r);
25348         //.console.log('deselectRow - record id :' + r.id);
25349         if(!preventViewNotify){
25350         
25351             var proxy = new Roo.Element(
25352                 this.grid.getRowDom(index)
25353             );
25354             proxy.removeClass('bg-info info');
25355         }
25356         this.fireEvent("rowdeselect", this, index);
25357         this.fireEvent("selectionchange", this);
25358     },
25359
25360     // private
25361     restoreLast : function(){
25362         if(this._last){
25363             this.last = this._last;
25364         }
25365     },
25366
25367     // private
25368     acceptsNav : function(row, col, cm){
25369         return !cm.isHidden(col) && cm.isCellEditable(col, row);
25370     },
25371
25372     // private
25373     onEditorKey : function(field, e){
25374         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
25375         if(k == e.TAB){
25376             e.stopEvent();
25377             ed.completeEdit();
25378             if(e.shiftKey){
25379                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
25380             }else{
25381                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
25382             }
25383         }else if(k == e.ENTER && !e.ctrlKey){
25384             e.stopEvent();
25385             ed.completeEdit();
25386             if(e.shiftKey){
25387                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25388             }else{
25389                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25390             }
25391         }else if(k == e.ESC){
25392             ed.cancelEdit();
25393         }
25394         if(newCell){
25395             g.startEditing(newCell[0], newCell[1]);
25396         }
25397     }
25398 });
25399 /*
25400  * Based on:
25401  * Ext JS Library 1.1.1
25402  * Copyright(c) 2006-2007, Ext JS, LLC.
25403  *
25404  * Originally Released Under LGPL - original licence link has changed is not relivant.
25405  *
25406  * Fork - LGPL
25407  * <script type="text/javascript">
25408  */
25409  
25410 /**
25411  * @class Roo.bootstrap.PagingToolbar
25412  * @extends Roo.bootstrap.NavSimplebar
25413  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
25414  * @constructor
25415  * Create a new PagingToolbar
25416  * @param {Object} config The config object
25417  * @param {Roo.data.Store} store
25418  */
25419 Roo.bootstrap.PagingToolbar = function(config)
25420 {
25421     // old args format still supported... - xtype is prefered..
25422         // created from xtype...
25423     
25424     this.ds = config.dataSource;
25425     
25426     if (config.store && !this.ds) {
25427         this.store= Roo.factory(config.store, Roo.data);
25428         this.ds = this.store;
25429         this.ds.xmodule = this.xmodule || false;
25430     }
25431     
25432     this.toolbarItems = [];
25433     if (config.items) {
25434         this.toolbarItems = config.items;
25435     }
25436     
25437     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25438     
25439     this.cursor = 0;
25440     
25441     if (this.ds) { 
25442         this.bind(this.ds);
25443     }
25444     
25445     if (Roo.bootstrap.version == 4) {
25446         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
25447     } else {
25448         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
25449     }
25450     
25451 };
25452
25453 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
25454     /**
25455      * @cfg {Roo.data.Store} dataSource
25456      * The underlying data store providing the paged data
25457      */
25458     /**
25459      * @cfg {String/HTMLElement/Element} container
25460      * container The id or element that will contain the toolbar
25461      */
25462     /**
25463      * @cfg {Boolean} displayInfo
25464      * True to display the displayMsg (defaults to false)
25465      */
25466     /**
25467      * @cfg {Number} pageSize
25468      * The number of records to display per page (defaults to 20)
25469      */
25470     pageSize: 20,
25471     /**
25472      * @cfg {String} displayMsg
25473      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25474      */
25475     displayMsg : 'Displaying {0} - {1} of {2}',
25476     /**
25477      * @cfg {String} emptyMsg
25478      * The message to display when no records are found (defaults to "No data to display")
25479      */
25480     emptyMsg : 'No data to display',
25481     /**
25482      * Customizable piece of the default paging text (defaults to "Page")
25483      * @type String
25484      */
25485     beforePageText : "Page",
25486     /**
25487      * Customizable piece of the default paging text (defaults to "of %0")
25488      * @type String
25489      */
25490     afterPageText : "of {0}",
25491     /**
25492      * Customizable piece of the default paging text (defaults to "First Page")
25493      * @type String
25494      */
25495     firstText : "First Page",
25496     /**
25497      * Customizable piece of the default paging text (defaults to "Previous Page")
25498      * @type String
25499      */
25500     prevText : "Previous Page",
25501     /**
25502      * Customizable piece of the default paging text (defaults to "Next Page")
25503      * @type String
25504      */
25505     nextText : "Next Page",
25506     /**
25507      * Customizable piece of the default paging text (defaults to "Last Page")
25508      * @type String
25509      */
25510     lastText : "Last Page",
25511     /**
25512      * Customizable piece of the default paging text (defaults to "Refresh")
25513      * @type String
25514      */
25515     refreshText : "Refresh",
25516
25517     buttons : false,
25518     // private
25519     onRender : function(ct, position) 
25520     {
25521         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25522         this.navgroup.parentId = this.id;
25523         this.navgroup.onRender(this.el, null);
25524         // add the buttons to the navgroup
25525         
25526         if(this.displayInfo){
25527             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25528             this.displayEl = this.el.select('.x-paging-info', true).first();
25529 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25530 //            this.displayEl = navel.el.select('span',true).first();
25531         }
25532         
25533         var _this = this;
25534         
25535         if(this.buttons){
25536             Roo.each(_this.buttons, function(e){ // this might need to use render????
25537                Roo.factory(e).render(_this.el);
25538             });
25539         }
25540             
25541         Roo.each(_this.toolbarItems, function(e) {
25542             _this.navgroup.addItem(e);
25543         });
25544         
25545         
25546         this.first = this.navgroup.addItem({
25547             tooltip: this.firstText,
25548             cls: "prev btn-outline-secondary",
25549             html : ' <i class="fa fa-step-backward"></i>',
25550             disabled: true,
25551             preventDefault: true,
25552             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25553         });
25554         
25555         this.prev =  this.navgroup.addItem({
25556             tooltip: this.prevText,
25557             cls: "prev btn-outline-secondary",
25558             html : ' <i class="fa fa-backward"></i>',
25559             disabled: true,
25560             preventDefault: true,
25561             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25562         });
25563     //this.addSeparator();
25564         
25565         
25566         var field = this.navgroup.addItem( {
25567             tagtype : 'span',
25568             cls : 'x-paging-position  btn-outline-secondary',
25569              disabled: true,
25570             html : this.beforePageText  +
25571                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25572                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25573          } ); //?? escaped?
25574         
25575         this.field = field.el.select('input', true).first();
25576         this.field.on("keydown", this.onPagingKeydown, this);
25577         this.field.on("focus", function(){this.dom.select();});
25578     
25579     
25580         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25581         //this.field.setHeight(18);
25582         //this.addSeparator();
25583         this.next = this.navgroup.addItem({
25584             tooltip: this.nextText,
25585             cls: "next btn-outline-secondary",
25586             html : ' <i class="fa fa-forward"></i>',
25587             disabled: true,
25588             preventDefault: true,
25589             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25590         });
25591         this.last = this.navgroup.addItem({
25592             tooltip: this.lastText,
25593             html : ' <i class="fa fa-step-forward"></i>',
25594             cls: "next btn-outline-secondary",
25595             disabled: true,
25596             preventDefault: true,
25597             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25598         });
25599     //this.addSeparator();
25600         this.loading = this.navgroup.addItem({
25601             tooltip: this.refreshText,
25602             cls: "btn-outline-secondary",
25603             html : ' <i class="fa fa-refresh"></i>',
25604             preventDefault: true,
25605             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25606         });
25607         
25608     },
25609
25610     // private
25611     updateInfo : function(){
25612         if(this.displayEl){
25613             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25614             var msg = count == 0 ?
25615                 this.emptyMsg :
25616                 String.format(
25617                     this.displayMsg,
25618                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25619                 );
25620             this.displayEl.update(msg);
25621         }
25622     },
25623
25624     // private
25625     onLoad : function(ds, r, o)
25626     {
25627         this.cursor = o.params.start ? o.params.start : 0;
25628         
25629         var d = this.getPageData(),
25630             ap = d.activePage,
25631             ps = d.pages;
25632         
25633         
25634         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25635         this.field.dom.value = ap;
25636         this.first.setDisabled(ap == 1);
25637         this.prev.setDisabled(ap == 1);
25638         this.next.setDisabled(ap == ps);
25639         this.last.setDisabled(ap == ps);
25640         this.loading.enable();
25641         this.updateInfo();
25642     },
25643
25644     // private
25645     getPageData : function(){
25646         var total = this.ds.getTotalCount();
25647         return {
25648             total : total,
25649             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25650             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25651         };
25652     },
25653
25654     // private
25655     onLoadError : function(){
25656         this.loading.enable();
25657     },
25658
25659     // private
25660     onPagingKeydown : function(e){
25661         var k = e.getKey();
25662         var d = this.getPageData();
25663         if(k == e.RETURN){
25664             var v = this.field.dom.value, pageNum;
25665             if(!v || isNaN(pageNum = parseInt(v, 10))){
25666                 this.field.dom.value = d.activePage;
25667                 return;
25668             }
25669             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25670             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25671             e.stopEvent();
25672         }
25673         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))
25674         {
25675           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25676           this.field.dom.value = pageNum;
25677           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25678           e.stopEvent();
25679         }
25680         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25681         {
25682           var v = this.field.dom.value, pageNum; 
25683           var increment = (e.shiftKey) ? 10 : 1;
25684           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25685                 increment *= -1;
25686           }
25687           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25688             this.field.dom.value = d.activePage;
25689             return;
25690           }
25691           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25692           {
25693             this.field.dom.value = parseInt(v, 10) + increment;
25694             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25695             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25696           }
25697           e.stopEvent();
25698         }
25699     },
25700
25701     // private
25702     beforeLoad : function(){
25703         if(this.loading){
25704             this.loading.disable();
25705         }
25706     },
25707
25708     // private
25709     onClick : function(which){
25710         
25711         var ds = this.ds;
25712         if (!ds) {
25713             return;
25714         }
25715         
25716         switch(which){
25717             case "first":
25718                 ds.load({params:{start: 0, limit: this.pageSize}});
25719             break;
25720             case "prev":
25721                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25722             break;
25723             case "next":
25724                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25725             break;
25726             case "last":
25727                 var total = ds.getTotalCount();
25728                 var extra = total % this.pageSize;
25729                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25730                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25731             break;
25732             case "refresh":
25733                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25734             break;
25735         }
25736     },
25737
25738     /**
25739      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25740      * @param {Roo.data.Store} store The data store to unbind
25741      */
25742     unbind : function(ds){
25743         ds.un("beforeload", this.beforeLoad, this);
25744         ds.un("load", this.onLoad, this);
25745         ds.un("loadexception", this.onLoadError, this);
25746         ds.un("remove", this.updateInfo, this);
25747         ds.un("add", this.updateInfo, this);
25748         this.ds = undefined;
25749     },
25750
25751     /**
25752      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25753      * @param {Roo.data.Store} store The data store to bind
25754      */
25755     bind : function(ds){
25756         ds.on("beforeload", this.beforeLoad, this);
25757         ds.on("load", this.onLoad, this);
25758         ds.on("loadexception", this.onLoadError, this);
25759         ds.on("remove", this.updateInfo, this);
25760         ds.on("add", this.updateInfo, this);
25761         this.ds = ds;
25762     }
25763 });/*
25764  * - LGPL
25765  *
25766  * element
25767  * 
25768  */
25769
25770 /**
25771  * @class Roo.bootstrap.MessageBar
25772  * @extends Roo.bootstrap.Component
25773  * Bootstrap MessageBar class
25774  * @cfg {String} html contents of the MessageBar
25775  * @cfg {String} weight (info | success | warning | danger) default info
25776  * @cfg {String} beforeClass insert the bar before the given class
25777  * @cfg {Boolean} closable (true | false) default false
25778  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25779  * 
25780  * @constructor
25781  * Create a new Element
25782  * @param {Object} config The config object
25783  */
25784
25785 Roo.bootstrap.MessageBar = function(config){
25786     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25787 };
25788
25789 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25790     
25791     html: '',
25792     weight: 'info',
25793     closable: false,
25794     fixed: false,
25795     beforeClass: 'bootstrap-sticky-wrap',
25796     
25797     getAutoCreate : function(){
25798         
25799         var cfg = {
25800             tag: 'div',
25801             cls: 'alert alert-dismissable alert-' + this.weight,
25802             cn: [
25803                 {
25804                     tag: 'span',
25805                     cls: 'message',
25806                     html: this.html || ''
25807                 }
25808             ]
25809         };
25810         
25811         if(this.fixed){
25812             cfg.cls += ' alert-messages-fixed';
25813         }
25814         
25815         if(this.closable){
25816             cfg.cn.push({
25817                 tag: 'button',
25818                 cls: 'close',
25819                 html: 'x'
25820             });
25821         }
25822         
25823         return cfg;
25824     },
25825     
25826     onRender : function(ct, position)
25827     {
25828         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25829         
25830         if(!this.el){
25831             var cfg = Roo.apply({},  this.getAutoCreate());
25832             cfg.id = Roo.id();
25833             
25834             if (this.cls) {
25835                 cfg.cls += ' ' + this.cls;
25836             }
25837             if (this.style) {
25838                 cfg.style = this.style;
25839             }
25840             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25841             
25842             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25843         }
25844         
25845         this.el.select('>button.close').on('click', this.hide, this);
25846         
25847     },
25848     
25849     show : function()
25850     {
25851         if (!this.rendered) {
25852             this.render();
25853         }
25854         
25855         this.el.show();
25856         
25857         this.fireEvent('show', this);
25858         
25859     },
25860     
25861     hide : function()
25862     {
25863         if (!this.rendered) {
25864             this.render();
25865         }
25866         
25867         this.el.hide();
25868         
25869         this.fireEvent('hide', this);
25870     },
25871     
25872     update : function()
25873     {
25874 //        var e = this.el.dom.firstChild;
25875 //        
25876 //        if(this.closable){
25877 //            e = e.nextSibling;
25878 //        }
25879 //        
25880 //        e.data = this.html || '';
25881
25882         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25883     }
25884    
25885 });
25886
25887  
25888
25889      /*
25890  * - LGPL
25891  *
25892  * Graph
25893  * 
25894  */
25895
25896
25897 /**
25898  * @class Roo.bootstrap.Graph
25899  * @extends Roo.bootstrap.Component
25900  * Bootstrap Graph class
25901 > Prameters
25902  -sm {number} sm 4
25903  -md {number} md 5
25904  @cfg {String} graphtype  bar | vbar | pie
25905  @cfg {number} g_x coodinator | centre x (pie)
25906  @cfg {number} g_y coodinator | centre y (pie)
25907  @cfg {number} g_r radius (pie)
25908  @cfg {number} g_height height of the chart (respected by all elements in the set)
25909  @cfg {number} g_width width of the chart (respected by all elements in the set)
25910  @cfg {Object} title The title of the chart
25911     
25912  -{Array}  values
25913  -opts (object) options for the chart 
25914      o {
25915      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25916      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25917      o vgutter (number)
25918      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.
25919      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25920      o to
25921      o stretch (boolean)
25922      o }
25923  -opts (object) options for the pie
25924      o{
25925      o cut
25926      o startAngle (number)
25927      o endAngle (number)
25928      } 
25929  *
25930  * @constructor
25931  * Create a new Input
25932  * @param {Object} config The config object
25933  */
25934
25935 Roo.bootstrap.Graph = function(config){
25936     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25937     
25938     this.addEvents({
25939         // img events
25940         /**
25941          * @event click
25942          * The img click event for the img.
25943          * @param {Roo.EventObject} e
25944          */
25945         "click" : true
25946     });
25947 };
25948
25949 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25950     
25951     sm: 4,
25952     md: 5,
25953     graphtype: 'bar',
25954     g_height: 250,
25955     g_width: 400,
25956     g_x: 50,
25957     g_y: 50,
25958     g_r: 30,
25959     opts:{
25960         //g_colors: this.colors,
25961         g_type: 'soft',
25962         g_gutter: '20%'
25963
25964     },
25965     title : false,
25966
25967     getAutoCreate : function(){
25968         
25969         var cfg = {
25970             tag: 'div',
25971             html : null
25972         };
25973         
25974         
25975         return  cfg;
25976     },
25977
25978     onRender : function(ct,position){
25979         
25980         
25981         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25982         
25983         if (typeof(Raphael) == 'undefined') {
25984             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25985             return;
25986         }
25987         
25988         this.raphael = Raphael(this.el.dom);
25989         
25990                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25991                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25992                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25993                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25994                 /*
25995                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25996                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25997                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25998                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25999                 
26000                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
26001                 r.barchart(330, 10, 300, 220, data1);
26002                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
26003                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
26004                 */
26005                 
26006                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
26007                 // r.barchart(30, 30, 560, 250,  xdata, {
26008                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
26009                 //     axis : "0 0 1 1",
26010                 //     axisxlabels :  xdata
26011                 //     //yvalues : cols,
26012                    
26013                 // });
26014 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
26015 //        
26016 //        this.load(null,xdata,{
26017 //                axis : "0 0 1 1",
26018 //                axisxlabels :  xdata
26019 //                });
26020
26021     },
26022
26023     load : function(graphtype,xdata,opts)
26024     {
26025         this.raphael.clear();
26026         if(!graphtype) {
26027             graphtype = this.graphtype;
26028         }
26029         if(!opts){
26030             opts = this.opts;
26031         }
26032         var r = this.raphael,
26033             fin = function () {
26034                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
26035             },
26036             fout = function () {
26037                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
26038             },
26039             pfin = function() {
26040                 this.sector.stop();
26041                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
26042
26043                 if (this.label) {
26044                     this.label[0].stop();
26045                     this.label[0].attr({ r: 7.5 });
26046                     this.label[1].attr({ "font-weight": 800 });
26047                 }
26048             },
26049             pfout = function() {
26050                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
26051
26052                 if (this.label) {
26053                     this.label[0].animate({ r: 5 }, 500, "bounce");
26054                     this.label[1].attr({ "font-weight": 400 });
26055                 }
26056             };
26057
26058         switch(graphtype){
26059             case 'bar':
26060                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26061                 break;
26062             case 'hbar':
26063                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
26064                 break;
26065             case 'pie':
26066 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
26067 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
26068 //            
26069                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
26070                 
26071                 break;
26072
26073         }
26074         
26075         if(this.title){
26076             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
26077         }
26078         
26079     },
26080     
26081     setTitle: function(o)
26082     {
26083         this.title = o;
26084     },
26085     
26086     initEvents: function() {
26087         
26088         if(!this.href){
26089             this.el.on('click', this.onClick, this);
26090         }
26091     },
26092     
26093     onClick : function(e)
26094     {
26095         Roo.log('img onclick');
26096         this.fireEvent('click', this, e);
26097     }
26098    
26099 });
26100
26101  
26102 /*
26103  * - LGPL
26104  *
26105  * numberBox
26106  * 
26107  */
26108 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26109
26110 /**
26111  * @class Roo.bootstrap.dash.NumberBox
26112  * @extends Roo.bootstrap.Component
26113  * Bootstrap NumberBox class
26114  * @cfg {String} headline Box headline
26115  * @cfg {String} content Box content
26116  * @cfg {String} icon Box icon
26117  * @cfg {String} footer Footer text
26118  * @cfg {String} fhref Footer href
26119  * 
26120  * @constructor
26121  * Create a new NumberBox
26122  * @param {Object} config The config object
26123  */
26124
26125
26126 Roo.bootstrap.dash.NumberBox = function(config){
26127     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
26128     
26129 };
26130
26131 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
26132     
26133     headline : '',
26134     content : '',
26135     icon : '',
26136     footer : '',
26137     fhref : '',
26138     ficon : '',
26139     
26140     getAutoCreate : function(){
26141         
26142         var cfg = {
26143             tag : 'div',
26144             cls : 'small-box ',
26145             cn : [
26146                 {
26147                     tag : 'div',
26148                     cls : 'inner',
26149                     cn :[
26150                         {
26151                             tag : 'h3',
26152                             cls : 'roo-headline',
26153                             html : this.headline
26154                         },
26155                         {
26156                             tag : 'p',
26157                             cls : 'roo-content',
26158                             html : this.content
26159                         }
26160                     ]
26161                 }
26162             ]
26163         };
26164         
26165         if(this.icon){
26166             cfg.cn.push({
26167                 tag : 'div',
26168                 cls : 'icon',
26169                 cn :[
26170                     {
26171                         tag : 'i',
26172                         cls : 'ion ' + this.icon
26173                     }
26174                 ]
26175             });
26176         }
26177         
26178         if(this.footer){
26179             var footer = {
26180                 tag : 'a',
26181                 cls : 'small-box-footer',
26182                 href : this.fhref || '#',
26183                 html : this.footer
26184             };
26185             
26186             cfg.cn.push(footer);
26187             
26188         }
26189         
26190         return  cfg;
26191     },
26192
26193     onRender : function(ct,position){
26194         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
26195
26196
26197        
26198                 
26199     },
26200
26201     setHeadline: function (value)
26202     {
26203         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
26204     },
26205     
26206     setFooter: function (value, href)
26207     {
26208         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
26209         
26210         if(href){
26211             this.el.select('a.small-box-footer',true).first().attr('href', href);
26212         }
26213         
26214     },
26215
26216     setContent: function (value)
26217     {
26218         this.el.select('.roo-content',true).first().dom.innerHTML = value;
26219     },
26220
26221     initEvents: function() 
26222     {   
26223         
26224     }
26225     
26226 });
26227
26228  
26229 /*
26230  * - LGPL
26231  *
26232  * TabBox
26233  * 
26234  */
26235 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26236
26237 /**
26238  * @class Roo.bootstrap.dash.TabBox
26239  * @extends Roo.bootstrap.Component
26240  * Bootstrap TabBox class
26241  * @cfg {String} title Title of the TabBox
26242  * @cfg {String} icon Icon of the TabBox
26243  * @cfg {Boolean} showtabs (true|false) show the tabs default true
26244  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
26245  * 
26246  * @constructor
26247  * Create a new TabBox
26248  * @param {Object} config The config object
26249  */
26250
26251
26252 Roo.bootstrap.dash.TabBox = function(config){
26253     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
26254     this.addEvents({
26255         // raw events
26256         /**
26257          * @event addpane
26258          * When a pane is added
26259          * @param {Roo.bootstrap.dash.TabPane} pane
26260          */
26261         "addpane" : true,
26262         /**
26263          * @event activatepane
26264          * When a pane is activated
26265          * @param {Roo.bootstrap.dash.TabPane} pane
26266          */
26267         "activatepane" : true
26268         
26269          
26270     });
26271     
26272     this.panes = [];
26273 };
26274
26275 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
26276
26277     title : '',
26278     icon : false,
26279     showtabs : true,
26280     tabScrollable : false,
26281     
26282     getChildContainer : function()
26283     {
26284         return this.el.select('.tab-content', true).first();
26285     },
26286     
26287     getAutoCreate : function(){
26288         
26289         var header = {
26290             tag: 'li',
26291             cls: 'pull-left header',
26292             html: this.title,
26293             cn : []
26294         };
26295         
26296         if(this.icon){
26297             header.cn.push({
26298                 tag: 'i',
26299                 cls: 'fa ' + this.icon
26300             });
26301         }
26302         
26303         var h = {
26304             tag: 'ul',
26305             cls: 'nav nav-tabs pull-right',
26306             cn: [
26307                 header
26308             ]
26309         };
26310         
26311         if(this.tabScrollable){
26312             h = {
26313                 tag: 'div',
26314                 cls: 'tab-header',
26315                 cn: [
26316                     {
26317                         tag: 'ul',
26318                         cls: 'nav nav-tabs pull-right',
26319                         cn: [
26320                             header
26321                         ]
26322                     }
26323                 ]
26324             };
26325         }
26326         
26327         var cfg = {
26328             tag: 'div',
26329             cls: 'nav-tabs-custom',
26330             cn: [
26331                 h,
26332                 {
26333                     tag: 'div',
26334                     cls: 'tab-content no-padding',
26335                     cn: []
26336                 }
26337             ]
26338         };
26339
26340         return  cfg;
26341     },
26342     initEvents : function()
26343     {
26344         //Roo.log('add add pane handler');
26345         this.on('addpane', this.onAddPane, this);
26346     },
26347      /**
26348      * Updates the box title
26349      * @param {String} html to set the title to.
26350      */
26351     setTitle : function(value)
26352     {
26353         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
26354     },
26355     onAddPane : function(pane)
26356     {
26357         this.panes.push(pane);
26358         //Roo.log('addpane');
26359         //Roo.log(pane);
26360         // tabs are rendere left to right..
26361         if(!this.showtabs){
26362             return;
26363         }
26364         
26365         var ctr = this.el.select('.nav-tabs', true).first();
26366          
26367          
26368         var existing = ctr.select('.nav-tab',true);
26369         var qty = existing.getCount();;
26370         
26371         
26372         var tab = ctr.createChild({
26373             tag : 'li',
26374             cls : 'nav-tab' + (qty ? '' : ' active'),
26375             cn : [
26376                 {
26377                     tag : 'a',
26378                     href:'#',
26379                     html : pane.title
26380                 }
26381             ]
26382         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
26383         pane.tab = tab;
26384         
26385         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
26386         if (!qty) {
26387             pane.el.addClass('active');
26388         }
26389         
26390                 
26391     },
26392     onTabClick : function(ev,un,ob,pane)
26393     {
26394         //Roo.log('tab - prev default');
26395         ev.preventDefault();
26396         
26397         
26398         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26399         pane.tab.addClass('active');
26400         //Roo.log(pane.title);
26401         this.getChildContainer().select('.tab-pane',true).removeClass('active');
26402         // technically we should have a deactivate event.. but maybe add later.
26403         // and it should not de-activate the selected tab...
26404         this.fireEvent('activatepane', pane);
26405         pane.el.addClass('active');
26406         pane.fireEvent('activate');
26407         
26408         
26409     },
26410     
26411     getActivePane : function()
26412     {
26413         var r = false;
26414         Roo.each(this.panes, function(p) {
26415             if(p.el.hasClass('active')){
26416                 r = p;
26417                 return false;
26418             }
26419             
26420             return;
26421         });
26422         
26423         return r;
26424     }
26425     
26426     
26427 });
26428
26429  
26430 /*
26431  * - LGPL
26432  *
26433  * Tab pane
26434  * 
26435  */
26436 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26437 /**
26438  * @class Roo.bootstrap.TabPane
26439  * @extends Roo.bootstrap.Component
26440  * Bootstrap TabPane class
26441  * @cfg {Boolean} active (false | true) Default false
26442  * @cfg {String} title title of panel
26443
26444  * 
26445  * @constructor
26446  * Create a new TabPane
26447  * @param {Object} config The config object
26448  */
26449
26450 Roo.bootstrap.dash.TabPane = function(config){
26451     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
26452     
26453     this.addEvents({
26454         // raw events
26455         /**
26456          * @event activate
26457          * When a pane is activated
26458          * @param {Roo.bootstrap.dash.TabPane} pane
26459          */
26460         "activate" : true
26461          
26462     });
26463 };
26464
26465 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
26466     
26467     active : false,
26468     title : '',
26469     
26470     // the tabBox that this is attached to.
26471     tab : false,
26472      
26473     getAutoCreate : function() 
26474     {
26475         var cfg = {
26476             tag: 'div',
26477             cls: 'tab-pane'
26478         };
26479         
26480         if(this.active){
26481             cfg.cls += ' active';
26482         }
26483         
26484         return cfg;
26485     },
26486     initEvents  : function()
26487     {
26488         //Roo.log('trigger add pane handler');
26489         this.parent().fireEvent('addpane', this)
26490     },
26491     
26492      /**
26493      * Updates the tab title 
26494      * @param {String} html to set the title to.
26495      */
26496     setTitle: function(str)
26497     {
26498         if (!this.tab) {
26499             return;
26500         }
26501         this.title = str;
26502         this.tab.select('a', true).first().dom.innerHTML = str;
26503         
26504     }
26505     
26506     
26507     
26508 });
26509
26510  
26511
26512
26513  /*
26514  * - LGPL
26515  *
26516  * menu
26517  * 
26518  */
26519 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26520
26521 /**
26522  * @class Roo.bootstrap.menu.Menu
26523  * @extends Roo.bootstrap.Component
26524  * Bootstrap Menu class - container for Menu
26525  * @cfg {String} html Text of the menu
26526  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26527  * @cfg {String} icon Font awesome icon
26528  * @cfg {String} pos Menu align to (top | bottom) default bottom
26529  * 
26530  * 
26531  * @constructor
26532  * Create a new Menu
26533  * @param {Object} config The config object
26534  */
26535
26536
26537 Roo.bootstrap.menu.Menu = function(config){
26538     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26539     
26540     this.addEvents({
26541         /**
26542          * @event beforeshow
26543          * Fires before this menu is displayed
26544          * @param {Roo.bootstrap.menu.Menu} this
26545          */
26546         beforeshow : true,
26547         /**
26548          * @event beforehide
26549          * Fires before this menu is hidden
26550          * @param {Roo.bootstrap.menu.Menu} this
26551          */
26552         beforehide : true,
26553         /**
26554          * @event show
26555          * Fires after this menu is displayed
26556          * @param {Roo.bootstrap.menu.Menu} this
26557          */
26558         show : true,
26559         /**
26560          * @event hide
26561          * Fires after this menu is hidden
26562          * @param {Roo.bootstrap.menu.Menu} this
26563          */
26564         hide : true,
26565         /**
26566          * @event click
26567          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26568          * @param {Roo.bootstrap.menu.Menu} this
26569          * @param {Roo.EventObject} e
26570          */
26571         click : true
26572     });
26573     
26574 };
26575
26576 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26577     
26578     submenu : false,
26579     html : '',
26580     weight : 'default',
26581     icon : false,
26582     pos : 'bottom',
26583     
26584     
26585     getChildContainer : function() {
26586         if(this.isSubMenu){
26587             return this.el;
26588         }
26589         
26590         return this.el.select('ul.dropdown-menu', true).first();  
26591     },
26592     
26593     getAutoCreate : function()
26594     {
26595         var text = [
26596             {
26597                 tag : 'span',
26598                 cls : 'roo-menu-text',
26599                 html : this.html
26600             }
26601         ];
26602         
26603         if(this.icon){
26604             text.unshift({
26605                 tag : 'i',
26606                 cls : 'fa ' + this.icon
26607             })
26608         }
26609         
26610         
26611         var cfg = {
26612             tag : 'div',
26613             cls : 'btn-group',
26614             cn : [
26615                 {
26616                     tag : 'button',
26617                     cls : 'dropdown-button btn btn-' + this.weight,
26618                     cn : text
26619                 },
26620                 {
26621                     tag : 'button',
26622                     cls : 'dropdown-toggle btn btn-' + this.weight,
26623                     cn : [
26624                         {
26625                             tag : 'span',
26626                             cls : 'caret'
26627                         }
26628                     ]
26629                 },
26630                 {
26631                     tag : 'ul',
26632                     cls : 'dropdown-menu'
26633                 }
26634             ]
26635             
26636         };
26637         
26638         if(this.pos == 'top'){
26639             cfg.cls += ' dropup';
26640         }
26641         
26642         if(this.isSubMenu){
26643             cfg = {
26644                 tag : 'ul',
26645                 cls : 'dropdown-menu'
26646             }
26647         }
26648         
26649         return cfg;
26650     },
26651     
26652     onRender : function(ct, position)
26653     {
26654         this.isSubMenu = ct.hasClass('dropdown-submenu');
26655         
26656         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26657     },
26658     
26659     initEvents : function() 
26660     {
26661         if(this.isSubMenu){
26662             return;
26663         }
26664         
26665         this.hidden = true;
26666         
26667         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26668         this.triggerEl.on('click', this.onTriggerPress, this);
26669         
26670         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26671         this.buttonEl.on('click', this.onClick, this);
26672         
26673     },
26674     
26675     list : function()
26676     {
26677         if(this.isSubMenu){
26678             return this.el;
26679         }
26680         
26681         return this.el.select('ul.dropdown-menu', true).first();
26682     },
26683     
26684     onClick : function(e)
26685     {
26686         this.fireEvent("click", this, e);
26687     },
26688     
26689     onTriggerPress  : function(e)
26690     {   
26691         if (this.isVisible()) {
26692             this.hide();
26693         } else {
26694             this.show();
26695         }
26696     },
26697     
26698     isVisible : function(){
26699         return !this.hidden;
26700     },
26701     
26702     show : function()
26703     {
26704         this.fireEvent("beforeshow", this);
26705         
26706         this.hidden = false;
26707         this.el.addClass('open');
26708         
26709         Roo.get(document).on("mouseup", this.onMouseUp, this);
26710         
26711         this.fireEvent("show", this);
26712         
26713         
26714     },
26715     
26716     hide : function()
26717     {
26718         this.fireEvent("beforehide", this);
26719         
26720         this.hidden = true;
26721         this.el.removeClass('open');
26722         
26723         Roo.get(document).un("mouseup", this.onMouseUp);
26724         
26725         this.fireEvent("hide", this);
26726     },
26727     
26728     onMouseUp : function()
26729     {
26730         this.hide();
26731     }
26732     
26733 });
26734
26735  
26736  /*
26737  * - LGPL
26738  *
26739  * menu item
26740  * 
26741  */
26742 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26743
26744 /**
26745  * @class Roo.bootstrap.menu.Item
26746  * @extends Roo.bootstrap.Component
26747  * Bootstrap MenuItem class
26748  * @cfg {Boolean} submenu (true | false) default false
26749  * @cfg {String} html text of the item
26750  * @cfg {String} href the link
26751  * @cfg {Boolean} disable (true | false) default false
26752  * @cfg {Boolean} preventDefault (true | false) default true
26753  * @cfg {String} icon Font awesome icon
26754  * @cfg {String} pos Submenu align to (left | right) default right 
26755  * 
26756  * 
26757  * @constructor
26758  * Create a new Item
26759  * @param {Object} config The config object
26760  */
26761
26762
26763 Roo.bootstrap.menu.Item = function(config){
26764     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26765     this.addEvents({
26766         /**
26767          * @event mouseover
26768          * Fires when the mouse is hovering over this menu
26769          * @param {Roo.bootstrap.menu.Item} this
26770          * @param {Roo.EventObject} e
26771          */
26772         mouseover : true,
26773         /**
26774          * @event mouseout
26775          * Fires when the mouse exits this menu
26776          * @param {Roo.bootstrap.menu.Item} this
26777          * @param {Roo.EventObject} e
26778          */
26779         mouseout : true,
26780         // raw events
26781         /**
26782          * @event click
26783          * The raw click event for the entire grid.
26784          * @param {Roo.EventObject} e
26785          */
26786         click : true
26787     });
26788 };
26789
26790 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26791     
26792     submenu : false,
26793     href : '',
26794     html : '',
26795     preventDefault: true,
26796     disable : false,
26797     icon : false,
26798     pos : 'right',
26799     
26800     getAutoCreate : function()
26801     {
26802         var text = [
26803             {
26804                 tag : 'span',
26805                 cls : 'roo-menu-item-text',
26806                 html : this.html
26807             }
26808         ];
26809         
26810         if(this.icon){
26811             text.unshift({
26812                 tag : 'i',
26813                 cls : 'fa ' + this.icon
26814             })
26815         }
26816         
26817         var cfg = {
26818             tag : 'li',
26819             cn : [
26820                 {
26821                     tag : 'a',
26822                     href : this.href || '#',
26823                     cn : text
26824                 }
26825             ]
26826         };
26827         
26828         if(this.disable){
26829             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26830         }
26831         
26832         if(this.submenu){
26833             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26834             
26835             if(this.pos == 'left'){
26836                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26837             }
26838         }
26839         
26840         return cfg;
26841     },
26842     
26843     initEvents : function() 
26844     {
26845         this.el.on('mouseover', this.onMouseOver, this);
26846         this.el.on('mouseout', this.onMouseOut, this);
26847         
26848         this.el.select('a', true).first().on('click', this.onClick, this);
26849         
26850     },
26851     
26852     onClick : function(e)
26853     {
26854         if(this.preventDefault){
26855             e.preventDefault();
26856         }
26857         
26858         this.fireEvent("click", this, e);
26859     },
26860     
26861     onMouseOver : function(e)
26862     {
26863         if(this.submenu && this.pos == 'left'){
26864             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26865         }
26866         
26867         this.fireEvent("mouseover", this, e);
26868     },
26869     
26870     onMouseOut : function(e)
26871     {
26872         this.fireEvent("mouseout", this, e);
26873     }
26874 });
26875
26876  
26877
26878  /*
26879  * - LGPL
26880  *
26881  * menu separator
26882  * 
26883  */
26884 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26885
26886 /**
26887  * @class Roo.bootstrap.menu.Separator
26888  * @extends Roo.bootstrap.Component
26889  * Bootstrap Separator class
26890  * 
26891  * @constructor
26892  * Create a new Separator
26893  * @param {Object} config The config object
26894  */
26895
26896
26897 Roo.bootstrap.menu.Separator = function(config){
26898     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26899 };
26900
26901 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26902     
26903     getAutoCreate : function(){
26904         var cfg = {
26905             tag : 'li',
26906             cls: 'divider'
26907         };
26908         
26909         return cfg;
26910     }
26911    
26912 });
26913
26914  
26915
26916  /*
26917  * - LGPL
26918  *
26919  * Tooltip
26920  * 
26921  */
26922
26923 /**
26924  * @class Roo.bootstrap.Tooltip
26925  * Bootstrap Tooltip class
26926  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26927  * to determine which dom element triggers the tooltip.
26928  * 
26929  * It needs to add support for additional attributes like tooltip-position
26930  * 
26931  * @constructor
26932  * Create a new Toolti
26933  * @param {Object} config The config object
26934  */
26935
26936 Roo.bootstrap.Tooltip = function(config){
26937     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26938     
26939     this.alignment = Roo.bootstrap.Tooltip.alignment;
26940     
26941     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26942         this.alignment = config.alignment;
26943     }
26944     
26945 };
26946
26947 Roo.apply(Roo.bootstrap.Tooltip, {
26948     /**
26949      * @function init initialize tooltip monitoring.
26950      * @static
26951      */
26952     currentEl : false,
26953     currentTip : false,
26954     currentRegion : false,
26955     
26956     //  init : delay?
26957     
26958     init : function()
26959     {
26960         Roo.get(document).on('mouseover', this.enter ,this);
26961         Roo.get(document).on('mouseout', this.leave, this);
26962          
26963         
26964         this.currentTip = new Roo.bootstrap.Tooltip();
26965     },
26966     
26967     enter : function(ev)
26968     {
26969         var dom = ev.getTarget();
26970         
26971         //Roo.log(['enter',dom]);
26972         var el = Roo.fly(dom);
26973         if (this.currentEl) {
26974             //Roo.log(dom);
26975             //Roo.log(this.currentEl);
26976             //Roo.log(this.currentEl.contains(dom));
26977             if (this.currentEl == el) {
26978                 return;
26979             }
26980             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26981                 return;
26982             }
26983
26984         }
26985         
26986         if (this.currentTip.el) {
26987             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26988         }    
26989         //Roo.log(ev);
26990         
26991         if(!el || el.dom == document){
26992             return;
26993         }
26994         
26995         var bindEl = el;
26996         
26997         // you can not look for children, as if el is the body.. then everythign is the child..
26998         if (!el.attr('tooltip')) { //
26999             if (!el.select("[tooltip]").elements.length) {
27000                 return;
27001             }
27002             // is the mouse over this child...?
27003             bindEl = el.select("[tooltip]").first();
27004             var xy = ev.getXY();
27005             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
27006                 //Roo.log("not in region.");
27007                 return;
27008             }
27009             //Roo.log("child element over..");
27010             
27011         }
27012         this.currentEl = bindEl;
27013         this.currentTip.bind(bindEl);
27014         this.currentRegion = Roo.lib.Region.getRegion(dom);
27015         this.currentTip.enter();
27016         
27017     },
27018     leave : function(ev)
27019     {
27020         var dom = ev.getTarget();
27021         //Roo.log(['leave',dom]);
27022         if (!this.currentEl) {
27023             return;
27024         }
27025         
27026         
27027         if (dom != this.currentEl.dom) {
27028             return;
27029         }
27030         var xy = ev.getXY();
27031         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
27032             return;
27033         }
27034         // only activate leave if mouse cursor is outside... bounding box..
27035         
27036         
27037         
27038         
27039         if (this.currentTip) {
27040             this.currentTip.leave();
27041         }
27042         //Roo.log('clear currentEl');
27043         this.currentEl = false;
27044         
27045         
27046     },
27047     alignment : {
27048         'left' : ['r-l', [-2,0], 'right'],
27049         'right' : ['l-r', [2,0], 'left'],
27050         'bottom' : ['t-b', [0,2], 'top'],
27051         'top' : [ 'b-t', [0,-2], 'bottom']
27052     }
27053     
27054 });
27055
27056
27057 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
27058     
27059     
27060     bindEl : false,
27061     
27062     delay : null, // can be { show : 300 , hide: 500}
27063     
27064     timeout : null,
27065     
27066     hoverState : null, //???
27067     
27068     placement : 'bottom', 
27069     
27070     alignment : false,
27071     
27072     getAutoCreate : function(){
27073     
27074         var cfg = {
27075            cls : 'tooltip',
27076            role : 'tooltip',
27077            cn : [
27078                 {
27079                     cls : 'tooltip-arrow'
27080                 },
27081                 {
27082                     cls : 'tooltip-inner'
27083                 }
27084            ]
27085         };
27086         
27087         return cfg;
27088     },
27089     bind : function(el)
27090     {
27091         this.bindEl = el;
27092     },
27093       
27094     
27095     enter : function () {
27096        
27097         if (this.timeout != null) {
27098             clearTimeout(this.timeout);
27099         }
27100         
27101         this.hoverState = 'in';
27102          //Roo.log("enter - show");
27103         if (!this.delay || !this.delay.show) {
27104             this.show();
27105             return;
27106         }
27107         var _t = this;
27108         this.timeout = setTimeout(function () {
27109             if (_t.hoverState == 'in') {
27110                 _t.show();
27111             }
27112         }, this.delay.show);
27113     },
27114     leave : function()
27115     {
27116         clearTimeout(this.timeout);
27117     
27118         this.hoverState = 'out';
27119          if (!this.delay || !this.delay.hide) {
27120             this.hide();
27121             return;
27122         }
27123        
27124         var _t = this;
27125         this.timeout = setTimeout(function () {
27126             //Roo.log("leave - timeout");
27127             
27128             if (_t.hoverState == 'out') {
27129                 _t.hide();
27130                 Roo.bootstrap.Tooltip.currentEl = false;
27131             }
27132         }, delay);
27133     },
27134     
27135     show : function (msg)
27136     {
27137         if (!this.el) {
27138             this.render(document.body);
27139         }
27140         // set content.
27141         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
27142         
27143         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
27144         
27145         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
27146         
27147         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
27148         
27149         var placement = typeof this.placement == 'function' ?
27150             this.placement.call(this, this.el, on_el) :
27151             this.placement;
27152             
27153         var autoToken = /\s?auto?\s?/i;
27154         var autoPlace = autoToken.test(placement);
27155         if (autoPlace) {
27156             placement = placement.replace(autoToken, '') || 'top';
27157         }
27158         
27159         //this.el.detach()
27160         //this.el.setXY([0,0]);
27161         this.el.show();
27162         //this.el.dom.style.display='block';
27163         
27164         //this.el.appendTo(on_el);
27165         
27166         var p = this.getPosition();
27167         var box = this.el.getBox();
27168         
27169         if (autoPlace) {
27170             // fixme..
27171         }
27172         
27173         var align = this.alignment[placement];
27174         
27175         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
27176         
27177         if(placement == 'top' || placement == 'bottom'){
27178             if(xy[0] < 0){
27179                 placement = 'right';
27180             }
27181             
27182             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
27183                 placement = 'left';
27184             }
27185             
27186             var scroll = Roo.select('body', true).first().getScroll();
27187             
27188             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
27189                 placement = 'top';
27190             }
27191             
27192             align = this.alignment[placement];
27193         }
27194         
27195         this.el.alignTo(this.bindEl, align[0],align[1]);
27196         //var arrow = this.el.select('.arrow',true).first();
27197         //arrow.set(align[2], 
27198         
27199         this.el.addClass(placement);
27200         
27201         this.el.addClass('in fade');
27202         
27203         this.hoverState = null;
27204         
27205         if (this.el.hasClass('fade')) {
27206             // fade it?
27207         }
27208         
27209     },
27210     hide : function()
27211     {
27212          
27213         if (!this.el) {
27214             return;
27215         }
27216         //this.el.setXY([0,0]);
27217         this.el.removeClass('in');
27218         //this.el.hide();
27219         
27220     }
27221     
27222 });
27223  
27224
27225  /*
27226  * - LGPL
27227  *
27228  * Location Picker
27229  * 
27230  */
27231
27232 /**
27233  * @class Roo.bootstrap.LocationPicker
27234  * @extends Roo.bootstrap.Component
27235  * Bootstrap LocationPicker class
27236  * @cfg {Number} latitude Position when init default 0
27237  * @cfg {Number} longitude Position when init default 0
27238  * @cfg {Number} zoom default 15
27239  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
27240  * @cfg {Boolean} mapTypeControl default false
27241  * @cfg {Boolean} disableDoubleClickZoom default false
27242  * @cfg {Boolean} scrollwheel default true
27243  * @cfg {Boolean} streetViewControl default false
27244  * @cfg {Number} radius default 0
27245  * @cfg {String} locationName
27246  * @cfg {Boolean} draggable default true
27247  * @cfg {Boolean} enableAutocomplete default false
27248  * @cfg {Boolean} enableReverseGeocode default true
27249  * @cfg {String} markerTitle
27250  * 
27251  * @constructor
27252  * Create a new LocationPicker
27253  * @param {Object} config The config object
27254  */
27255
27256
27257 Roo.bootstrap.LocationPicker = function(config){
27258     
27259     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
27260     
27261     this.addEvents({
27262         /**
27263          * @event initial
27264          * Fires when the picker initialized.
27265          * @param {Roo.bootstrap.LocationPicker} this
27266          * @param {Google Location} location
27267          */
27268         initial : true,
27269         /**
27270          * @event positionchanged
27271          * Fires when the picker position changed.
27272          * @param {Roo.bootstrap.LocationPicker} this
27273          * @param {Google Location} location
27274          */
27275         positionchanged : true,
27276         /**
27277          * @event resize
27278          * Fires when the map resize.
27279          * @param {Roo.bootstrap.LocationPicker} this
27280          */
27281         resize : true,
27282         /**
27283          * @event show
27284          * Fires when the map show.
27285          * @param {Roo.bootstrap.LocationPicker} this
27286          */
27287         show : true,
27288         /**
27289          * @event hide
27290          * Fires when the map hide.
27291          * @param {Roo.bootstrap.LocationPicker} this
27292          */
27293         hide : true,
27294         /**
27295          * @event mapClick
27296          * Fires when click the map.
27297          * @param {Roo.bootstrap.LocationPicker} this
27298          * @param {Map event} e
27299          */
27300         mapClick : true,
27301         /**
27302          * @event mapRightClick
27303          * Fires when right click the map.
27304          * @param {Roo.bootstrap.LocationPicker} this
27305          * @param {Map event} e
27306          */
27307         mapRightClick : true,
27308         /**
27309          * @event markerClick
27310          * Fires when click the marker.
27311          * @param {Roo.bootstrap.LocationPicker} this
27312          * @param {Map event} e
27313          */
27314         markerClick : true,
27315         /**
27316          * @event markerRightClick
27317          * Fires when right click the marker.
27318          * @param {Roo.bootstrap.LocationPicker} this
27319          * @param {Map event} e
27320          */
27321         markerRightClick : true,
27322         /**
27323          * @event OverlayViewDraw
27324          * Fires when OverlayView Draw
27325          * @param {Roo.bootstrap.LocationPicker} this
27326          */
27327         OverlayViewDraw : true,
27328         /**
27329          * @event OverlayViewOnAdd
27330          * Fires when OverlayView Draw
27331          * @param {Roo.bootstrap.LocationPicker} this
27332          */
27333         OverlayViewOnAdd : true,
27334         /**
27335          * @event OverlayViewOnRemove
27336          * Fires when OverlayView Draw
27337          * @param {Roo.bootstrap.LocationPicker} this
27338          */
27339         OverlayViewOnRemove : true,
27340         /**
27341          * @event OverlayViewShow
27342          * Fires when OverlayView Draw
27343          * @param {Roo.bootstrap.LocationPicker} this
27344          * @param {Pixel} cpx
27345          */
27346         OverlayViewShow : true,
27347         /**
27348          * @event OverlayViewHide
27349          * Fires when OverlayView Draw
27350          * @param {Roo.bootstrap.LocationPicker} this
27351          */
27352         OverlayViewHide : true,
27353         /**
27354          * @event loadexception
27355          * Fires when load google lib failed.
27356          * @param {Roo.bootstrap.LocationPicker} this
27357          */
27358         loadexception : true
27359     });
27360         
27361 };
27362
27363 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
27364     
27365     gMapContext: false,
27366     
27367     latitude: 0,
27368     longitude: 0,
27369     zoom: 15,
27370     mapTypeId: false,
27371     mapTypeControl: false,
27372     disableDoubleClickZoom: false,
27373     scrollwheel: true,
27374     streetViewControl: false,
27375     radius: 0,
27376     locationName: '',
27377     draggable: true,
27378     enableAutocomplete: false,
27379     enableReverseGeocode: true,
27380     markerTitle: '',
27381     
27382     getAutoCreate: function()
27383     {
27384
27385         var cfg = {
27386             tag: 'div',
27387             cls: 'roo-location-picker'
27388         };
27389         
27390         return cfg
27391     },
27392     
27393     initEvents: function(ct, position)
27394     {       
27395         if(!this.el.getWidth() || this.isApplied()){
27396             return;
27397         }
27398         
27399         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27400         
27401         this.initial();
27402     },
27403     
27404     initial: function()
27405     {
27406         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27407             this.fireEvent('loadexception', this);
27408             return;
27409         }
27410         
27411         if(!this.mapTypeId){
27412             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
27413         }
27414         
27415         this.gMapContext = this.GMapContext();
27416         
27417         this.initOverlayView();
27418         
27419         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
27420         
27421         var _this = this;
27422                 
27423         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
27424             _this.setPosition(_this.gMapContext.marker.position);
27425         });
27426         
27427         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
27428             _this.fireEvent('mapClick', this, event);
27429             
27430         });
27431
27432         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
27433             _this.fireEvent('mapRightClick', this, event);
27434             
27435         });
27436         
27437         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27438             _this.fireEvent('markerClick', this, event);
27439             
27440         });
27441
27442         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27443             _this.fireEvent('markerRightClick', this, event);
27444             
27445         });
27446         
27447         this.setPosition(this.gMapContext.location);
27448         
27449         this.fireEvent('initial', this, this.gMapContext.location);
27450     },
27451     
27452     initOverlayView: function()
27453     {
27454         var _this = this;
27455         
27456         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27457             
27458             draw: function()
27459             {
27460                 _this.fireEvent('OverlayViewDraw', _this);
27461             },
27462             
27463             onAdd: function()
27464             {
27465                 _this.fireEvent('OverlayViewOnAdd', _this);
27466             },
27467             
27468             onRemove: function()
27469             {
27470                 _this.fireEvent('OverlayViewOnRemove', _this);
27471             },
27472             
27473             show: function(cpx)
27474             {
27475                 _this.fireEvent('OverlayViewShow', _this, cpx);
27476             },
27477             
27478             hide: function()
27479             {
27480                 _this.fireEvent('OverlayViewHide', _this);
27481             }
27482             
27483         });
27484     },
27485     
27486     fromLatLngToContainerPixel: function(event)
27487     {
27488         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27489     },
27490     
27491     isApplied: function() 
27492     {
27493         return this.getGmapContext() == false ? false : true;
27494     },
27495     
27496     getGmapContext: function() 
27497     {
27498         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27499     },
27500     
27501     GMapContext: function() 
27502     {
27503         var position = new google.maps.LatLng(this.latitude, this.longitude);
27504         
27505         var _map = new google.maps.Map(this.el.dom, {
27506             center: position,
27507             zoom: this.zoom,
27508             mapTypeId: this.mapTypeId,
27509             mapTypeControl: this.mapTypeControl,
27510             disableDoubleClickZoom: this.disableDoubleClickZoom,
27511             scrollwheel: this.scrollwheel,
27512             streetViewControl: this.streetViewControl,
27513             locationName: this.locationName,
27514             draggable: this.draggable,
27515             enableAutocomplete: this.enableAutocomplete,
27516             enableReverseGeocode: this.enableReverseGeocode
27517         });
27518         
27519         var _marker = new google.maps.Marker({
27520             position: position,
27521             map: _map,
27522             title: this.markerTitle,
27523             draggable: this.draggable
27524         });
27525         
27526         return {
27527             map: _map,
27528             marker: _marker,
27529             circle: null,
27530             location: position,
27531             radius: this.radius,
27532             locationName: this.locationName,
27533             addressComponents: {
27534                 formatted_address: null,
27535                 addressLine1: null,
27536                 addressLine2: null,
27537                 streetName: null,
27538                 streetNumber: null,
27539                 city: null,
27540                 district: null,
27541                 state: null,
27542                 stateOrProvince: null
27543             },
27544             settings: this,
27545             domContainer: this.el.dom,
27546             geodecoder: new google.maps.Geocoder()
27547         };
27548     },
27549     
27550     drawCircle: function(center, radius, options) 
27551     {
27552         if (this.gMapContext.circle != null) {
27553             this.gMapContext.circle.setMap(null);
27554         }
27555         if (radius > 0) {
27556             radius *= 1;
27557             options = Roo.apply({}, options, {
27558                 strokeColor: "#0000FF",
27559                 strokeOpacity: .35,
27560                 strokeWeight: 2,
27561                 fillColor: "#0000FF",
27562                 fillOpacity: .2
27563             });
27564             
27565             options.map = this.gMapContext.map;
27566             options.radius = radius;
27567             options.center = center;
27568             this.gMapContext.circle = new google.maps.Circle(options);
27569             return this.gMapContext.circle;
27570         }
27571         
27572         return null;
27573     },
27574     
27575     setPosition: function(location) 
27576     {
27577         this.gMapContext.location = location;
27578         this.gMapContext.marker.setPosition(location);
27579         this.gMapContext.map.panTo(location);
27580         this.drawCircle(location, this.gMapContext.radius, {});
27581         
27582         var _this = this;
27583         
27584         if (this.gMapContext.settings.enableReverseGeocode) {
27585             this.gMapContext.geodecoder.geocode({
27586                 latLng: this.gMapContext.location
27587             }, function(results, status) {
27588                 
27589                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27590                     _this.gMapContext.locationName = results[0].formatted_address;
27591                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27592                     
27593                     _this.fireEvent('positionchanged', this, location);
27594                 }
27595             });
27596             
27597             return;
27598         }
27599         
27600         this.fireEvent('positionchanged', this, location);
27601     },
27602     
27603     resize: function()
27604     {
27605         google.maps.event.trigger(this.gMapContext.map, "resize");
27606         
27607         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27608         
27609         this.fireEvent('resize', this);
27610     },
27611     
27612     setPositionByLatLng: function(latitude, longitude)
27613     {
27614         this.setPosition(new google.maps.LatLng(latitude, longitude));
27615     },
27616     
27617     getCurrentPosition: function() 
27618     {
27619         return {
27620             latitude: this.gMapContext.location.lat(),
27621             longitude: this.gMapContext.location.lng()
27622         };
27623     },
27624     
27625     getAddressName: function() 
27626     {
27627         return this.gMapContext.locationName;
27628     },
27629     
27630     getAddressComponents: function() 
27631     {
27632         return this.gMapContext.addressComponents;
27633     },
27634     
27635     address_component_from_google_geocode: function(address_components) 
27636     {
27637         var result = {};
27638         
27639         for (var i = 0; i < address_components.length; i++) {
27640             var component = address_components[i];
27641             if (component.types.indexOf("postal_code") >= 0) {
27642                 result.postalCode = component.short_name;
27643             } else if (component.types.indexOf("street_number") >= 0) {
27644                 result.streetNumber = component.short_name;
27645             } else if (component.types.indexOf("route") >= 0) {
27646                 result.streetName = component.short_name;
27647             } else if (component.types.indexOf("neighborhood") >= 0) {
27648                 result.city = component.short_name;
27649             } else if (component.types.indexOf("locality") >= 0) {
27650                 result.city = component.short_name;
27651             } else if (component.types.indexOf("sublocality") >= 0) {
27652                 result.district = component.short_name;
27653             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27654                 result.stateOrProvince = component.short_name;
27655             } else if (component.types.indexOf("country") >= 0) {
27656                 result.country = component.short_name;
27657             }
27658         }
27659         
27660         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27661         result.addressLine2 = "";
27662         return result;
27663     },
27664     
27665     setZoomLevel: function(zoom)
27666     {
27667         this.gMapContext.map.setZoom(zoom);
27668     },
27669     
27670     show: function()
27671     {
27672         if(!this.el){
27673             return;
27674         }
27675         
27676         this.el.show();
27677         
27678         this.resize();
27679         
27680         this.fireEvent('show', this);
27681     },
27682     
27683     hide: function()
27684     {
27685         if(!this.el){
27686             return;
27687         }
27688         
27689         this.el.hide();
27690         
27691         this.fireEvent('hide', this);
27692     }
27693     
27694 });
27695
27696 Roo.apply(Roo.bootstrap.LocationPicker, {
27697     
27698     OverlayView : function(map, options)
27699     {
27700         options = options || {};
27701         
27702         this.setMap(map);
27703     }
27704     
27705     
27706 });/**
27707  * @class Roo.bootstrap.Alert
27708  * @extends Roo.bootstrap.Component
27709  * Bootstrap Alert class - shows an alert area box
27710  * eg
27711  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27712   Enter a valid email address
27713 </div>
27714  * @licence LGPL
27715  * @cfg {String} title The title of alert
27716  * @cfg {String} html The content of alert
27717  * @cfg {String} weight (  success | info | warning | danger )
27718  * @cfg {String} faicon font-awesomeicon
27719  * 
27720  * @constructor
27721  * Create a new alert
27722  * @param {Object} config The config object
27723  */
27724
27725
27726 Roo.bootstrap.Alert = function(config){
27727     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27728     
27729 };
27730
27731 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27732     
27733     title: '',
27734     html: '',
27735     weight: false,
27736     faicon: false,
27737     
27738     getAutoCreate : function()
27739     {
27740         
27741         var cfg = {
27742             tag : 'div',
27743             cls : 'alert',
27744             cn : [
27745                 {
27746                     tag : 'i',
27747                     cls : 'roo-alert-icon'
27748                     
27749                 },
27750                 {
27751                     tag : 'b',
27752                     cls : 'roo-alert-title',
27753                     html : this.title
27754                 },
27755                 {
27756                     tag : 'span',
27757                     cls : 'roo-alert-text',
27758                     html : this.html
27759                 }
27760             ]
27761         };
27762         
27763         if(this.faicon){
27764             cfg.cn[0].cls += ' fa ' + this.faicon;
27765         }
27766         
27767         if(this.weight){
27768             cfg.cls += ' alert-' + this.weight;
27769         }
27770         
27771         return cfg;
27772     },
27773     
27774     initEvents: function() 
27775     {
27776         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27777     },
27778     
27779     setTitle : function(str)
27780     {
27781         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27782     },
27783     
27784     setText : function(str)
27785     {
27786         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27787     },
27788     
27789     setWeight : function(weight)
27790     {
27791         if(this.weight){
27792             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27793         }
27794         
27795         this.weight = weight;
27796         
27797         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27798     },
27799     
27800     setIcon : function(icon)
27801     {
27802         if(this.faicon){
27803             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27804         }
27805         
27806         this.faicon = icon;
27807         
27808         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27809     },
27810     
27811     hide: function() 
27812     {
27813         this.el.hide();   
27814     },
27815     
27816     show: function() 
27817     {  
27818         this.el.show();   
27819     }
27820     
27821 });
27822
27823  
27824 /*
27825 * Licence: LGPL
27826 */
27827
27828 /**
27829  * @class Roo.bootstrap.UploadCropbox
27830  * @extends Roo.bootstrap.Component
27831  * Bootstrap UploadCropbox class
27832  * @cfg {String} emptyText show when image has been loaded
27833  * @cfg {String} rotateNotify show when image too small to rotate
27834  * @cfg {Number} errorTimeout default 3000
27835  * @cfg {Number} minWidth default 300
27836  * @cfg {Number} minHeight default 300
27837  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27838  * @cfg {Boolean} isDocument (true|false) default false
27839  * @cfg {String} url action url
27840  * @cfg {String} paramName default 'imageUpload'
27841  * @cfg {String} method default POST
27842  * @cfg {Boolean} loadMask (true|false) default true
27843  * @cfg {Boolean} loadingText default 'Loading...'
27844  * 
27845  * @constructor
27846  * Create a new UploadCropbox
27847  * @param {Object} config The config object
27848  */
27849
27850 Roo.bootstrap.UploadCropbox = function(config){
27851     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27852     
27853     this.addEvents({
27854         /**
27855          * @event beforeselectfile
27856          * Fire before select file
27857          * @param {Roo.bootstrap.UploadCropbox} this
27858          */
27859         "beforeselectfile" : true,
27860         /**
27861          * @event initial
27862          * Fire after initEvent
27863          * @param {Roo.bootstrap.UploadCropbox} this
27864          */
27865         "initial" : true,
27866         /**
27867          * @event crop
27868          * Fire after initEvent
27869          * @param {Roo.bootstrap.UploadCropbox} this
27870          * @param {String} data
27871          */
27872         "crop" : true,
27873         /**
27874          * @event prepare
27875          * Fire when preparing the file data
27876          * @param {Roo.bootstrap.UploadCropbox} this
27877          * @param {Object} file
27878          */
27879         "prepare" : true,
27880         /**
27881          * @event exception
27882          * Fire when get exception
27883          * @param {Roo.bootstrap.UploadCropbox} this
27884          * @param {XMLHttpRequest} xhr
27885          */
27886         "exception" : true,
27887         /**
27888          * @event beforeloadcanvas
27889          * Fire before load the canvas
27890          * @param {Roo.bootstrap.UploadCropbox} this
27891          * @param {String} src
27892          */
27893         "beforeloadcanvas" : true,
27894         /**
27895          * @event trash
27896          * Fire when trash image
27897          * @param {Roo.bootstrap.UploadCropbox} this
27898          */
27899         "trash" : true,
27900         /**
27901          * @event download
27902          * Fire when download the image
27903          * @param {Roo.bootstrap.UploadCropbox} this
27904          */
27905         "download" : true,
27906         /**
27907          * @event footerbuttonclick
27908          * Fire when footerbuttonclick
27909          * @param {Roo.bootstrap.UploadCropbox} this
27910          * @param {String} type
27911          */
27912         "footerbuttonclick" : true,
27913         /**
27914          * @event resize
27915          * Fire when resize
27916          * @param {Roo.bootstrap.UploadCropbox} this
27917          */
27918         "resize" : true,
27919         /**
27920          * @event rotate
27921          * Fire when rotate the image
27922          * @param {Roo.bootstrap.UploadCropbox} this
27923          * @param {String} pos
27924          */
27925         "rotate" : true,
27926         /**
27927          * @event inspect
27928          * Fire when inspect the file
27929          * @param {Roo.bootstrap.UploadCropbox} this
27930          * @param {Object} file
27931          */
27932         "inspect" : true,
27933         /**
27934          * @event upload
27935          * Fire when xhr upload the file
27936          * @param {Roo.bootstrap.UploadCropbox} this
27937          * @param {Object} data
27938          */
27939         "upload" : true,
27940         /**
27941          * @event arrange
27942          * Fire when arrange the file data
27943          * @param {Roo.bootstrap.UploadCropbox} this
27944          * @param {Object} formData
27945          */
27946         "arrange" : true
27947     });
27948     
27949     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27950 };
27951
27952 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27953     
27954     emptyText : 'Click to upload image',
27955     rotateNotify : 'Image is too small to rotate',
27956     errorTimeout : 3000,
27957     scale : 0,
27958     baseScale : 1,
27959     rotate : 0,
27960     dragable : false,
27961     pinching : false,
27962     mouseX : 0,
27963     mouseY : 0,
27964     cropData : false,
27965     minWidth : 300,
27966     minHeight : 300,
27967     file : false,
27968     exif : {},
27969     baseRotate : 1,
27970     cropType : 'image/jpeg',
27971     buttons : false,
27972     canvasLoaded : false,
27973     isDocument : false,
27974     method : 'POST',
27975     paramName : 'imageUpload',
27976     loadMask : true,
27977     loadingText : 'Loading...',
27978     maskEl : false,
27979     
27980     getAutoCreate : function()
27981     {
27982         var cfg = {
27983             tag : 'div',
27984             cls : 'roo-upload-cropbox',
27985             cn : [
27986                 {
27987                     tag : 'input',
27988                     cls : 'roo-upload-cropbox-selector',
27989                     type : 'file'
27990                 },
27991                 {
27992                     tag : 'div',
27993                     cls : 'roo-upload-cropbox-body',
27994                     style : 'cursor:pointer',
27995                     cn : [
27996                         {
27997                             tag : 'div',
27998                             cls : 'roo-upload-cropbox-preview'
27999                         },
28000                         {
28001                             tag : 'div',
28002                             cls : 'roo-upload-cropbox-thumb'
28003                         },
28004                         {
28005                             tag : 'div',
28006                             cls : 'roo-upload-cropbox-empty-notify',
28007                             html : this.emptyText
28008                         },
28009                         {
28010                             tag : 'div',
28011                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
28012                             html : this.rotateNotify
28013                         }
28014                     ]
28015                 },
28016                 {
28017                     tag : 'div',
28018                     cls : 'roo-upload-cropbox-footer',
28019                     cn : {
28020                         tag : 'div',
28021                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
28022                         cn : []
28023                     }
28024                 }
28025             ]
28026         };
28027         
28028         return cfg;
28029     },
28030     
28031     onRender : function(ct, position)
28032     {
28033         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
28034         
28035         if (this.buttons.length) {
28036             
28037             Roo.each(this.buttons, function(bb) {
28038                 
28039                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
28040                 
28041                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
28042                 
28043             }, this);
28044         }
28045         
28046         if(this.loadMask){
28047             this.maskEl = this.el;
28048         }
28049     },
28050     
28051     initEvents : function()
28052     {
28053         this.urlAPI = (window.createObjectURL && window) || 
28054                                 (window.URL && URL.revokeObjectURL && URL) || 
28055                                 (window.webkitURL && webkitURL);
28056                         
28057         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
28058         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28059         
28060         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
28061         this.selectorEl.hide();
28062         
28063         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
28064         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28065         
28066         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
28067         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28068         this.thumbEl.hide();
28069         
28070         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
28071         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28072         
28073         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
28074         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28075         this.errorEl.hide();
28076         
28077         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
28078         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28079         this.footerEl.hide();
28080         
28081         this.setThumbBoxSize();
28082         
28083         this.bind();
28084         
28085         this.resize();
28086         
28087         this.fireEvent('initial', this);
28088     },
28089
28090     bind : function()
28091     {
28092         var _this = this;
28093         
28094         window.addEventListener("resize", function() { _this.resize(); } );
28095         
28096         this.bodyEl.on('click', this.beforeSelectFile, this);
28097         
28098         if(Roo.isTouch){
28099             this.bodyEl.on('touchstart', this.onTouchStart, this);
28100             this.bodyEl.on('touchmove', this.onTouchMove, this);
28101             this.bodyEl.on('touchend', this.onTouchEnd, this);
28102         }
28103         
28104         if(!Roo.isTouch){
28105             this.bodyEl.on('mousedown', this.onMouseDown, this);
28106             this.bodyEl.on('mousemove', this.onMouseMove, this);
28107             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
28108             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
28109             Roo.get(document).on('mouseup', this.onMouseUp, this);
28110         }
28111         
28112         this.selectorEl.on('change', this.onFileSelected, this);
28113     },
28114     
28115     reset : function()
28116     {    
28117         this.scale = 0;
28118         this.baseScale = 1;
28119         this.rotate = 0;
28120         this.baseRotate = 1;
28121         this.dragable = false;
28122         this.pinching = false;
28123         this.mouseX = 0;
28124         this.mouseY = 0;
28125         this.cropData = false;
28126         this.notifyEl.dom.innerHTML = this.emptyText;
28127         
28128         this.selectorEl.dom.value = '';
28129         
28130     },
28131     
28132     resize : function()
28133     {
28134         if(this.fireEvent('resize', this) != false){
28135             this.setThumbBoxPosition();
28136             this.setCanvasPosition();
28137         }
28138     },
28139     
28140     onFooterButtonClick : function(e, el, o, type)
28141     {
28142         switch (type) {
28143             case 'rotate-left' :
28144                 this.onRotateLeft(e);
28145                 break;
28146             case 'rotate-right' :
28147                 this.onRotateRight(e);
28148                 break;
28149             case 'picture' :
28150                 this.beforeSelectFile(e);
28151                 break;
28152             case 'trash' :
28153                 this.trash(e);
28154                 break;
28155             case 'crop' :
28156                 this.crop(e);
28157                 break;
28158             case 'download' :
28159                 this.download(e);
28160                 break;
28161             default :
28162                 break;
28163         }
28164         
28165         this.fireEvent('footerbuttonclick', this, type);
28166     },
28167     
28168     beforeSelectFile : function(e)
28169     {
28170         e.preventDefault();
28171         
28172         if(this.fireEvent('beforeselectfile', this) != false){
28173             this.selectorEl.dom.click();
28174         }
28175     },
28176     
28177     onFileSelected : function(e)
28178     {
28179         e.preventDefault();
28180         
28181         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28182             return;
28183         }
28184         
28185         var file = this.selectorEl.dom.files[0];
28186         
28187         if(this.fireEvent('inspect', this, file) != false){
28188             this.prepare(file);
28189         }
28190         
28191     },
28192     
28193     trash : function(e)
28194     {
28195         this.fireEvent('trash', this);
28196     },
28197     
28198     download : function(e)
28199     {
28200         this.fireEvent('download', this);
28201     },
28202     
28203     loadCanvas : function(src)
28204     {   
28205         if(this.fireEvent('beforeloadcanvas', this, src) != false){
28206             
28207             this.reset();
28208             
28209             this.imageEl = document.createElement('img');
28210             
28211             var _this = this;
28212             
28213             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
28214             
28215             this.imageEl.src = src;
28216         }
28217     },
28218     
28219     onLoadCanvas : function()
28220     {   
28221         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
28222         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
28223         
28224         this.bodyEl.un('click', this.beforeSelectFile, this);
28225         
28226         this.notifyEl.hide();
28227         this.thumbEl.show();
28228         this.footerEl.show();
28229         
28230         this.baseRotateLevel();
28231         
28232         if(this.isDocument){
28233             this.setThumbBoxSize();
28234         }
28235         
28236         this.setThumbBoxPosition();
28237         
28238         this.baseScaleLevel();
28239         
28240         this.draw();
28241         
28242         this.resize();
28243         
28244         this.canvasLoaded = true;
28245         
28246         if(this.loadMask){
28247             this.maskEl.unmask();
28248         }
28249         
28250     },
28251     
28252     setCanvasPosition : function()
28253     {   
28254         if(!this.canvasEl){
28255             return;
28256         }
28257         
28258         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
28259         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
28260         
28261         this.previewEl.setLeft(pw);
28262         this.previewEl.setTop(ph);
28263         
28264     },
28265     
28266     onMouseDown : function(e)
28267     {   
28268         e.stopEvent();
28269         
28270         this.dragable = true;
28271         this.pinching = false;
28272         
28273         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
28274             this.dragable = false;
28275             return;
28276         }
28277         
28278         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28279         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28280         
28281     },
28282     
28283     onMouseMove : function(e)
28284     {   
28285         e.stopEvent();
28286         
28287         if(!this.canvasLoaded){
28288             return;
28289         }
28290         
28291         if (!this.dragable){
28292             return;
28293         }
28294         
28295         var minX = Math.ceil(this.thumbEl.getLeft(true));
28296         var minY = Math.ceil(this.thumbEl.getTop(true));
28297         
28298         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
28299         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
28300         
28301         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28302         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28303         
28304         x = x - this.mouseX;
28305         y = y - this.mouseY;
28306         
28307         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
28308         var bgY = Math.ceil(y + this.previewEl.getTop(true));
28309         
28310         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
28311         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
28312         
28313         this.previewEl.setLeft(bgX);
28314         this.previewEl.setTop(bgY);
28315         
28316         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
28317         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
28318     },
28319     
28320     onMouseUp : function(e)
28321     {   
28322         e.stopEvent();
28323         
28324         this.dragable = false;
28325     },
28326     
28327     onMouseWheel : function(e)
28328     {   
28329         e.stopEvent();
28330         
28331         this.startScale = this.scale;
28332         
28333         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
28334         
28335         if(!this.zoomable()){
28336             this.scale = this.startScale;
28337             return;
28338         }
28339         
28340         this.draw();
28341         
28342         return;
28343     },
28344     
28345     zoomable : function()
28346     {
28347         var minScale = this.thumbEl.getWidth() / this.minWidth;
28348         
28349         if(this.minWidth < this.minHeight){
28350             minScale = this.thumbEl.getHeight() / this.minHeight;
28351         }
28352         
28353         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
28354         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
28355         
28356         if(
28357                 this.isDocument &&
28358                 (this.rotate == 0 || this.rotate == 180) && 
28359                 (
28360                     width > this.imageEl.OriginWidth || 
28361                     height > this.imageEl.OriginHeight ||
28362                     (width < this.minWidth && height < this.minHeight)
28363                 )
28364         ){
28365             return false;
28366         }
28367         
28368         if(
28369                 this.isDocument &&
28370                 (this.rotate == 90 || this.rotate == 270) && 
28371                 (
28372                     width > this.imageEl.OriginWidth || 
28373                     height > this.imageEl.OriginHeight ||
28374                     (width < this.minHeight && height < this.minWidth)
28375                 )
28376         ){
28377             return false;
28378         }
28379         
28380         if(
28381                 !this.isDocument &&
28382                 (this.rotate == 0 || this.rotate == 180) && 
28383                 (
28384                     width < this.minWidth || 
28385                     width > this.imageEl.OriginWidth || 
28386                     height < this.minHeight || 
28387                     height > this.imageEl.OriginHeight
28388                 )
28389         ){
28390             return false;
28391         }
28392         
28393         if(
28394                 !this.isDocument &&
28395                 (this.rotate == 90 || this.rotate == 270) && 
28396                 (
28397                     width < this.minHeight || 
28398                     width > this.imageEl.OriginWidth || 
28399                     height < this.minWidth || 
28400                     height > this.imageEl.OriginHeight
28401                 )
28402         ){
28403             return false;
28404         }
28405         
28406         return true;
28407         
28408     },
28409     
28410     onRotateLeft : function(e)
28411     {   
28412         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28413             
28414             var minScale = this.thumbEl.getWidth() / this.minWidth;
28415             
28416             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28417             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28418             
28419             this.startScale = this.scale;
28420             
28421             while (this.getScaleLevel() < minScale){
28422             
28423                 this.scale = this.scale + 1;
28424                 
28425                 if(!this.zoomable()){
28426                     break;
28427                 }
28428                 
28429                 if(
28430                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28431                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28432                 ){
28433                     continue;
28434                 }
28435                 
28436                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28437
28438                 this.draw();
28439                 
28440                 return;
28441             }
28442             
28443             this.scale = this.startScale;
28444             
28445             this.onRotateFail();
28446             
28447             return false;
28448         }
28449         
28450         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28451
28452         if(this.isDocument){
28453             this.setThumbBoxSize();
28454             this.setThumbBoxPosition();
28455             this.setCanvasPosition();
28456         }
28457         
28458         this.draw();
28459         
28460         this.fireEvent('rotate', this, 'left');
28461         
28462     },
28463     
28464     onRotateRight : function(e)
28465     {
28466         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28467             
28468             var minScale = this.thumbEl.getWidth() / this.minWidth;
28469         
28470             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28471             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28472             
28473             this.startScale = this.scale;
28474             
28475             while (this.getScaleLevel() < minScale){
28476             
28477                 this.scale = this.scale + 1;
28478                 
28479                 if(!this.zoomable()){
28480                     break;
28481                 }
28482                 
28483                 if(
28484                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28485                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28486                 ){
28487                     continue;
28488                 }
28489                 
28490                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28491
28492                 this.draw();
28493                 
28494                 return;
28495             }
28496             
28497             this.scale = this.startScale;
28498             
28499             this.onRotateFail();
28500             
28501             return false;
28502         }
28503         
28504         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28505
28506         if(this.isDocument){
28507             this.setThumbBoxSize();
28508             this.setThumbBoxPosition();
28509             this.setCanvasPosition();
28510         }
28511         
28512         this.draw();
28513         
28514         this.fireEvent('rotate', this, 'right');
28515     },
28516     
28517     onRotateFail : function()
28518     {
28519         this.errorEl.show(true);
28520         
28521         var _this = this;
28522         
28523         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28524     },
28525     
28526     draw : function()
28527     {
28528         this.previewEl.dom.innerHTML = '';
28529         
28530         var canvasEl = document.createElement("canvas");
28531         
28532         var contextEl = canvasEl.getContext("2d");
28533         
28534         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28535         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28536         var center = this.imageEl.OriginWidth / 2;
28537         
28538         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28539             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28540             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28541             center = this.imageEl.OriginHeight / 2;
28542         }
28543         
28544         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28545         
28546         contextEl.translate(center, center);
28547         contextEl.rotate(this.rotate * Math.PI / 180);
28548
28549         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28550         
28551         this.canvasEl = document.createElement("canvas");
28552         
28553         this.contextEl = this.canvasEl.getContext("2d");
28554         
28555         switch (this.rotate) {
28556             case 0 :
28557                 
28558                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28559                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28560                 
28561                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28562                 
28563                 break;
28564             case 90 : 
28565                 
28566                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28567                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28568                 
28569                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28570                     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);
28571                     break;
28572                 }
28573                 
28574                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28575                 
28576                 break;
28577             case 180 :
28578                 
28579                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28580                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28581                 
28582                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28583                     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);
28584                     break;
28585                 }
28586                 
28587                 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);
28588                 
28589                 break;
28590             case 270 :
28591                 
28592                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28593                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28594         
28595                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28596                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28597                     break;
28598                 }
28599                 
28600                 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);
28601                 
28602                 break;
28603             default : 
28604                 break;
28605         }
28606         
28607         this.previewEl.appendChild(this.canvasEl);
28608         
28609         this.setCanvasPosition();
28610     },
28611     
28612     crop : function()
28613     {
28614         if(!this.canvasLoaded){
28615             return;
28616         }
28617         
28618         var imageCanvas = document.createElement("canvas");
28619         
28620         var imageContext = imageCanvas.getContext("2d");
28621         
28622         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28623         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28624         
28625         var center = imageCanvas.width / 2;
28626         
28627         imageContext.translate(center, center);
28628         
28629         imageContext.rotate(this.rotate * Math.PI / 180);
28630         
28631         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28632         
28633         var canvas = document.createElement("canvas");
28634         
28635         var context = canvas.getContext("2d");
28636                 
28637         canvas.width = this.minWidth;
28638         canvas.height = this.minHeight;
28639
28640         switch (this.rotate) {
28641             case 0 :
28642                 
28643                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28644                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28645                 
28646                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28647                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28648                 
28649                 var targetWidth = this.minWidth - 2 * x;
28650                 var targetHeight = this.minHeight - 2 * y;
28651                 
28652                 var scale = 1;
28653                 
28654                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28655                     scale = targetWidth / width;
28656                 }
28657                 
28658                 if(x > 0 && y == 0){
28659                     scale = targetHeight / height;
28660                 }
28661                 
28662                 if(x > 0 && y > 0){
28663                     scale = targetWidth / width;
28664                     
28665                     if(width < height){
28666                         scale = targetHeight / height;
28667                     }
28668                 }
28669                 
28670                 context.scale(scale, scale);
28671                 
28672                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28673                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28674
28675                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28676                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28677
28678                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28679                 
28680                 break;
28681             case 90 : 
28682                 
28683                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28684                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28685                 
28686                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28687                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28688                 
28689                 var targetWidth = this.minWidth - 2 * x;
28690                 var targetHeight = this.minHeight - 2 * y;
28691                 
28692                 var scale = 1;
28693                 
28694                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28695                     scale = targetWidth / width;
28696                 }
28697                 
28698                 if(x > 0 && y == 0){
28699                     scale = targetHeight / height;
28700                 }
28701                 
28702                 if(x > 0 && y > 0){
28703                     scale = targetWidth / width;
28704                     
28705                     if(width < height){
28706                         scale = targetHeight / height;
28707                     }
28708                 }
28709                 
28710                 context.scale(scale, scale);
28711                 
28712                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28713                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28714
28715                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28716                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28717                 
28718                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28719                 
28720                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28721                 
28722                 break;
28723             case 180 :
28724                 
28725                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28726                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28727                 
28728                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28729                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28730                 
28731                 var targetWidth = this.minWidth - 2 * x;
28732                 var targetHeight = this.minHeight - 2 * y;
28733                 
28734                 var scale = 1;
28735                 
28736                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28737                     scale = targetWidth / width;
28738                 }
28739                 
28740                 if(x > 0 && y == 0){
28741                     scale = targetHeight / height;
28742                 }
28743                 
28744                 if(x > 0 && y > 0){
28745                     scale = targetWidth / width;
28746                     
28747                     if(width < height){
28748                         scale = targetHeight / height;
28749                     }
28750                 }
28751                 
28752                 context.scale(scale, scale);
28753                 
28754                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28755                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28756
28757                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28758                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28759
28760                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28761                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28762                 
28763                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28764                 
28765                 break;
28766             case 270 :
28767                 
28768                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28769                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28770                 
28771                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28772                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28773                 
28774                 var targetWidth = this.minWidth - 2 * x;
28775                 var targetHeight = this.minHeight - 2 * y;
28776                 
28777                 var scale = 1;
28778                 
28779                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28780                     scale = targetWidth / width;
28781                 }
28782                 
28783                 if(x > 0 && y == 0){
28784                     scale = targetHeight / height;
28785                 }
28786                 
28787                 if(x > 0 && y > 0){
28788                     scale = targetWidth / width;
28789                     
28790                     if(width < height){
28791                         scale = targetHeight / height;
28792                     }
28793                 }
28794                 
28795                 context.scale(scale, scale);
28796                 
28797                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28798                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28799
28800                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28801                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28802                 
28803                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28804                 
28805                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28806                 
28807                 break;
28808             default : 
28809                 break;
28810         }
28811         
28812         this.cropData = canvas.toDataURL(this.cropType);
28813         
28814         if(this.fireEvent('crop', this, this.cropData) !== false){
28815             this.process(this.file, this.cropData);
28816         }
28817         
28818         return;
28819         
28820     },
28821     
28822     setThumbBoxSize : function()
28823     {
28824         var width, height;
28825         
28826         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28827             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28828             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28829             
28830             this.minWidth = width;
28831             this.minHeight = height;
28832             
28833             if(this.rotate == 90 || this.rotate == 270){
28834                 this.minWidth = height;
28835                 this.minHeight = width;
28836             }
28837         }
28838         
28839         height = 300;
28840         width = Math.ceil(this.minWidth * height / this.minHeight);
28841         
28842         if(this.minWidth > this.minHeight){
28843             width = 300;
28844             height = Math.ceil(this.minHeight * width / this.minWidth);
28845         }
28846         
28847         this.thumbEl.setStyle({
28848             width : width + 'px',
28849             height : height + 'px'
28850         });
28851
28852         return;
28853             
28854     },
28855     
28856     setThumbBoxPosition : function()
28857     {
28858         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28859         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28860         
28861         this.thumbEl.setLeft(x);
28862         this.thumbEl.setTop(y);
28863         
28864     },
28865     
28866     baseRotateLevel : function()
28867     {
28868         this.baseRotate = 1;
28869         
28870         if(
28871                 typeof(this.exif) != 'undefined' &&
28872                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28873                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28874         ){
28875             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28876         }
28877         
28878         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28879         
28880     },
28881     
28882     baseScaleLevel : function()
28883     {
28884         var width, height;
28885         
28886         if(this.isDocument){
28887             
28888             if(this.baseRotate == 6 || this.baseRotate == 8){
28889             
28890                 height = this.thumbEl.getHeight();
28891                 this.baseScale = height / this.imageEl.OriginWidth;
28892
28893                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28894                     width = this.thumbEl.getWidth();
28895                     this.baseScale = width / this.imageEl.OriginHeight;
28896                 }
28897
28898                 return;
28899             }
28900
28901             height = this.thumbEl.getHeight();
28902             this.baseScale = height / this.imageEl.OriginHeight;
28903
28904             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28905                 width = this.thumbEl.getWidth();
28906                 this.baseScale = width / this.imageEl.OriginWidth;
28907             }
28908
28909             return;
28910         }
28911         
28912         if(this.baseRotate == 6 || this.baseRotate == 8){
28913             
28914             width = this.thumbEl.getHeight();
28915             this.baseScale = width / this.imageEl.OriginHeight;
28916             
28917             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28918                 height = this.thumbEl.getWidth();
28919                 this.baseScale = height / this.imageEl.OriginHeight;
28920             }
28921             
28922             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28923                 height = this.thumbEl.getWidth();
28924                 this.baseScale = height / this.imageEl.OriginHeight;
28925                 
28926                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28927                     width = this.thumbEl.getHeight();
28928                     this.baseScale = width / this.imageEl.OriginWidth;
28929                 }
28930             }
28931             
28932             return;
28933         }
28934         
28935         width = this.thumbEl.getWidth();
28936         this.baseScale = width / this.imageEl.OriginWidth;
28937         
28938         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28939             height = this.thumbEl.getHeight();
28940             this.baseScale = height / this.imageEl.OriginHeight;
28941         }
28942         
28943         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28944             
28945             height = this.thumbEl.getHeight();
28946             this.baseScale = height / this.imageEl.OriginHeight;
28947             
28948             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28949                 width = this.thumbEl.getWidth();
28950                 this.baseScale = width / this.imageEl.OriginWidth;
28951             }
28952             
28953         }
28954         
28955         return;
28956     },
28957     
28958     getScaleLevel : function()
28959     {
28960         return this.baseScale * Math.pow(1.1, this.scale);
28961     },
28962     
28963     onTouchStart : function(e)
28964     {
28965         if(!this.canvasLoaded){
28966             this.beforeSelectFile(e);
28967             return;
28968         }
28969         
28970         var touches = e.browserEvent.touches;
28971         
28972         if(!touches){
28973             return;
28974         }
28975         
28976         if(touches.length == 1){
28977             this.onMouseDown(e);
28978             return;
28979         }
28980         
28981         if(touches.length != 2){
28982             return;
28983         }
28984         
28985         var coords = [];
28986         
28987         for(var i = 0, finger; finger = touches[i]; i++){
28988             coords.push(finger.pageX, finger.pageY);
28989         }
28990         
28991         var x = Math.pow(coords[0] - coords[2], 2);
28992         var y = Math.pow(coords[1] - coords[3], 2);
28993         
28994         this.startDistance = Math.sqrt(x + y);
28995         
28996         this.startScale = this.scale;
28997         
28998         this.pinching = true;
28999         this.dragable = false;
29000         
29001     },
29002     
29003     onTouchMove : function(e)
29004     {
29005         if(!this.pinching && !this.dragable){
29006             return;
29007         }
29008         
29009         var touches = e.browserEvent.touches;
29010         
29011         if(!touches){
29012             return;
29013         }
29014         
29015         if(this.dragable){
29016             this.onMouseMove(e);
29017             return;
29018         }
29019         
29020         var coords = [];
29021         
29022         for(var i = 0, finger; finger = touches[i]; i++){
29023             coords.push(finger.pageX, finger.pageY);
29024         }
29025         
29026         var x = Math.pow(coords[0] - coords[2], 2);
29027         var y = Math.pow(coords[1] - coords[3], 2);
29028         
29029         this.endDistance = Math.sqrt(x + y);
29030         
29031         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
29032         
29033         if(!this.zoomable()){
29034             this.scale = this.startScale;
29035             return;
29036         }
29037         
29038         this.draw();
29039         
29040     },
29041     
29042     onTouchEnd : function(e)
29043     {
29044         this.pinching = false;
29045         this.dragable = false;
29046         
29047     },
29048     
29049     process : function(file, crop)
29050     {
29051         if(this.loadMask){
29052             this.maskEl.mask(this.loadingText);
29053         }
29054         
29055         this.xhr = new XMLHttpRequest();
29056         
29057         file.xhr = this.xhr;
29058
29059         this.xhr.open(this.method, this.url, true);
29060         
29061         var headers = {
29062             "Accept": "application/json",
29063             "Cache-Control": "no-cache",
29064             "X-Requested-With": "XMLHttpRequest"
29065         };
29066         
29067         for (var headerName in headers) {
29068             var headerValue = headers[headerName];
29069             if (headerValue) {
29070                 this.xhr.setRequestHeader(headerName, headerValue);
29071             }
29072         }
29073         
29074         var _this = this;
29075         
29076         this.xhr.onload = function()
29077         {
29078             _this.xhrOnLoad(_this.xhr);
29079         }
29080         
29081         this.xhr.onerror = function()
29082         {
29083             _this.xhrOnError(_this.xhr);
29084         }
29085         
29086         var formData = new FormData();
29087
29088         formData.append('returnHTML', 'NO');
29089         
29090         if(crop){
29091             formData.append('crop', crop);
29092         }
29093         
29094         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
29095             formData.append(this.paramName, file, file.name);
29096         }
29097         
29098         if(typeof(file.filename) != 'undefined'){
29099             formData.append('filename', file.filename);
29100         }
29101         
29102         if(typeof(file.mimetype) != 'undefined'){
29103             formData.append('mimetype', file.mimetype);
29104         }
29105         
29106         if(this.fireEvent('arrange', this, formData) != false){
29107             this.xhr.send(formData);
29108         };
29109     },
29110     
29111     xhrOnLoad : function(xhr)
29112     {
29113         if(this.loadMask){
29114             this.maskEl.unmask();
29115         }
29116         
29117         if (xhr.readyState !== 4) {
29118             this.fireEvent('exception', this, xhr);
29119             return;
29120         }
29121
29122         var response = Roo.decode(xhr.responseText);
29123         
29124         if(!response.success){
29125             this.fireEvent('exception', this, xhr);
29126             return;
29127         }
29128         
29129         var response = Roo.decode(xhr.responseText);
29130         
29131         this.fireEvent('upload', this, response);
29132         
29133     },
29134     
29135     xhrOnError : function()
29136     {
29137         if(this.loadMask){
29138             this.maskEl.unmask();
29139         }
29140         
29141         Roo.log('xhr on error');
29142         
29143         var response = Roo.decode(xhr.responseText);
29144           
29145         Roo.log(response);
29146         
29147     },
29148     
29149     prepare : function(file)
29150     {   
29151         if(this.loadMask){
29152             this.maskEl.mask(this.loadingText);
29153         }
29154         
29155         this.file = false;
29156         this.exif = {};
29157         
29158         if(typeof(file) === 'string'){
29159             this.loadCanvas(file);
29160             return;
29161         }
29162         
29163         if(!file || !this.urlAPI){
29164             return;
29165         }
29166         
29167         this.file = file;
29168         this.cropType = file.type;
29169         
29170         var _this = this;
29171         
29172         if(this.fireEvent('prepare', this, this.file) != false){
29173             
29174             var reader = new FileReader();
29175             
29176             reader.onload = function (e) {
29177                 if (e.target.error) {
29178                     Roo.log(e.target.error);
29179                     return;
29180                 }
29181                 
29182                 var buffer = e.target.result,
29183                     dataView = new DataView(buffer),
29184                     offset = 2,
29185                     maxOffset = dataView.byteLength - 4,
29186                     markerBytes,
29187                     markerLength;
29188                 
29189                 if (dataView.getUint16(0) === 0xffd8) {
29190                     while (offset < maxOffset) {
29191                         markerBytes = dataView.getUint16(offset);
29192                         
29193                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
29194                             markerLength = dataView.getUint16(offset + 2) + 2;
29195                             if (offset + markerLength > dataView.byteLength) {
29196                                 Roo.log('Invalid meta data: Invalid segment size.');
29197                                 break;
29198                             }
29199                             
29200                             if(markerBytes == 0xffe1){
29201                                 _this.parseExifData(
29202                                     dataView,
29203                                     offset,
29204                                     markerLength
29205                                 );
29206                             }
29207                             
29208                             offset += markerLength;
29209                             
29210                             continue;
29211                         }
29212                         
29213                         break;
29214                     }
29215                     
29216                 }
29217                 
29218                 var url = _this.urlAPI.createObjectURL(_this.file);
29219                 
29220                 _this.loadCanvas(url);
29221                 
29222                 return;
29223             }
29224             
29225             reader.readAsArrayBuffer(this.file);
29226             
29227         }
29228         
29229     },
29230     
29231     parseExifData : function(dataView, offset, length)
29232     {
29233         var tiffOffset = offset + 10,
29234             littleEndian,
29235             dirOffset;
29236     
29237         if (dataView.getUint32(offset + 4) !== 0x45786966) {
29238             // No Exif data, might be XMP data instead
29239             return;
29240         }
29241         
29242         // Check for the ASCII code for "Exif" (0x45786966):
29243         if (dataView.getUint32(offset + 4) !== 0x45786966) {
29244             // No Exif data, might be XMP data instead
29245             return;
29246         }
29247         if (tiffOffset + 8 > dataView.byteLength) {
29248             Roo.log('Invalid Exif data: Invalid segment size.');
29249             return;
29250         }
29251         // Check for the two null bytes:
29252         if (dataView.getUint16(offset + 8) !== 0x0000) {
29253             Roo.log('Invalid Exif data: Missing byte alignment offset.');
29254             return;
29255         }
29256         // Check the byte alignment:
29257         switch (dataView.getUint16(tiffOffset)) {
29258         case 0x4949:
29259             littleEndian = true;
29260             break;
29261         case 0x4D4D:
29262             littleEndian = false;
29263             break;
29264         default:
29265             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
29266             return;
29267         }
29268         // Check for the TIFF tag marker (0x002A):
29269         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
29270             Roo.log('Invalid Exif data: Missing TIFF marker.');
29271             return;
29272         }
29273         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
29274         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
29275         
29276         this.parseExifTags(
29277             dataView,
29278             tiffOffset,
29279             tiffOffset + dirOffset,
29280             littleEndian
29281         );
29282     },
29283     
29284     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
29285     {
29286         var tagsNumber,
29287             dirEndOffset,
29288             i;
29289         if (dirOffset + 6 > dataView.byteLength) {
29290             Roo.log('Invalid Exif data: Invalid directory offset.');
29291             return;
29292         }
29293         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
29294         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
29295         if (dirEndOffset + 4 > dataView.byteLength) {
29296             Roo.log('Invalid Exif data: Invalid directory size.');
29297             return;
29298         }
29299         for (i = 0; i < tagsNumber; i += 1) {
29300             this.parseExifTag(
29301                 dataView,
29302                 tiffOffset,
29303                 dirOffset + 2 + 12 * i, // tag offset
29304                 littleEndian
29305             );
29306         }
29307         // Return the offset to the next directory:
29308         return dataView.getUint32(dirEndOffset, littleEndian);
29309     },
29310     
29311     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
29312     {
29313         var tag = dataView.getUint16(offset, littleEndian);
29314         
29315         this.exif[tag] = this.getExifValue(
29316             dataView,
29317             tiffOffset,
29318             offset,
29319             dataView.getUint16(offset + 2, littleEndian), // tag type
29320             dataView.getUint32(offset + 4, littleEndian), // tag length
29321             littleEndian
29322         );
29323     },
29324     
29325     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
29326     {
29327         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
29328             tagSize,
29329             dataOffset,
29330             values,
29331             i,
29332             str,
29333             c;
29334     
29335         if (!tagType) {
29336             Roo.log('Invalid Exif data: Invalid tag type.');
29337             return;
29338         }
29339         
29340         tagSize = tagType.size * length;
29341         // Determine if the value is contained in the dataOffset bytes,
29342         // or if the value at the dataOffset is a pointer to the actual data:
29343         dataOffset = tagSize > 4 ?
29344                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
29345         if (dataOffset + tagSize > dataView.byteLength) {
29346             Roo.log('Invalid Exif data: Invalid data offset.');
29347             return;
29348         }
29349         if (length === 1) {
29350             return tagType.getValue(dataView, dataOffset, littleEndian);
29351         }
29352         values = [];
29353         for (i = 0; i < length; i += 1) {
29354             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
29355         }
29356         
29357         if (tagType.ascii) {
29358             str = '';
29359             // Concatenate the chars:
29360             for (i = 0; i < values.length; i += 1) {
29361                 c = values[i];
29362                 // Ignore the terminating NULL byte(s):
29363                 if (c === '\u0000') {
29364                     break;
29365                 }
29366                 str += c;
29367             }
29368             return str;
29369         }
29370         return values;
29371     }
29372     
29373 });
29374
29375 Roo.apply(Roo.bootstrap.UploadCropbox, {
29376     tags : {
29377         'Orientation': 0x0112
29378     },
29379     
29380     Orientation: {
29381             1: 0, //'top-left',
29382 //            2: 'top-right',
29383             3: 180, //'bottom-right',
29384 //            4: 'bottom-left',
29385 //            5: 'left-top',
29386             6: 90, //'right-top',
29387 //            7: 'right-bottom',
29388             8: 270 //'left-bottom'
29389     },
29390     
29391     exifTagTypes : {
29392         // byte, 8-bit unsigned int:
29393         1: {
29394             getValue: function (dataView, dataOffset) {
29395                 return dataView.getUint8(dataOffset);
29396             },
29397             size: 1
29398         },
29399         // ascii, 8-bit byte:
29400         2: {
29401             getValue: function (dataView, dataOffset) {
29402                 return String.fromCharCode(dataView.getUint8(dataOffset));
29403             },
29404             size: 1,
29405             ascii: true
29406         },
29407         // short, 16 bit int:
29408         3: {
29409             getValue: function (dataView, dataOffset, littleEndian) {
29410                 return dataView.getUint16(dataOffset, littleEndian);
29411             },
29412             size: 2
29413         },
29414         // long, 32 bit int:
29415         4: {
29416             getValue: function (dataView, dataOffset, littleEndian) {
29417                 return dataView.getUint32(dataOffset, littleEndian);
29418             },
29419             size: 4
29420         },
29421         // rational = two long values, first is numerator, second is denominator:
29422         5: {
29423             getValue: function (dataView, dataOffset, littleEndian) {
29424                 return dataView.getUint32(dataOffset, littleEndian) /
29425                     dataView.getUint32(dataOffset + 4, littleEndian);
29426             },
29427             size: 8
29428         },
29429         // slong, 32 bit signed int:
29430         9: {
29431             getValue: function (dataView, dataOffset, littleEndian) {
29432                 return dataView.getInt32(dataOffset, littleEndian);
29433             },
29434             size: 4
29435         },
29436         // srational, two slongs, first is numerator, second is denominator:
29437         10: {
29438             getValue: function (dataView, dataOffset, littleEndian) {
29439                 return dataView.getInt32(dataOffset, littleEndian) /
29440                     dataView.getInt32(dataOffset + 4, littleEndian);
29441             },
29442             size: 8
29443         }
29444     },
29445     
29446     footer : {
29447         STANDARD : [
29448             {
29449                 tag : 'div',
29450                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29451                 action : 'rotate-left',
29452                 cn : [
29453                     {
29454                         tag : 'button',
29455                         cls : 'btn btn-default',
29456                         html : '<i class="fa fa-undo"></i>'
29457                     }
29458                 ]
29459             },
29460             {
29461                 tag : 'div',
29462                 cls : 'btn-group roo-upload-cropbox-picture',
29463                 action : 'picture',
29464                 cn : [
29465                     {
29466                         tag : 'button',
29467                         cls : 'btn btn-default',
29468                         html : '<i class="fa fa-picture-o"></i>'
29469                     }
29470                 ]
29471             },
29472             {
29473                 tag : 'div',
29474                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29475                 action : 'rotate-right',
29476                 cn : [
29477                     {
29478                         tag : 'button',
29479                         cls : 'btn btn-default',
29480                         html : '<i class="fa fa-repeat"></i>'
29481                     }
29482                 ]
29483             }
29484         ],
29485         DOCUMENT : [
29486             {
29487                 tag : 'div',
29488                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29489                 action : 'rotate-left',
29490                 cn : [
29491                     {
29492                         tag : 'button',
29493                         cls : 'btn btn-default',
29494                         html : '<i class="fa fa-undo"></i>'
29495                     }
29496                 ]
29497             },
29498             {
29499                 tag : 'div',
29500                 cls : 'btn-group roo-upload-cropbox-download',
29501                 action : 'download',
29502                 cn : [
29503                     {
29504                         tag : 'button',
29505                         cls : 'btn btn-default',
29506                         html : '<i class="fa fa-download"></i>'
29507                     }
29508                 ]
29509             },
29510             {
29511                 tag : 'div',
29512                 cls : 'btn-group roo-upload-cropbox-crop',
29513                 action : 'crop',
29514                 cn : [
29515                     {
29516                         tag : 'button',
29517                         cls : 'btn btn-default',
29518                         html : '<i class="fa fa-crop"></i>'
29519                     }
29520                 ]
29521             },
29522             {
29523                 tag : 'div',
29524                 cls : 'btn-group roo-upload-cropbox-trash',
29525                 action : 'trash',
29526                 cn : [
29527                     {
29528                         tag : 'button',
29529                         cls : 'btn btn-default',
29530                         html : '<i class="fa fa-trash"></i>'
29531                     }
29532                 ]
29533             },
29534             {
29535                 tag : 'div',
29536                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29537                 action : 'rotate-right',
29538                 cn : [
29539                     {
29540                         tag : 'button',
29541                         cls : 'btn btn-default',
29542                         html : '<i class="fa fa-repeat"></i>'
29543                     }
29544                 ]
29545             }
29546         ],
29547         ROTATOR : [
29548             {
29549                 tag : 'div',
29550                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29551                 action : 'rotate-left',
29552                 cn : [
29553                     {
29554                         tag : 'button',
29555                         cls : 'btn btn-default',
29556                         html : '<i class="fa fa-undo"></i>'
29557                     }
29558                 ]
29559             },
29560             {
29561                 tag : 'div',
29562                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29563                 action : 'rotate-right',
29564                 cn : [
29565                     {
29566                         tag : 'button',
29567                         cls : 'btn btn-default',
29568                         html : '<i class="fa fa-repeat"></i>'
29569                     }
29570                 ]
29571             }
29572         ]
29573     }
29574 });
29575
29576 /*
29577 * Licence: LGPL
29578 */
29579
29580 /**
29581  * @class Roo.bootstrap.DocumentManager
29582  * @extends Roo.bootstrap.Component
29583  * Bootstrap DocumentManager class
29584  * @cfg {String} paramName default 'imageUpload'
29585  * @cfg {String} toolTipName default 'filename'
29586  * @cfg {String} method default POST
29587  * @cfg {String} url action url
29588  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29589  * @cfg {Boolean} multiple multiple upload default true
29590  * @cfg {Number} thumbSize default 300
29591  * @cfg {String} fieldLabel
29592  * @cfg {Number} labelWidth default 4
29593  * @cfg {String} labelAlign (left|top) default left
29594  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29595 * @cfg {Number} labellg set the width of label (1-12)
29596  * @cfg {Number} labelmd set the width of label (1-12)
29597  * @cfg {Number} labelsm set the width of label (1-12)
29598  * @cfg {Number} labelxs set the width of label (1-12)
29599  * 
29600  * @constructor
29601  * Create a new DocumentManager
29602  * @param {Object} config The config object
29603  */
29604
29605 Roo.bootstrap.DocumentManager = function(config){
29606     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29607     
29608     this.files = [];
29609     this.delegates = [];
29610     
29611     this.addEvents({
29612         /**
29613          * @event initial
29614          * Fire when initial the DocumentManager
29615          * @param {Roo.bootstrap.DocumentManager} this
29616          */
29617         "initial" : true,
29618         /**
29619          * @event inspect
29620          * inspect selected file
29621          * @param {Roo.bootstrap.DocumentManager} this
29622          * @param {File} file
29623          */
29624         "inspect" : true,
29625         /**
29626          * @event exception
29627          * Fire when xhr load exception
29628          * @param {Roo.bootstrap.DocumentManager} this
29629          * @param {XMLHttpRequest} xhr
29630          */
29631         "exception" : true,
29632         /**
29633          * @event afterupload
29634          * Fire when xhr load exception
29635          * @param {Roo.bootstrap.DocumentManager} this
29636          * @param {XMLHttpRequest} xhr
29637          */
29638         "afterupload" : true,
29639         /**
29640          * @event prepare
29641          * prepare the form data
29642          * @param {Roo.bootstrap.DocumentManager} this
29643          * @param {Object} formData
29644          */
29645         "prepare" : true,
29646         /**
29647          * @event remove
29648          * Fire when remove the file
29649          * @param {Roo.bootstrap.DocumentManager} this
29650          * @param {Object} file
29651          */
29652         "remove" : true,
29653         /**
29654          * @event refresh
29655          * Fire after refresh the file
29656          * @param {Roo.bootstrap.DocumentManager} this
29657          */
29658         "refresh" : true,
29659         /**
29660          * @event click
29661          * Fire after click the image
29662          * @param {Roo.bootstrap.DocumentManager} this
29663          * @param {Object} file
29664          */
29665         "click" : true,
29666         /**
29667          * @event edit
29668          * Fire when upload a image and editable set to true
29669          * @param {Roo.bootstrap.DocumentManager} this
29670          * @param {Object} file
29671          */
29672         "edit" : true,
29673         /**
29674          * @event beforeselectfile
29675          * Fire before select file
29676          * @param {Roo.bootstrap.DocumentManager} this
29677          */
29678         "beforeselectfile" : true,
29679         /**
29680          * @event process
29681          * Fire before process file
29682          * @param {Roo.bootstrap.DocumentManager} this
29683          * @param {Object} file
29684          */
29685         "process" : true,
29686         /**
29687          * @event previewrendered
29688          * Fire when preview rendered
29689          * @param {Roo.bootstrap.DocumentManager} this
29690          * @param {Object} file
29691          */
29692         "previewrendered" : true,
29693         /**
29694          */
29695         "previewResize" : true
29696         
29697     });
29698 };
29699
29700 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29701     
29702     boxes : 0,
29703     inputName : '',
29704     thumbSize : 300,
29705     multiple : true,
29706     files : false,
29707     method : 'POST',
29708     url : '',
29709     paramName : 'imageUpload',
29710     toolTipName : 'filename',
29711     fieldLabel : '',
29712     labelWidth : 4,
29713     labelAlign : 'left',
29714     editable : true,
29715     delegates : false,
29716     xhr : false, 
29717     
29718     labellg : 0,
29719     labelmd : 0,
29720     labelsm : 0,
29721     labelxs : 0,
29722     
29723     getAutoCreate : function()
29724     {   
29725         var managerWidget = {
29726             tag : 'div',
29727             cls : 'roo-document-manager',
29728             cn : [
29729                 {
29730                     tag : 'input',
29731                     cls : 'roo-document-manager-selector',
29732                     type : 'file'
29733                 },
29734                 {
29735                     tag : 'div',
29736                     cls : 'roo-document-manager-uploader',
29737                     cn : [
29738                         {
29739                             tag : 'div',
29740                             cls : 'roo-document-manager-upload-btn',
29741                             html : '<i class="fa fa-plus"></i>'
29742                         }
29743                     ]
29744                     
29745                 }
29746             ]
29747         };
29748         
29749         var content = [
29750             {
29751                 tag : 'div',
29752                 cls : 'column col-md-12',
29753                 cn : managerWidget
29754             }
29755         ];
29756         
29757         if(this.fieldLabel.length){
29758             
29759             content = [
29760                 {
29761                     tag : 'div',
29762                     cls : 'column col-md-12',
29763                     html : this.fieldLabel
29764                 },
29765                 {
29766                     tag : 'div',
29767                     cls : 'column col-md-12',
29768                     cn : managerWidget
29769                 }
29770             ];
29771
29772             if(this.labelAlign == 'left'){
29773                 content = [
29774                     {
29775                         tag : 'div',
29776                         cls : 'column',
29777                         html : this.fieldLabel
29778                     },
29779                     {
29780                         tag : 'div',
29781                         cls : 'column',
29782                         cn : managerWidget
29783                     }
29784                 ];
29785                 
29786                 if(this.labelWidth > 12){
29787                     content[0].style = "width: " + this.labelWidth + 'px';
29788                 }
29789
29790                 if(this.labelWidth < 13 && this.labelmd == 0){
29791                     this.labelmd = this.labelWidth;
29792                 }
29793
29794                 if(this.labellg > 0){
29795                     content[0].cls += ' col-lg-' + this.labellg;
29796                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29797                 }
29798
29799                 if(this.labelmd > 0){
29800                     content[0].cls += ' col-md-' + this.labelmd;
29801                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29802                 }
29803
29804                 if(this.labelsm > 0){
29805                     content[0].cls += ' col-sm-' + this.labelsm;
29806                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29807                 }
29808
29809                 if(this.labelxs > 0){
29810                     content[0].cls += ' col-xs-' + this.labelxs;
29811                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29812                 }
29813                 
29814             }
29815         }
29816         
29817         var cfg = {
29818             tag : 'div',
29819             cls : 'row clearfix',
29820             cn : content
29821         };
29822         
29823         return cfg;
29824         
29825     },
29826     
29827     initEvents : function()
29828     {
29829         this.managerEl = this.el.select('.roo-document-manager', true).first();
29830         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29831         
29832         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29833         this.selectorEl.hide();
29834         
29835         if(this.multiple){
29836             this.selectorEl.attr('multiple', 'multiple');
29837         }
29838         
29839         this.selectorEl.on('change', this.onFileSelected, this);
29840         
29841         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29842         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29843         
29844         this.uploader.on('click', this.onUploaderClick, this);
29845         
29846         this.renderProgressDialog();
29847         
29848         var _this = this;
29849         
29850         window.addEventListener("resize", function() { _this.refresh(); } );
29851         
29852         this.fireEvent('initial', this);
29853     },
29854     
29855     renderProgressDialog : function()
29856     {
29857         var _this = this;
29858         
29859         this.progressDialog = new Roo.bootstrap.Modal({
29860             cls : 'roo-document-manager-progress-dialog',
29861             allow_close : false,
29862             animate : false,
29863             title : '',
29864             buttons : [
29865                 {
29866                     name  :'cancel',
29867                     weight : 'danger',
29868                     html : 'Cancel'
29869                 }
29870             ], 
29871             listeners : { 
29872                 btnclick : function() {
29873                     _this.uploadCancel();
29874                     this.hide();
29875                 }
29876             }
29877         });
29878          
29879         this.progressDialog.render(Roo.get(document.body));
29880          
29881         this.progress = new Roo.bootstrap.Progress({
29882             cls : 'roo-document-manager-progress',
29883             active : true,
29884             striped : true
29885         });
29886         
29887         this.progress.render(this.progressDialog.getChildContainer());
29888         
29889         this.progressBar = new Roo.bootstrap.ProgressBar({
29890             cls : 'roo-document-manager-progress-bar',
29891             aria_valuenow : 0,
29892             aria_valuemin : 0,
29893             aria_valuemax : 12,
29894             panel : 'success'
29895         });
29896         
29897         this.progressBar.render(this.progress.getChildContainer());
29898     },
29899     
29900     onUploaderClick : function(e)
29901     {
29902         e.preventDefault();
29903      
29904         if(this.fireEvent('beforeselectfile', this) != false){
29905             this.selectorEl.dom.click();
29906         }
29907         
29908     },
29909     
29910     onFileSelected : function(e)
29911     {
29912         e.preventDefault();
29913         
29914         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29915             return;
29916         }
29917         
29918         Roo.each(this.selectorEl.dom.files, function(file){
29919             if(this.fireEvent('inspect', this, file) != false){
29920                 this.files.push(file);
29921             }
29922         }, this);
29923         
29924         this.queue();
29925         
29926     },
29927     
29928     queue : function()
29929     {
29930         this.selectorEl.dom.value = '';
29931         
29932         if(!this.files || !this.files.length){
29933             return;
29934         }
29935         
29936         if(this.boxes > 0 && this.files.length > this.boxes){
29937             this.files = this.files.slice(0, this.boxes);
29938         }
29939         
29940         this.uploader.show();
29941         
29942         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29943             this.uploader.hide();
29944         }
29945         
29946         var _this = this;
29947         
29948         var files = [];
29949         
29950         var docs = [];
29951         
29952         Roo.each(this.files, function(file){
29953             
29954             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29955                 var f = this.renderPreview(file);
29956                 files.push(f);
29957                 return;
29958             }
29959             
29960             if(file.type.indexOf('image') != -1){
29961                 this.delegates.push(
29962                     (function(){
29963                         _this.process(file);
29964                     }).createDelegate(this)
29965                 );
29966         
29967                 return;
29968             }
29969             
29970             docs.push(
29971                 (function(){
29972                     _this.process(file);
29973                 }).createDelegate(this)
29974             );
29975             
29976         }, this);
29977         
29978         this.files = files;
29979         
29980         this.delegates = this.delegates.concat(docs);
29981         
29982         if(!this.delegates.length){
29983             this.refresh();
29984             return;
29985         }
29986         
29987         this.progressBar.aria_valuemax = this.delegates.length;
29988         
29989         this.arrange();
29990         
29991         return;
29992     },
29993     
29994     arrange : function()
29995     {
29996         if(!this.delegates.length){
29997             this.progressDialog.hide();
29998             this.refresh();
29999             return;
30000         }
30001         
30002         var delegate = this.delegates.shift();
30003         
30004         this.progressDialog.show();
30005         
30006         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
30007         
30008         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
30009         
30010         delegate();
30011     },
30012     
30013     refresh : function()
30014     {
30015         this.uploader.show();
30016         
30017         if(this.boxes > 0 && this.files.length > this.boxes - 1){
30018             this.uploader.hide();
30019         }
30020         
30021         Roo.isTouch ? this.closable(false) : this.closable(true);
30022         
30023         this.fireEvent('refresh', this);
30024     },
30025     
30026     onRemove : function(e, el, o)
30027     {
30028         e.preventDefault();
30029         
30030         this.fireEvent('remove', this, o);
30031         
30032     },
30033     
30034     remove : function(o)
30035     {
30036         var files = [];
30037         
30038         Roo.each(this.files, function(file){
30039             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
30040                 files.push(file);
30041                 return;
30042             }
30043
30044             o.target.remove();
30045
30046         }, this);
30047         
30048         this.files = files;
30049         
30050         this.refresh();
30051     },
30052     
30053     clear : function()
30054     {
30055         Roo.each(this.files, function(file){
30056             if(!file.target){
30057                 return;
30058             }
30059             
30060             file.target.remove();
30061
30062         }, this);
30063         
30064         this.files = [];
30065         
30066         this.refresh();
30067     },
30068     
30069     onClick : function(e, el, o)
30070     {
30071         e.preventDefault();
30072         
30073         this.fireEvent('click', this, o);
30074         
30075     },
30076     
30077     closable : function(closable)
30078     {
30079         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
30080             
30081             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30082             
30083             if(closable){
30084                 el.show();
30085                 return;
30086             }
30087             
30088             el.hide();
30089             
30090         }, this);
30091     },
30092     
30093     xhrOnLoad : function(xhr)
30094     {
30095         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30096             el.remove();
30097         }, this);
30098         
30099         if (xhr.readyState !== 4) {
30100             this.arrange();
30101             this.fireEvent('exception', this, xhr);
30102             return;
30103         }
30104
30105         var response = Roo.decode(xhr.responseText);
30106         
30107         if(!response.success){
30108             this.arrange();
30109             this.fireEvent('exception', this, xhr);
30110             return;
30111         }
30112         
30113         var file = this.renderPreview(response.data);
30114         
30115         this.files.push(file);
30116         
30117         this.arrange();
30118         
30119         this.fireEvent('afterupload', this, xhr);
30120         
30121     },
30122     
30123     xhrOnError : function(xhr)
30124     {
30125         Roo.log('xhr on error');
30126         
30127         var response = Roo.decode(xhr.responseText);
30128           
30129         Roo.log(response);
30130         
30131         this.arrange();
30132     },
30133     
30134     process : function(file)
30135     {
30136         if(this.fireEvent('process', this, file) !== false){
30137             if(this.editable && file.type.indexOf('image') != -1){
30138                 this.fireEvent('edit', this, file);
30139                 return;
30140             }
30141
30142             this.uploadStart(file, false);
30143
30144             return;
30145         }
30146         
30147     },
30148     
30149     uploadStart : function(file, crop)
30150     {
30151         this.xhr = new XMLHttpRequest();
30152         
30153         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
30154             this.arrange();
30155             return;
30156         }
30157         
30158         file.xhr = this.xhr;
30159             
30160         this.managerEl.createChild({
30161             tag : 'div',
30162             cls : 'roo-document-manager-loading',
30163             cn : [
30164                 {
30165                     tag : 'div',
30166                     tooltip : file.name,
30167                     cls : 'roo-document-manager-thumb',
30168                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30169                 }
30170             ]
30171
30172         });
30173
30174         this.xhr.open(this.method, this.url, true);
30175         
30176         var headers = {
30177             "Accept": "application/json",
30178             "Cache-Control": "no-cache",
30179             "X-Requested-With": "XMLHttpRequest"
30180         };
30181         
30182         for (var headerName in headers) {
30183             var headerValue = headers[headerName];
30184             if (headerValue) {
30185                 this.xhr.setRequestHeader(headerName, headerValue);
30186             }
30187         }
30188         
30189         var _this = this;
30190         
30191         this.xhr.onload = function()
30192         {
30193             _this.xhrOnLoad(_this.xhr);
30194         }
30195         
30196         this.xhr.onerror = function()
30197         {
30198             _this.xhrOnError(_this.xhr);
30199         }
30200         
30201         var formData = new FormData();
30202
30203         formData.append('returnHTML', 'NO');
30204         
30205         if(crop){
30206             formData.append('crop', crop);
30207         }
30208         
30209         formData.append(this.paramName, file, file.name);
30210         
30211         var options = {
30212             file : file, 
30213             manually : false
30214         };
30215         
30216         if(this.fireEvent('prepare', this, formData, options) != false){
30217             
30218             if(options.manually){
30219                 return;
30220             }
30221             
30222             this.xhr.send(formData);
30223             return;
30224         };
30225         
30226         this.uploadCancel();
30227     },
30228     
30229     uploadCancel : function()
30230     {
30231         if (this.xhr) {
30232             this.xhr.abort();
30233         }
30234         
30235         this.delegates = [];
30236         
30237         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
30238             el.remove();
30239         }, this);
30240         
30241         this.arrange();
30242     },
30243     
30244     renderPreview : function(file)
30245     {
30246         if(typeof(file.target) != 'undefined' && file.target){
30247             return file;
30248         }
30249         
30250         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
30251         
30252         var previewEl = this.managerEl.createChild({
30253             tag : 'div',
30254             cls : 'roo-document-manager-preview',
30255             cn : [
30256                 {
30257                     tag : 'div',
30258                     tooltip : file[this.toolTipName],
30259                     cls : 'roo-document-manager-thumb',
30260                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
30261                 },
30262                 {
30263                     tag : 'button',
30264                     cls : 'close',
30265                     html : '<i class="fa fa-times-circle"></i>'
30266                 }
30267             ]
30268         });
30269
30270         var close = previewEl.select('button.close', true).first();
30271
30272         close.on('click', this.onRemove, this, file);
30273
30274         file.target = previewEl;
30275
30276         var image = previewEl.select('img', true).first();
30277         
30278         var _this = this;
30279         
30280         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
30281         
30282         image.on('click', this.onClick, this, file);
30283         
30284         this.fireEvent('previewrendered', this, file);
30285         
30286         return file;
30287         
30288     },
30289     
30290     onPreviewLoad : function(file, image)
30291     {
30292         if(typeof(file.target) == 'undefined' || !file.target){
30293             return;
30294         }
30295         
30296         var width = image.dom.naturalWidth || image.dom.width;
30297         var height = image.dom.naturalHeight || image.dom.height;
30298         
30299         if(!this.previewResize) {
30300             return;
30301         }
30302         
30303         if(width > height){
30304             file.target.addClass('wide');
30305             return;
30306         }
30307         
30308         file.target.addClass('tall');
30309         return;
30310         
30311     },
30312     
30313     uploadFromSource : function(file, crop)
30314     {
30315         this.xhr = new XMLHttpRequest();
30316         
30317         this.managerEl.createChild({
30318             tag : 'div',
30319             cls : 'roo-document-manager-loading',
30320             cn : [
30321                 {
30322                     tag : 'div',
30323                     tooltip : file.name,
30324                     cls : 'roo-document-manager-thumb',
30325                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
30326                 }
30327             ]
30328
30329         });
30330
30331         this.xhr.open(this.method, this.url, true);
30332         
30333         var headers = {
30334             "Accept": "application/json",
30335             "Cache-Control": "no-cache",
30336             "X-Requested-With": "XMLHttpRequest"
30337         };
30338         
30339         for (var headerName in headers) {
30340             var headerValue = headers[headerName];
30341             if (headerValue) {
30342                 this.xhr.setRequestHeader(headerName, headerValue);
30343             }
30344         }
30345         
30346         var _this = this;
30347         
30348         this.xhr.onload = function()
30349         {
30350             _this.xhrOnLoad(_this.xhr);
30351         }
30352         
30353         this.xhr.onerror = function()
30354         {
30355             _this.xhrOnError(_this.xhr);
30356         }
30357         
30358         var formData = new FormData();
30359
30360         formData.append('returnHTML', 'NO');
30361         
30362         formData.append('crop', crop);
30363         
30364         if(typeof(file.filename) != 'undefined'){
30365             formData.append('filename', file.filename);
30366         }
30367         
30368         if(typeof(file.mimetype) != 'undefined'){
30369             formData.append('mimetype', file.mimetype);
30370         }
30371         
30372         Roo.log(formData);
30373         
30374         if(this.fireEvent('prepare', this, formData) != false){
30375             this.xhr.send(formData);
30376         };
30377     }
30378 });
30379
30380 /*
30381 * Licence: LGPL
30382 */
30383
30384 /**
30385  * @class Roo.bootstrap.DocumentViewer
30386  * @extends Roo.bootstrap.Component
30387  * Bootstrap DocumentViewer class
30388  * @cfg {Boolean} showDownload (true|false) show download button (default true)
30389  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30390  * 
30391  * @constructor
30392  * Create a new DocumentViewer
30393  * @param {Object} config The config object
30394  */
30395
30396 Roo.bootstrap.DocumentViewer = function(config){
30397     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30398     
30399     this.addEvents({
30400         /**
30401          * @event initial
30402          * Fire after initEvent
30403          * @param {Roo.bootstrap.DocumentViewer} this
30404          */
30405         "initial" : true,
30406         /**
30407          * @event click
30408          * Fire after click
30409          * @param {Roo.bootstrap.DocumentViewer} this
30410          */
30411         "click" : true,
30412         /**
30413          * @event download
30414          * Fire after download button
30415          * @param {Roo.bootstrap.DocumentViewer} this
30416          */
30417         "download" : true,
30418         /**
30419          * @event trash
30420          * Fire after trash button
30421          * @param {Roo.bootstrap.DocumentViewer} this
30422          */
30423         "trash" : true
30424         
30425     });
30426 };
30427
30428 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
30429     
30430     showDownload : true,
30431     
30432     showTrash : true,
30433     
30434     getAutoCreate : function()
30435     {
30436         var cfg = {
30437             tag : 'div',
30438             cls : 'roo-document-viewer',
30439             cn : [
30440                 {
30441                     tag : 'div',
30442                     cls : 'roo-document-viewer-body',
30443                     cn : [
30444                         {
30445                             tag : 'div',
30446                             cls : 'roo-document-viewer-thumb',
30447                             cn : [
30448                                 {
30449                                     tag : 'img',
30450                                     cls : 'roo-document-viewer-image'
30451                                 }
30452                             ]
30453                         }
30454                     ]
30455                 },
30456                 {
30457                     tag : 'div',
30458                     cls : 'roo-document-viewer-footer',
30459                     cn : {
30460                         tag : 'div',
30461                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
30462                         cn : [
30463                             {
30464                                 tag : 'div',
30465                                 cls : 'btn-group roo-document-viewer-download',
30466                                 cn : [
30467                                     {
30468                                         tag : 'button',
30469                                         cls : 'btn btn-default',
30470                                         html : '<i class="fa fa-download"></i>'
30471                                     }
30472                                 ]
30473                             },
30474                             {
30475                                 tag : 'div',
30476                                 cls : 'btn-group roo-document-viewer-trash',
30477                                 cn : [
30478                                     {
30479                                         tag : 'button',
30480                                         cls : 'btn btn-default',
30481                                         html : '<i class="fa fa-trash"></i>'
30482                                     }
30483                                 ]
30484                             }
30485                         ]
30486                     }
30487                 }
30488             ]
30489         };
30490         
30491         return cfg;
30492     },
30493     
30494     initEvents : function()
30495     {
30496         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30497         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30498         
30499         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30500         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30501         
30502         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30503         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30504         
30505         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30506         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30507         
30508         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30509         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30510         
30511         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30512         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30513         
30514         this.bodyEl.on('click', this.onClick, this);
30515         this.downloadBtn.on('click', this.onDownload, this);
30516         this.trashBtn.on('click', this.onTrash, this);
30517         
30518         this.downloadBtn.hide();
30519         this.trashBtn.hide();
30520         
30521         if(this.showDownload){
30522             this.downloadBtn.show();
30523         }
30524         
30525         if(this.showTrash){
30526             this.trashBtn.show();
30527         }
30528         
30529         if(!this.showDownload && !this.showTrash) {
30530             this.footerEl.hide();
30531         }
30532         
30533     },
30534     
30535     initial : function()
30536     {
30537         this.fireEvent('initial', this);
30538         
30539     },
30540     
30541     onClick : function(e)
30542     {
30543         e.preventDefault();
30544         
30545         this.fireEvent('click', this);
30546     },
30547     
30548     onDownload : function(e)
30549     {
30550         e.preventDefault();
30551         
30552         this.fireEvent('download', this);
30553     },
30554     
30555     onTrash : function(e)
30556     {
30557         e.preventDefault();
30558         
30559         this.fireEvent('trash', this);
30560     }
30561     
30562 });
30563 /*
30564  * - LGPL
30565  *
30566  * nav progress bar
30567  * 
30568  */
30569
30570 /**
30571  * @class Roo.bootstrap.NavProgressBar
30572  * @extends Roo.bootstrap.Component
30573  * Bootstrap NavProgressBar class
30574  * 
30575  * @constructor
30576  * Create a new nav progress bar
30577  * @param {Object} config The config object
30578  */
30579
30580 Roo.bootstrap.NavProgressBar = function(config){
30581     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30582
30583     this.bullets = this.bullets || [];
30584    
30585 //    Roo.bootstrap.NavProgressBar.register(this);
30586      this.addEvents({
30587         /**
30588              * @event changed
30589              * Fires when the active item changes
30590              * @param {Roo.bootstrap.NavProgressBar} this
30591              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30592              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30593          */
30594         'changed': true
30595      });
30596     
30597 };
30598
30599 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30600     
30601     bullets : [],
30602     barItems : [],
30603     
30604     getAutoCreate : function()
30605     {
30606         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30607         
30608         cfg = {
30609             tag : 'div',
30610             cls : 'roo-navigation-bar-group',
30611             cn : [
30612                 {
30613                     tag : 'div',
30614                     cls : 'roo-navigation-top-bar'
30615                 },
30616                 {
30617                     tag : 'div',
30618                     cls : 'roo-navigation-bullets-bar',
30619                     cn : [
30620                         {
30621                             tag : 'ul',
30622                             cls : 'roo-navigation-bar'
30623                         }
30624                     ]
30625                 },
30626                 
30627                 {
30628                     tag : 'div',
30629                     cls : 'roo-navigation-bottom-bar'
30630                 }
30631             ]
30632             
30633         };
30634         
30635         return cfg;
30636         
30637     },
30638     
30639     initEvents: function() 
30640     {
30641         
30642     },
30643     
30644     onRender : function(ct, position) 
30645     {
30646         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30647         
30648         if(this.bullets.length){
30649             Roo.each(this.bullets, function(b){
30650                this.addItem(b);
30651             }, this);
30652         }
30653         
30654         this.format();
30655         
30656     },
30657     
30658     addItem : function(cfg)
30659     {
30660         var item = new Roo.bootstrap.NavProgressItem(cfg);
30661         
30662         item.parentId = this.id;
30663         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30664         
30665         if(cfg.html){
30666             var top = new Roo.bootstrap.Element({
30667                 tag : 'div',
30668                 cls : 'roo-navigation-bar-text'
30669             });
30670             
30671             var bottom = new Roo.bootstrap.Element({
30672                 tag : 'div',
30673                 cls : 'roo-navigation-bar-text'
30674             });
30675             
30676             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30677             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30678             
30679             var topText = new Roo.bootstrap.Element({
30680                 tag : 'span',
30681                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30682             });
30683             
30684             var bottomText = new Roo.bootstrap.Element({
30685                 tag : 'span',
30686                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30687             });
30688             
30689             topText.onRender(top.el, null);
30690             bottomText.onRender(bottom.el, null);
30691             
30692             item.topEl = top;
30693             item.bottomEl = bottom;
30694         }
30695         
30696         this.barItems.push(item);
30697         
30698         return item;
30699     },
30700     
30701     getActive : function()
30702     {
30703         var active = false;
30704         
30705         Roo.each(this.barItems, function(v){
30706             
30707             if (!v.isActive()) {
30708                 return;
30709             }
30710             
30711             active = v;
30712             return false;
30713             
30714         });
30715         
30716         return active;
30717     },
30718     
30719     setActiveItem : function(item)
30720     {
30721         var prev = false;
30722         
30723         Roo.each(this.barItems, function(v){
30724             if (v.rid == item.rid) {
30725                 return ;
30726             }
30727             
30728             if (v.isActive()) {
30729                 v.setActive(false);
30730                 prev = v;
30731             }
30732         });
30733
30734         item.setActive(true);
30735         
30736         this.fireEvent('changed', this, item, prev);
30737     },
30738     
30739     getBarItem: function(rid)
30740     {
30741         var ret = false;
30742         
30743         Roo.each(this.barItems, function(e) {
30744             if (e.rid != rid) {
30745                 return;
30746             }
30747             
30748             ret =  e;
30749             return false;
30750         });
30751         
30752         return ret;
30753     },
30754     
30755     indexOfItem : function(item)
30756     {
30757         var index = false;
30758         
30759         Roo.each(this.barItems, function(v, i){
30760             
30761             if (v.rid != item.rid) {
30762                 return;
30763             }
30764             
30765             index = i;
30766             return false
30767         });
30768         
30769         return index;
30770     },
30771     
30772     setActiveNext : function()
30773     {
30774         var i = this.indexOfItem(this.getActive());
30775         
30776         if (i > this.barItems.length) {
30777             return;
30778         }
30779         
30780         this.setActiveItem(this.barItems[i+1]);
30781     },
30782     
30783     setActivePrev : function()
30784     {
30785         var i = this.indexOfItem(this.getActive());
30786         
30787         if (i  < 1) {
30788             return;
30789         }
30790         
30791         this.setActiveItem(this.barItems[i-1]);
30792     },
30793     
30794     format : function()
30795     {
30796         if(!this.barItems.length){
30797             return;
30798         }
30799      
30800         var width = 100 / this.barItems.length;
30801         
30802         Roo.each(this.barItems, function(i){
30803             i.el.setStyle('width', width + '%');
30804             i.topEl.el.setStyle('width', width + '%');
30805             i.bottomEl.el.setStyle('width', width + '%');
30806         }, this);
30807         
30808     }
30809     
30810 });
30811 /*
30812  * - LGPL
30813  *
30814  * Nav Progress Item
30815  * 
30816  */
30817
30818 /**
30819  * @class Roo.bootstrap.NavProgressItem
30820  * @extends Roo.bootstrap.Component
30821  * Bootstrap NavProgressItem class
30822  * @cfg {String} rid the reference id
30823  * @cfg {Boolean} active (true|false) Is item active default false
30824  * @cfg {Boolean} disabled (true|false) Is item active default false
30825  * @cfg {String} html
30826  * @cfg {String} position (top|bottom) text position default bottom
30827  * @cfg {String} icon show icon instead of number
30828  * 
30829  * @constructor
30830  * Create a new NavProgressItem
30831  * @param {Object} config The config object
30832  */
30833 Roo.bootstrap.NavProgressItem = function(config){
30834     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30835     this.addEvents({
30836         // raw events
30837         /**
30838          * @event click
30839          * The raw click event for the entire grid.
30840          * @param {Roo.bootstrap.NavProgressItem} this
30841          * @param {Roo.EventObject} e
30842          */
30843         "click" : true
30844     });
30845    
30846 };
30847
30848 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30849     
30850     rid : '',
30851     active : false,
30852     disabled : false,
30853     html : '',
30854     position : 'bottom',
30855     icon : false,
30856     
30857     getAutoCreate : function()
30858     {
30859         var iconCls = 'roo-navigation-bar-item-icon';
30860         
30861         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30862         
30863         var cfg = {
30864             tag: 'li',
30865             cls: 'roo-navigation-bar-item',
30866             cn : [
30867                 {
30868                     tag : 'i',
30869                     cls : iconCls
30870                 }
30871             ]
30872         };
30873         
30874         if(this.active){
30875             cfg.cls += ' active';
30876         }
30877         if(this.disabled){
30878             cfg.cls += ' disabled';
30879         }
30880         
30881         return cfg;
30882     },
30883     
30884     disable : function()
30885     {
30886         this.setDisabled(true);
30887     },
30888     
30889     enable : function()
30890     {
30891         this.setDisabled(false);
30892     },
30893     
30894     initEvents: function() 
30895     {
30896         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30897         
30898         this.iconEl.on('click', this.onClick, this);
30899     },
30900     
30901     onClick : function(e)
30902     {
30903         e.preventDefault();
30904         
30905         if(this.disabled){
30906             return;
30907         }
30908         
30909         if(this.fireEvent('click', this, e) === false){
30910             return;
30911         };
30912         
30913         this.parent().setActiveItem(this);
30914     },
30915     
30916     isActive: function () 
30917     {
30918         return this.active;
30919     },
30920     
30921     setActive : function(state)
30922     {
30923         if(this.active == state){
30924             return;
30925         }
30926         
30927         this.active = state;
30928         
30929         if (state) {
30930             this.el.addClass('active');
30931             return;
30932         }
30933         
30934         this.el.removeClass('active');
30935         
30936         return;
30937     },
30938     
30939     setDisabled : function(state)
30940     {
30941         if(this.disabled == state){
30942             return;
30943         }
30944         
30945         this.disabled = state;
30946         
30947         if (state) {
30948             this.el.addClass('disabled');
30949             return;
30950         }
30951         
30952         this.el.removeClass('disabled');
30953     },
30954     
30955     tooltipEl : function()
30956     {
30957         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30958     }
30959 });
30960  
30961
30962  /*
30963  * - LGPL
30964  *
30965  * FieldLabel
30966  * 
30967  */
30968
30969 /**
30970  * @class Roo.bootstrap.FieldLabel
30971  * @extends Roo.bootstrap.Component
30972  * Bootstrap FieldLabel class
30973  * @cfg {String} html contents of the element
30974  * @cfg {String} tag tag of the element default label
30975  * @cfg {String} cls class of the element
30976  * @cfg {String} target label target 
30977  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30978  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30979  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30980  * @cfg {String} iconTooltip default "This field is required"
30981  * @cfg {String} indicatorpos (left|right) default left
30982  * 
30983  * @constructor
30984  * Create a new FieldLabel
30985  * @param {Object} config The config object
30986  */
30987
30988 Roo.bootstrap.FieldLabel = function(config){
30989     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30990     
30991     this.addEvents({
30992             /**
30993              * @event invalid
30994              * Fires after the field has been marked as invalid.
30995              * @param {Roo.form.FieldLabel} this
30996              * @param {String} msg The validation message
30997              */
30998             invalid : true,
30999             /**
31000              * @event valid
31001              * Fires after the field has been validated with no errors.
31002              * @param {Roo.form.FieldLabel} this
31003              */
31004             valid : true
31005         });
31006 };
31007
31008 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
31009     
31010     tag: 'label',
31011     cls: '',
31012     html: '',
31013     target: '',
31014     allowBlank : true,
31015     invalidClass : 'has-warning',
31016     validClass : 'has-success',
31017     iconTooltip : 'This field is required',
31018     indicatorpos : 'left',
31019     
31020     getAutoCreate : function(){
31021         
31022         var cls = "";
31023         if (!this.allowBlank) {
31024             cls  = "visible";
31025         }
31026         
31027         var cfg = {
31028             tag : this.tag,
31029             cls : 'roo-bootstrap-field-label ' + this.cls,
31030             for : this.target,
31031             cn : [
31032                 {
31033                     tag : 'i',
31034                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
31035                     tooltip : this.iconTooltip
31036                 },
31037                 {
31038                     tag : 'span',
31039                     html : this.html
31040                 }
31041             ] 
31042         };
31043         
31044         if(this.indicatorpos == 'right'){
31045             var cfg = {
31046                 tag : this.tag,
31047                 cls : 'roo-bootstrap-field-label ' + this.cls,
31048                 for : this.target,
31049                 cn : [
31050                     {
31051                         tag : 'span',
31052                         html : this.html
31053                     },
31054                     {
31055                         tag : 'i',
31056                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
31057                         tooltip : this.iconTooltip
31058                     }
31059                 ] 
31060             };
31061         }
31062         
31063         return cfg;
31064     },
31065     
31066     initEvents: function() 
31067     {
31068         Roo.bootstrap.Element.superclass.initEvents.call(this);
31069         
31070         this.indicator = this.indicatorEl();
31071         
31072         if(this.indicator){
31073             this.indicator.removeClass('visible');
31074             this.indicator.addClass('invisible');
31075         }
31076         
31077         Roo.bootstrap.FieldLabel.register(this);
31078     },
31079     
31080     indicatorEl : function()
31081     {
31082         var indicator = this.el.select('i.roo-required-indicator',true).first();
31083         
31084         if(!indicator){
31085             return false;
31086         }
31087         
31088         return indicator;
31089         
31090     },
31091     
31092     /**
31093      * Mark this field as valid
31094      */
31095     markValid : function()
31096     {
31097         if(this.indicator){
31098             this.indicator.removeClass('visible');
31099             this.indicator.addClass('invisible');
31100         }
31101         if (Roo.bootstrap.version == 3) {
31102             this.el.removeClass(this.invalidClass);
31103             this.el.addClass(this.validClass);
31104         } else {
31105             this.el.removeClass('is-invalid');
31106             this.el.addClass('is-valid');
31107         }
31108         
31109         
31110         this.fireEvent('valid', this);
31111     },
31112     
31113     /**
31114      * Mark this field as invalid
31115      * @param {String} msg The validation message
31116      */
31117     markInvalid : function(msg)
31118     {
31119         if(this.indicator){
31120             this.indicator.removeClass('invisible');
31121             this.indicator.addClass('visible');
31122         }
31123           if (Roo.bootstrap.version == 3) {
31124             this.el.removeClass(this.validClass);
31125             this.el.addClass(this.invalidClass);
31126         } else {
31127             this.el.removeClass('is-valid');
31128             this.el.addClass('is-invalid');
31129         }
31130         
31131         
31132         this.fireEvent('invalid', this, msg);
31133     }
31134     
31135    
31136 });
31137
31138 Roo.apply(Roo.bootstrap.FieldLabel, {
31139     
31140     groups: {},
31141     
31142      /**
31143     * register a FieldLabel Group
31144     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
31145     */
31146     register : function(label)
31147     {
31148         if(this.groups.hasOwnProperty(label.target)){
31149             return;
31150         }
31151      
31152         this.groups[label.target] = label;
31153         
31154     },
31155     /**
31156     * fetch a FieldLabel Group based on the target
31157     * @param {string} target
31158     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
31159     */
31160     get: function(target) {
31161         if (typeof(this.groups[target]) == 'undefined') {
31162             return false;
31163         }
31164         
31165         return this.groups[target] ;
31166     }
31167 });
31168
31169  
31170
31171  /*
31172  * - LGPL
31173  *
31174  * page DateSplitField.
31175  * 
31176  */
31177
31178
31179 /**
31180  * @class Roo.bootstrap.DateSplitField
31181  * @extends Roo.bootstrap.Component
31182  * Bootstrap DateSplitField class
31183  * @cfg {string} fieldLabel - the label associated
31184  * @cfg {Number} labelWidth set the width of label (0-12)
31185  * @cfg {String} labelAlign (top|left)
31186  * @cfg {Boolean} dayAllowBlank (true|false) default false
31187  * @cfg {Boolean} monthAllowBlank (true|false) default false
31188  * @cfg {Boolean} yearAllowBlank (true|false) default false
31189  * @cfg {string} dayPlaceholder 
31190  * @cfg {string} monthPlaceholder
31191  * @cfg {string} yearPlaceholder
31192  * @cfg {string} dayFormat default 'd'
31193  * @cfg {string} monthFormat default 'm'
31194  * @cfg {string} yearFormat default 'Y'
31195  * @cfg {Number} labellg set the width of label (1-12)
31196  * @cfg {Number} labelmd set the width of label (1-12)
31197  * @cfg {Number} labelsm set the width of label (1-12)
31198  * @cfg {Number} labelxs set the width of label (1-12)
31199
31200  *     
31201  * @constructor
31202  * Create a new DateSplitField
31203  * @param {Object} config The config object
31204  */
31205
31206 Roo.bootstrap.DateSplitField = function(config){
31207     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
31208     
31209     this.addEvents({
31210         // raw events
31211          /**
31212          * @event years
31213          * getting the data of years
31214          * @param {Roo.bootstrap.DateSplitField} this
31215          * @param {Object} years
31216          */
31217         "years" : true,
31218         /**
31219          * @event days
31220          * getting the data of days
31221          * @param {Roo.bootstrap.DateSplitField} this
31222          * @param {Object} days
31223          */
31224         "days" : true,
31225         /**
31226          * @event invalid
31227          * Fires after the field has been marked as invalid.
31228          * @param {Roo.form.Field} this
31229          * @param {String} msg The validation message
31230          */
31231         invalid : true,
31232        /**
31233          * @event valid
31234          * Fires after the field has been validated with no errors.
31235          * @param {Roo.form.Field} this
31236          */
31237         valid : true
31238     });
31239 };
31240
31241 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
31242     
31243     fieldLabel : '',
31244     labelAlign : 'top',
31245     labelWidth : 3,
31246     dayAllowBlank : false,
31247     monthAllowBlank : false,
31248     yearAllowBlank : false,
31249     dayPlaceholder : '',
31250     monthPlaceholder : '',
31251     yearPlaceholder : '',
31252     dayFormat : 'd',
31253     monthFormat : 'm',
31254     yearFormat : 'Y',
31255     isFormField : true,
31256     labellg : 0,
31257     labelmd : 0,
31258     labelsm : 0,
31259     labelxs : 0,
31260     
31261     getAutoCreate : function()
31262     {
31263         var cfg = {
31264             tag : 'div',
31265             cls : 'row roo-date-split-field-group',
31266             cn : [
31267                 {
31268                     tag : 'input',
31269                     type : 'hidden',
31270                     cls : 'form-hidden-field roo-date-split-field-group-value',
31271                     name : this.name
31272                 }
31273             ]
31274         };
31275         
31276         var labelCls = 'col-md-12';
31277         var contentCls = 'col-md-4';
31278         
31279         if(this.fieldLabel){
31280             
31281             var label = {
31282                 tag : 'div',
31283                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
31284                 cn : [
31285                     {
31286                         tag : 'label',
31287                         html : this.fieldLabel
31288                     }
31289                 ]
31290             };
31291             
31292             if(this.labelAlign == 'left'){
31293             
31294                 if(this.labelWidth > 12){
31295                     label.style = "width: " + this.labelWidth + 'px';
31296                 }
31297
31298                 if(this.labelWidth < 13 && this.labelmd == 0){
31299                     this.labelmd = this.labelWidth;
31300                 }
31301
31302                 if(this.labellg > 0){
31303                     labelCls = ' col-lg-' + this.labellg;
31304                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
31305                 }
31306
31307                 if(this.labelmd > 0){
31308                     labelCls = ' col-md-' + this.labelmd;
31309                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
31310                 }
31311
31312                 if(this.labelsm > 0){
31313                     labelCls = ' col-sm-' + this.labelsm;
31314                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
31315                 }
31316
31317                 if(this.labelxs > 0){
31318                     labelCls = ' col-xs-' + this.labelxs;
31319                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
31320                 }
31321             }
31322             
31323             label.cls += ' ' + labelCls;
31324             
31325             cfg.cn.push(label);
31326         }
31327         
31328         Roo.each(['day', 'month', 'year'], function(t){
31329             cfg.cn.push({
31330                 tag : 'div',
31331                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
31332             });
31333         }, this);
31334         
31335         return cfg;
31336     },
31337     
31338     inputEl: function ()
31339     {
31340         return this.el.select('.roo-date-split-field-group-value', true).first();
31341     },
31342     
31343     onRender : function(ct, position) 
31344     {
31345         var _this = this;
31346         
31347         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31348         
31349         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
31350         
31351         this.dayField = new Roo.bootstrap.ComboBox({
31352             allowBlank : this.dayAllowBlank,
31353             alwaysQuery : true,
31354             displayField : 'value',
31355             editable : false,
31356             fieldLabel : '',
31357             forceSelection : true,
31358             mode : 'local',
31359             placeholder : this.dayPlaceholder,
31360             selectOnFocus : true,
31361             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31362             triggerAction : 'all',
31363             typeAhead : true,
31364             valueField : 'value',
31365             store : new Roo.data.SimpleStore({
31366                 data : (function() {    
31367                     var days = [];
31368                     _this.fireEvent('days', _this, days);
31369                     return days;
31370                 })(),
31371                 fields : [ 'value' ]
31372             }),
31373             listeners : {
31374                 select : function (_self, record, index)
31375                 {
31376                     _this.setValue(_this.getValue());
31377                 }
31378             }
31379         });
31380
31381         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
31382         
31383         this.monthField = new Roo.bootstrap.MonthField({
31384             after : '<i class=\"fa fa-calendar\"></i>',
31385             allowBlank : this.monthAllowBlank,
31386             placeholder : this.monthPlaceholder,
31387             readOnly : true,
31388             listeners : {
31389                 render : function (_self)
31390                 {
31391                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
31392                         e.preventDefault();
31393                         _self.focus();
31394                     });
31395                 },
31396                 select : function (_self, oldvalue, newvalue)
31397                 {
31398                     _this.setValue(_this.getValue());
31399                 }
31400             }
31401         });
31402         
31403         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31404         
31405         this.yearField = new Roo.bootstrap.ComboBox({
31406             allowBlank : this.yearAllowBlank,
31407             alwaysQuery : true,
31408             displayField : 'value',
31409             editable : false,
31410             fieldLabel : '',
31411             forceSelection : true,
31412             mode : 'local',
31413             placeholder : this.yearPlaceholder,
31414             selectOnFocus : true,
31415             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31416             triggerAction : 'all',
31417             typeAhead : true,
31418             valueField : 'value',
31419             store : new Roo.data.SimpleStore({
31420                 data : (function() {
31421                     var years = [];
31422                     _this.fireEvent('years', _this, years);
31423                     return years;
31424                 })(),
31425                 fields : [ 'value' ]
31426             }),
31427             listeners : {
31428                 select : function (_self, record, index)
31429                 {
31430                     _this.setValue(_this.getValue());
31431                 }
31432             }
31433         });
31434
31435         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
31436     },
31437     
31438     setValue : function(v, format)
31439     {
31440         this.inputEl.dom.value = v;
31441         
31442         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31443         
31444         var d = Date.parseDate(v, f);
31445         
31446         if(!d){
31447             this.validate();
31448             return;
31449         }
31450         
31451         this.setDay(d.format(this.dayFormat));
31452         this.setMonth(d.format(this.monthFormat));
31453         this.setYear(d.format(this.yearFormat));
31454         
31455         this.validate();
31456         
31457         return;
31458     },
31459     
31460     setDay : function(v)
31461     {
31462         this.dayField.setValue(v);
31463         this.inputEl.dom.value = this.getValue();
31464         this.validate();
31465         return;
31466     },
31467     
31468     setMonth : function(v)
31469     {
31470         this.monthField.setValue(v, true);
31471         this.inputEl.dom.value = this.getValue();
31472         this.validate();
31473         return;
31474     },
31475     
31476     setYear : function(v)
31477     {
31478         this.yearField.setValue(v);
31479         this.inputEl.dom.value = this.getValue();
31480         this.validate();
31481         return;
31482     },
31483     
31484     getDay : function()
31485     {
31486         return this.dayField.getValue();
31487     },
31488     
31489     getMonth : function()
31490     {
31491         return this.monthField.getValue();
31492     },
31493     
31494     getYear : function()
31495     {
31496         return this.yearField.getValue();
31497     },
31498     
31499     getValue : function()
31500     {
31501         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31502         
31503         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31504         
31505         return date;
31506     },
31507     
31508     reset : function()
31509     {
31510         this.setDay('');
31511         this.setMonth('');
31512         this.setYear('');
31513         this.inputEl.dom.value = '';
31514         this.validate();
31515         return;
31516     },
31517     
31518     validate : function()
31519     {
31520         var d = this.dayField.validate();
31521         var m = this.monthField.validate();
31522         var y = this.yearField.validate();
31523         
31524         var valid = true;
31525         
31526         if(
31527                 (!this.dayAllowBlank && !d) ||
31528                 (!this.monthAllowBlank && !m) ||
31529                 (!this.yearAllowBlank && !y)
31530         ){
31531             valid = false;
31532         }
31533         
31534         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31535             return valid;
31536         }
31537         
31538         if(valid){
31539             this.markValid();
31540             return valid;
31541         }
31542         
31543         this.markInvalid();
31544         
31545         return valid;
31546     },
31547     
31548     markValid : function()
31549     {
31550         
31551         var label = this.el.select('label', true).first();
31552         var icon = this.el.select('i.fa-star', true).first();
31553
31554         if(label && icon){
31555             icon.remove();
31556         }
31557         
31558         this.fireEvent('valid', this);
31559     },
31560     
31561      /**
31562      * Mark this field as invalid
31563      * @param {String} msg The validation message
31564      */
31565     markInvalid : function(msg)
31566     {
31567         
31568         var label = this.el.select('label', true).first();
31569         var icon = this.el.select('i.fa-star', true).first();
31570
31571         if(label && !icon){
31572             this.el.select('.roo-date-split-field-label', true).createChild({
31573                 tag : 'i',
31574                 cls : 'text-danger fa fa-lg fa-star',
31575                 tooltip : 'This field is required',
31576                 style : 'margin-right:5px;'
31577             }, label, true);
31578         }
31579         
31580         this.fireEvent('invalid', this, msg);
31581     },
31582     
31583     clearInvalid : function()
31584     {
31585         var label = this.el.select('label', true).first();
31586         var icon = this.el.select('i.fa-star', true).first();
31587
31588         if(label && icon){
31589             icon.remove();
31590         }
31591         
31592         this.fireEvent('valid', this);
31593     },
31594     
31595     getName: function()
31596     {
31597         return this.name;
31598     }
31599     
31600 });
31601
31602  /**
31603  *
31604  * This is based on 
31605  * http://masonry.desandro.com
31606  *
31607  * The idea is to render all the bricks based on vertical width...
31608  *
31609  * The original code extends 'outlayer' - we might need to use that....
31610  * 
31611  */
31612
31613
31614 /**
31615  * @class Roo.bootstrap.LayoutMasonry
31616  * @extends Roo.bootstrap.Component
31617  * Bootstrap Layout Masonry class
31618  * 
31619  * @constructor
31620  * Create a new Element
31621  * @param {Object} config The config object
31622  */
31623
31624 Roo.bootstrap.LayoutMasonry = function(config){
31625     
31626     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31627     
31628     this.bricks = [];
31629     
31630     Roo.bootstrap.LayoutMasonry.register(this);
31631     
31632     this.addEvents({
31633         // raw events
31634         /**
31635          * @event layout
31636          * Fire after layout the items
31637          * @param {Roo.bootstrap.LayoutMasonry} this
31638          * @param {Roo.EventObject} e
31639          */
31640         "layout" : true
31641     });
31642     
31643 };
31644
31645 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31646     
31647     /**
31648      * @cfg {Boolean} isLayoutInstant = no animation?
31649      */   
31650     isLayoutInstant : false, // needed?
31651    
31652     /**
31653      * @cfg {Number} boxWidth  width of the columns
31654      */   
31655     boxWidth : 450,
31656     
31657       /**
31658      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31659      */   
31660     boxHeight : 0,
31661     
31662     /**
31663      * @cfg {Number} padWidth padding below box..
31664      */   
31665     padWidth : 10, 
31666     
31667     /**
31668      * @cfg {Number} gutter gutter width..
31669      */   
31670     gutter : 10,
31671     
31672      /**
31673      * @cfg {Number} maxCols maximum number of columns
31674      */   
31675     
31676     maxCols: 0,
31677     
31678     /**
31679      * @cfg {Boolean} isAutoInitial defalut true
31680      */   
31681     isAutoInitial : true, 
31682     
31683     containerWidth: 0,
31684     
31685     /**
31686      * @cfg {Boolean} isHorizontal defalut false
31687      */   
31688     isHorizontal : false, 
31689
31690     currentSize : null,
31691     
31692     tag: 'div',
31693     
31694     cls: '',
31695     
31696     bricks: null, //CompositeElement
31697     
31698     cols : 1,
31699     
31700     _isLayoutInited : false,
31701     
31702 //    isAlternative : false, // only use for vertical layout...
31703     
31704     /**
31705      * @cfg {Number} alternativePadWidth padding below box..
31706      */   
31707     alternativePadWidth : 50,
31708     
31709     selectedBrick : [],
31710     
31711     getAutoCreate : function(){
31712         
31713         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31714         
31715         var cfg = {
31716             tag: this.tag,
31717             cls: 'blog-masonary-wrapper ' + this.cls,
31718             cn : {
31719                 cls : 'mas-boxes masonary'
31720             }
31721         };
31722         
31723         return cfg;
31724     },
31725     
31726     getChildContainer: function( )
31727     {
31728         if (this.boxesEl) {
31729             return this.boxesEl;
31730         }
31731         
31732         this.boxesEl = this.el.select('.mas-boxes').first();
31733         
31734         return this.boxesEl;
31735     },
31736     
31737     
31738     initEvents : function()
31739     {
31740         var _this = this;
31741         
31742         if(this.isAutoInitial){
31743             Roo.log('hook children rendered');
31744             this.on('childrenrendered', function() {
31745                 Roo.log('children rendered');
31746                 _this.initial();
31747             } ,this);
31748         }
31749     },
31750     
31751     initial : function()
31752     {
31753         this.selectedBrick = [];
31754         
31755         this.currentSize = this.el.getBox(true);
31756         
31757         Roo.EventManager.onWindowResize(this.resize, this); 
31758
31759         if(!this.isAutoInitial){
31760             this.layout();
31761             return;
31762         }
31763         
31764         this.layout();
31765         
31766         return;
31767         //this.layout.defer(500,this);
31768         
31769     },
31770     
31771     resize : function()
31772     {
31773         var cs = this.el.getBox(true);
31774         
31775         if (
31776                 this.currentSize.width == cs.width && 
31777                 this.currentSize.x == cs.x && 
31778                 this.currentSize.height == cs.height && 
31779                 this.currentSize.y == cs.y 
31780         ) {
31781             Roo.log("no change in with or X or Y");
31782             return;
31783         }
31784         
31785         this.currentSize = cs;
31786         
31787         this.layout();
31788         
31789     },
31790     
31791     layout : function()
31792     {   
31793         this._resetLayout();
31794         
31795         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31796         
31797         this.layoutItems( isInstant );
31798       
31799         this._isLayoutInited = true;
31800         
31801         this.fireEvent('layout', this);
31802         
31803     },
31804     
31805     _resetLayout : function()
31806     {
31807         if(this.isHorizontal){
31808             this.horizontalMeasureColumns();
31809             return;
31810         }
31811         
31812         this.verticalMeasureColumns();
31813         
31814     },
31815     
31816     verticalMeasureColumns : function()
31817     {
31818         this.getContainerWidth();
31819         
31820 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31821 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31822 //            return;
31823 //        }
31824         
31825         var boxWidth = this.boxWidth + this.padWidth;
31826         
31827         if(this.containerWidth < this.boxWidth){
31828             boxWidth = this.containerWidth
31829         }
31830         
31831         var containerWidth = this.containerWidth;
31832         
31833         var cols = Math.floor(containerWidth / boxWidth);
31834         
31835         this.cols = Math.max( cols, 1 );
31836         
31837         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31838         
31839         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31840         
31841         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31842         
31843         this.colWidth = boxWidth + avail - this.padWidth;
31844         
31845         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31846         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31847     },
31848     
31849     horizontalMeasureColumns : function()
31850     {
31851         this.getContainerWidth();
31852         
31853         var boxWidth = this.boxWidth;
31854         
31855         if(this.containerWidth < boxWidth){
31856             boxWidth = this.containerWidth;
31857         }
31858         
31859         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31860         
31861         this.el.setHeight(boxWidth);
31862         
31863     },
31864     
31865     getContainerWidth : function()
31866     {
31867         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31868     },
31869     
31870     layoutItems : function( isInstant )
31871     {
31872         Roo.log(this.bricks);
31873         
31874         var items = Roo.apply([], this.bricks);
31875         
31876         if(this.isHorizontal){
31877             this._horizontalLayoutItems( items , isInstant );
31878             return;
31879         }
31880         
31881 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31882 //            this._verticalAlternativeLayoutItems( items , isInstant );
31883 //            return;
31884 //        }
31885         
31886         this._verticalLayoutItems( items , isInstant );
31887         
31888     },
31889     
31890     _verticalLayoutItems : function ( items , isInstant)
31891     {
31892         if ( !items || !items.length ) {
31893             return;
31894         }
31895         
31896         var standard = [
31897             ['xs', 'xs', 'xs', 'tall'],
31898             ['xs', 'xs', 'tall'],
31899             ['xs', 'xs', 'sm'],
31900             ['xs', 'xs', 'xs'],
31901             ['xs', 'tall'],
31902             ['xs', 'sm'],
31903             ['xs', 'xs'],
31904             ['xs'],
31905             
31906             ['sm', 'xs', 'xs'],
31907             ['sm', 'xs'],
31908             ['sm'],
31909             
31910             ['tall', 'xs', 'xs', 'xs'],
31911             ['tall', 'xs', 'xs'],
31912             ['tall', 'xs'],
31913             ['tall']
31914             
31915         ];
31916         
31917         var queue = [];
31918         
31919         var boxes = [];
31920         
31921         var box = [];
31922         
31923         Roo.each(items, function(item, k){
31924             
31925             switch (item.size) {
31926                 // these layouts take up a full box,
31927                 case 'md' :
31928                 case 'md-left' :
31929                 case 'md-right' :
31930                 case 'wide' :
31931                     
31932                     if(box.length){
31933                         boxes.push(box);
31934                         box = [];
31935                     }
31936                     
31937                     boxes.push([item]);
31938                     
31939                     break;
31940                     
31941                 case 'xs' :
31942                 case 'sm' :
31943                 case 'tall' :
31944                     
31945                     box.push(item);
31946                     
31947                     break;
31948                 default :
31949                     break;
31950                     
31951             }
31952             
31953         }, this);
31954         
31955         if(box.length){
31956             boxes.push(box);
31957             box = [];
31958         }
31959         
31960         var filterPattern = function(box, length)
31961         {
31962             if(!box.length){
31963                 return;
31964             }
31965             
31966             var match = false;
31967             
31968             var pattern = box.slice(0, length);
31969             
31970             var format = [];
31971             
31972             Roo.each(pattern, function(i){
31973                 format.push(i.size);
31974             }, this);
31975             
31976             Roo.each(standard, function(s){
31977                 
31978                 if(String(s) != String(format)){
31979                     return;
31980                 }
31981                 
31982                 match = true;
31983                 return false;
31984                 
31985             }, this);
31986             
31987             if(!match && length == 1){
31988                 return;
31989             }
31990             
31991             if(!match){
31992                 filterPattern(box, length - 1);
31993                 return;
31994             }
31995                 
31996             queue.push(pattern);
31997
31998             box = box.slice(length, box.length);
31999
32000             filterPattern(box, 4);
32001
32002             return;
32003             
32004         }
32005         
32006         Roo.each(boxes, function(box, k){
32007             
32008             if(!box.length){
32009                 return;
32010             }
32011             
32012             if(box.length == 1){
32013                 queue.push(box);
32014                 return;
32015             }
32016             
32017             filterPattern(box, 4);
32018             
32019         }, this);
32020         
32021         this._processVerticalLayoutQueue( queue, isInstant );
32022         
32023     },
32024     
32025 //    _verticalAlternativeLayoutItems : function( items , isInstant )
32026 //    {
32027 //        if ( !items || !items.length ) {
32028 //            return;
32029 //        }
32030 //
32031 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
32032 //        
32033 //    },
32034     
32035     _horizontalLayoutItems : function ( items , isInstant)
32036     {
32037         if ( !items || !items.length || items.length < 3) {
32038             return;
32039         }
32040         
32041         items.reverse();
32042         
32043         var eItems = items.slice(0, 3);
32044         
32045         items = items.slice(3, items.length);
32046         
32047         var standard = [
32048             ['xs', 'xs', 'xs', 'wide'],
32049             ['xs', 'xs', 'wide'],
32050             ['xs', 'xs', 'sm'],
32051             ['xs', 'xs', 'xs'],
32052             ['xs', 'wide'],
32053             ['xs', 'sm'],
32054             ['xs', 'xs'],
32055             ['xs'],
32056             
32057             ['sm', 'xs', 'xs'],
32058             ['sm', 'xs'],
32059             ['sm'],
32060             
32061             ['wide', 'xs', 'xs', 'xs'],
32062             ['wide', 'xs', 'xs'],
32063             ['wide', 'xs'],
32064             ['wide'],
32065             
32066             ['wide-thin']
32067         ];
32068         
32069         var queue = [];
32070         
32071         var boxes = [];
32072         
32073         var box = [];
32074         
32075         Roo.each(items, function(item, k){
32076             
32077             switch (item.size) {
32078                 case 'md' :
32079                 case 'md-left' :
32080                 case 'md-right' :
32081                 case 'tall' :
32082                     
32083                     if(box.length){
32084                         boxes.push(box);
32085                         box = [];
32086                     }
32087                     
32088                     boxes.push([item]);
32089                     
32090                     break;
32091                     
32092                 case 'xs' :
32093                 case 'sm' :
32094                 case 'wide' :
32095                 case 'wide-thin' :
32096                     
32097                     box.push(item);
32098                     
32099                     break;
32100                 default :
32101                     break;
32102                     
32103             }
32104             
32105         }, this);
32106         
32107         if(box.length){
32108             boxes.push(box);
32109             box = [];
32110         }
32111         
32112         var filterPattern = function(box, length)
32113         {
32114             if(!box.length){
32115                 return;
32116             }
32117             
32118             var match = false;
32119             
32120             var pattern = box.slice(0, length);
32121             
32122             var format = [];
32123             
32124             Roo.each(pattern, function(i){
32125                 format.push(i.size);
32126             }, this);
32127             
32128             Roo.each(standard, function(s){
32129                 
32130                 if(String(s) != String(format)){
32131                     return;
32132                 }
32133                 
32134                 match = true;
32135                 return false;
32136                 
32137             }, this);
32138             
32139             if(!match && length == 1){
32140                 return;
32141             }
32142             
32143             if(!match){
32144                 filterPattern(box, length - 1);
32145                 return;
32146             }
32147                 
32148             queue.push(pattern);
32149
32150             box = box.slice(length, box.length);
32151
32152             filterPattern(box, 4);
32153
32154             return;
32155             
32156         }
32157         
32158         Roo.each(boxes, function(box, k){
32159             
32160             if(!box.length){
32161                 return;
32162             }
32163             
32164             if(box.length == 1){
32165                 queue.push(box);
32166                 return;
32167             }
32168             
32169             filterPattern(box, 4);
32170             
32171         }, this);
32172         
32173         
32174         var prune = [];
32175         
32176         var pos = this.el.getBox(true);
32177         
32178         var minX = pos.x;
32179         
32180         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32181         
32182         var hit_end = false;
32183         
32184         Roo.each(queue, function(box){
32185             
32186             if(hit_end){
32187                 
32188                 Roo.each(box, function(b){
32189                 
32190                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
32191                     b.el.hide();
32192
32193                 }, this);
32194
32195                 return;
32196             }
32197             
32198             var mx = 0;
32199             
32200             Roo.each(box, function(b){
32201                 
32202                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
32203                 b.el.show();
32204
32205                 mx = Math.max(mx, b.x);
32206                 
32207             }, this);
32208             
32209             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
32210             
32211             if(maxX < minX){
32212                 
32213                 Roo.each(box, function(b){
32214                 
32215                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
32216                     b.el.hide();
32217                     
32218                 }, this);
32219                 
32220                 hit_end = true;
32221                 
32222                 return;
32223             }
32224             
32225             prune.push(box);
32226             
32227         }, this);
32228         
32229         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
32230     },
32231     
32232     /** Sets position of item in DOM
32233     * @param {Element} item
32234     * @param {Number} x - horizontal position
32235     * @param {Number} y - vertical position
32236     * @param {Boolean} isInstant - disables transitions
32237     */
32238     _processVerticalLayoutQueue : function( queue, isInstant )
32239     {
32240         var pos = this.el.getBox(true);
32241         var x = pos.x;
32242         var y = pos.y;
32243         var maxY = [];
32244         
32245         for (var i = 0; i < this.cols; i++){
32246             maxY[i] = pos.y;
32247         }
32248         
32249         Roo.each(queue, function(box, k){
32250             
32251             var col = k % this.cols;
32252             
32253             Roo.each(box, function(b,kk){
32254                 
32255                 b.el.position('absolute');
32256                 
32257                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32258                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32259                 
32260                 if(b.size == 'md-left' || b.size == 'md-right'){
32261                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32262                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32263                 }
32264                 
32265                 b.el.setWidth(width);
32266                 b.el.setHeight(height);
32267                 // iframe?
32268                 b.el.select('iframe',true).setSize(width,height);
32269                 
32270             }, this);
32271             
32272             for (var i = 0; i < this.cols; i++){
32273                 
32274                 if(maxY[i] < maxY[col]){
32275                     col = i;
32276                     continue;
32277                 }
32278                 
32279                 col = Math.min(col, i);
32280                 
32281             }
32282             
32283             x = pos.x + col * (this.colWidth + this.padWidth);
32284             
32285             y = maxY[col];
32286             
32287             var positions = [];
32288             
32289             switch (box.length){
32290                 case 1 :
32291                     positions = this.getVerticalOneBoxColPositions(x, y, box);
32292                     break;
32293                 case 2 :
32294                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
32295                     break;
32296                 case 3 :
32297                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
32298                     break;
32299                 case 4 :
32300                     positions = this.getVerticalFourBoxColPositions(x, y, box);
32301                     break;
32302                 default :
32303                     break;
32304             }
32305             
32306             Roo.each(box, function(b,kk){
32307                 
32308                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32309                 
32310                 var sz = b.el.getSize();
32311                 
32312                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
32313                 
32314             }, this);
32315             
32316         }, this);
32317         
32318         var mY = 0;
32319         
32320         for (var i = 0; i < this.cols; i++){
32321             mY = Math.max(mY, maxY[i]);
32322         }
32323         
32324         this.el.setHeight(mY - pos.y);
32325         
32326     },
32327     
32328 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
32329 //    {
32330 //        var pos = this.el.getBox(true);
32331 //        var x = pos.x;
32332 //        var y = pos.y;
32333 //        var maxX = pos.right;
32334 //        
32335 //        var maxHeight = 0;
32336 //        
32337 //        Roo.each(items, function(item, k){
32338 //            
32339 //            var c = k % 2;
32340 //            
32341 //            item.el.position('absolute');
32342 //                
32343 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
32344 //
32345 //            item.el.setWidth(width);
32346 //
32347 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
32348 //
32349 //            item.el.setHeight(height);
32350 //            
32351 //            if(c == 0){
32352 //                item.el.setXY([x, y], isInstant ? false : true);
32353 //            } else {
32354 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
32355 //            }
32356 //            
32357 //            y = y + height + this.alternativePadWidth;
32358 //            
32359 //            maxHeight = maxHeight + height + this.alternativePadWidth;
32360 //            
32361 //        }, this);
32362 //        
32363 //        this.el.setHeight(maxHeight);
32364 //        
32365 //    },
32366     
32367     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
32368     {
32369         var pos = this.el.getBox(true);
32370         
32371         var minX = pos.x;
32372         var minY = pos.y;
32373         
32374         var maxX = pos.right;
32375         
32376         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
32377         
32378         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
32379         
32380         Roo.each(queue, function(box, k){
32381             
32382             Roo.each(box, function(b, kk){
32383                 
32384                 b.el.position('absolute');
32385                 
32386                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32387                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32388                 
32389                 if(b.size == 'md-left' || b.size == 'md-right'){
32390                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32391                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32392                 }
32393                 
32394                 b.el.setWidth(width);
32395                 b.el.setHeight(height);
32396                 
32397             }, this);
32398             
32399             if(!box.length){
32400                 return;
32401             }
32402             
32403             var positions = [];
32404             
32405             switch (box.length){
32406                 case 1 :
32407                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32408                     break;
32409                 case 2 :
32410                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32411                     break;
32412                 case 3 :
32413                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
32414                     break;
32415                 case 4 :
32416                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
32417                     break;
32418                 default :
32419                     break;
32420             }
32421             
32422             Roo.each(box, function(b,kk){
32423                 
32424                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32425                 
32426                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
32427                 
32428             }, this);
32429             
32430         }, this);
32431         
32432     },
32433     
32434     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
32435     {
32436         Roo.each(eItems, function(b,k){
32437             
32438             b.size = (k == 0) ? 'sm' : 'xs';
32439             b.x = (k == 0) ? 2 : 1;
32440             b.y = (k == 0) ? 2 : 1;
32441             
32442             b.el.position('absolute');
32443             
32444             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32445                 
32446             b.el.setWidth(width);
32447             
32448             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32449             
32450             b.el.setHeight(height);
32451             
32452         }, this);
32453
32454         var positions = [];
32455         
32456         positions.push({
32457             x : maxX - this.unitWidth * 2 - this.gutter,
32458             y : minY
32459         });
32460         
32461         positions.push({
32462             x : maxX - this.unitWidth,
32463             y : minY + (this.unitWidth + this.gutter) * 2
32464         });
32465         
32466         positions.push({
32467             x : maxX - this.unitWidth * 3 - this.gutter * 2,
32468             y : minY
32469         });
32470         
32471         Roo.each(eItems, function(b,k){
32472             
32473             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32474
32475         }, this);
32476         
32477     },
32478     
32479     getVerticalOneBoxColPositions : function(x, y, box)
32480     {
32481         var pos = [];
32482         
32483         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32484         
32485         if(box[0].size == 'md-left'){
32486             rand = 0;
32487         }
32488         
32489         if(box[0].size == 'md-right'){
32490             rand = 1;
32491         }
32492         
32493         pos.push({
32494             x : x + (this.unitWidth + this.gutter) * rand,
32495             y : y
32496         });
32497         
32498         return pos;
32499     },
32500     
32501     getVerticalTwoBoxColPositions : function(x, y, box)
32502     {
32503         var pos = [];
32504         
32505         if(box[0].size == 'xs'){
32506             
32507             pos.push({
32508                 x : x,
32509                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32510             });
32511
32512             pos.push({
32513                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32514                 y : y
32515             });
32516             
32517             return pos;
32518             
32519         }
32520         
32521         pos.push({
32522             x : x,
32523             y : y
32524         });
32525
32526         pos.push({
32527             x : x + (this.unitWidth + this.gutter) * 2,
32528             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32529         });
32530         
32531         return pos;
32532         
32533     },
32534     
32535     getVerticalThreeBoxColPositions : function(x, y, box)
32536     {
32537         var pos = [];
32538         
32539         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32540             
32541             pos.push({
32542                 x : x,
32543                 y : y
32544             });
32545
32546             pos.push({
32547                 x : x + (this.unitWidth + this.gutter) * 1,
32548                 y : y
32549             });
32550             
32551             pos.push({
32552                 x : x + (this.unitWidth + this.gutter) * 2,
32553                 y : y
32554             });
32555             
32556             return pos;
32557             
32558         }
32559         
32560         if(box[0].size == 'xs' && box[1].size == 'xs'){
32561             
32562             pos.push({
32563                 x : x,
32564                 y : y
32565             });
32566
32567             pos.push({
32568                 x : x,
32569                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32570             });
32571             
32572             pos.push({
32573                 x : x + (this.unitWidth + this.gutter) * 1,
32574                 y : y
32575             });
32576             
32577             return pos;
32578             
32579         }
32580         
32581         pos.push({
32582             x : x,
32583             y : y
32584         });
32585
32586         pos.push({
32587             x : x + (this.unitWidth + this.gutter) * 2,
32588             y : y
32589         });
32590
32591         pos.push({
32592             x : x + (this.unitWidth + this.gutter) * 2,
32593             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32594         });
32595             
32596         return pos;
32597         
32598     },
32599     
32600     getVerticalFourBoxColPositions : function(x, y, box)
32601     {
32602         var pos = [];
32603         
32604         if(box[0].size == 'xs'){
32605             
32606             pos.push({
32607                 x : x,
32608                 y : y
32609             });
32610
32611             pos.push({
32612                 x : x,
32613                 y : y + (this.unitHeight + this.gutter) * 1
32614             });
32615             
32616             pos.push({
32617                 x : x,
32618                 y : y + (this.unitHeight + this.gutter) * 2
32619             });
32620             
32621             pos.push({
32622                 x : x + (this.unitWidth + this.gutter) * 1,
32623                 y : y
32624             });
32625             
32626             return pos;
32627             
32628         }
32629         
32630         pos.push({
32631             x : x,
32632             y : y
32633         });
32634
32635         pos.push({
32636             x : x + (this.unitWidth + this.gutter) * 2,
32637             y : y
32638         });
32639
32640         pos.push({
32641             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32642             y : y + (this.unitHeight + this.gutter) * 1
32643         });
32644
32645         pos.push({
32646             x : x + (this.unitWidth + this.gutter) * 2,
32647             y : y + (this.unitWidth + this.gutter) * 2
32648         });
32649
32650         return pos;
32651         
32652     },
32653     
32654     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32655     {
32656         var pos = [];
32657         
32658         if(box[0].size == 'md-left'){
32659             pos.push({
32660                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32661                 y : minY
32662             });
32663             
32664             return pos;
32665         }
32666         
32667         if(box[0].size == 'md-right'){
32668             pos.push({
32669                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32670                 y : minY + (this.unitWidth + this.gutter) * 1
32671             });
32672             
32673             return pos;
32674         }
32675         
32676         var rand = Math.floor(Math.random() * (4 - box[0].y));
32677         
32678         pos.push({
32679             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32680             y : minY + (this.unitWidth + this.gutter) * rand
32681         });
32682         
32683         return pos;
32684         
32685     },
32686     
32687     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32688     {
32689         var pos = [];
32690         
32691         if(box[0].size == 'xs'){
32692             
32693             pos.push({
32694                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32695                 y : minY
32696             });
32697
32698             pos.push({
32699                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32700                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32701             });
32702             
32703             return pos;
32704             
32705         }
32706         
32707         pos.push({
32708             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32709             y : minY
32710         });
32711
32712         pos.push({
32713             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32714             y : minY + (this.unitWidth + this.gutter) * 2
32715         });
32716         
32717         return pos;
32718         
32719     },
32720     
32721     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32722     {
32723         var pos = [];
32724         
32725         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32726             
32727             pos.push({
32728                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32729                 y : minY
32730             });
32731
32732             pos.push({
32733                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32734                 y : minY + (this.unitWidth + this.gutter) * 1
32735             });
32736             
32737             pos.push({
32738                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32739                 y : minY + (this.unitWidth + this.gutter) * 2
32740             });
32741             
32742             return pos;
32743             
32744         }
32745         
32746         if(box[0].size == 'xs' && box[1].size == 'xs'){
32747             
32748             pos.push({
32749                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32750                 y : minY
32751             });
32752
32753             pos.push({
32754                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32755                 y : minY
32756             });
32757             
32758             pos.push({
32759                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32760                 y : minY + (this.unitWidth + this.gutter) * 1
32761             });
32762             
32763             return pos;
32764             
32765         }
32766         
32767         pos.push({
32768             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32769             y : minY
32770         });
32771
32772         pos.push({
32773             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32774             y : minY + (this.unitWidth + this.gutter) * 2
32775         });
32776
32777         pos.push({
32778             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32779             y : minY + (this.unitWidth + this.gutter) * 2
32780         });
32781             
32782         return pos;
32783         
32784     },
32785     
32786     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32787     {
32788         var pos = [];
32789         
32790         if(box[0].size == 'xs'){
32791             
32792             pos.push({
32793                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32794                 y : minY
32795             });
32796
32797             pos.push({
32798                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32799                 y : minY
32800             });
32801             
32802             pos.push({
32803                 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),
32804                 y : minY
32805             });
32806             
32807             pos.push({
32808                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32809                 y : minY + (this.unitWidth + this.gutter) * 1
32810             });
32811             
32812             return pos;
32813             
32814         }
32815         
32816         pos.push({
32817             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32818             y : minY
32819         });
32820         
32821         pos.push({
32822             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32823             y : minY + (this.unitWidth + this.gutter) * 2
32824         });
32825         
32826         pos.push({
32827             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32828             y : minY + (this.unitWidth + this.gutter) * 2
32829         });
32830         
32831         pos.push({
32832             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),
32833             y : minY + (this.unitWidth + this.gutter) * 2
32834         });
32835
32836         return pos;
32837         
32838     },
32839     
32840     /**
32841     * remove a Masonry Brick
32842     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32843     */
32844     removeBrick : function(brick_id)
32845     {
32846         if (!brick_id) {
32847             return;
32848         }
32849         
32850         for (var i = 0; i<this.bricks.length; i++) {
32851             if (this.bricks[i].id == brick_id) {
32852                 this.bricks.splice(i,1);
32853                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32854                 this.initial();
32855             }
32856         }
32857     },
32858     
32859     /**
32860     * adds a Masonry Brick
32861     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32862     */
32863     addBrick : function(cfg)
32864     {
32865         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32866         //this.register(cn);
32867         cn.parentId = this.id;
32868         cn.render(this.el);
32869         return cn;
32870     },
32871     
32872     /**
32873     * register a Masonry Brick
32874     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32875     */
32876     
32877     register : function(brick)
32878     {
32879         this.bricks.push(brick);
32880         brick.masonryId = this.id;
32881     },
32882     
32883     /**
32884     * clear all the Masonry Brick
32885     */
32886     clearAll : function()
32887     {
32888         this.bricks = [];
32889         //this.getChildContainer().dom.innerHTML = "";
32890         this.el.dom.innerHTML = '';
32891     },
32892     
32893     getSelected : function()
32894     {
32895         if (!this.selectedBrick) {
32896             return false;
32897         }
32898         
32899         return this.selectedBrick;
32900     }
32901 });
32902
32903 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32904     
32905     groups: {},
32906      /**
32907     * register a Masonry Layout
32908     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32909     */
32910     
32911     register : function(layout)
32912     {
32913         this.groups[layout.id] = layout;
32914     },
32915     /**
32916     * fetch a  Masonry Layout based on the masonry layout ID
32917     * @param {string} the masonry layout to add
32918     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32919     */
32920     
32921     get: function(layout_id) {
32922         if (typeof(this.groups[layout_id]) == 'undefined') {
32923             return false;
32924         }
32925         return this.groups[layout_id] ;
32926     }
32927     
32928     
32929     
32930 });
32931
32932  
32933
32934  /**
32935  *
32936  * This is based on 
32937  * http://masonry.desandro.com
32938  *
32939  * The idea is to render all the bricks based on vertical width...
32940  *
32941  * The original code extends 'outlayer' - we might need to use that....
32942  * 
32943  */
32944
32945
32946 /**
32947  * @class Roo.bootstrap.LayoutMasonryAuto
32948  * @extends Roo.bootstrap.Component
32949  * Bootstrap Layout Masonry class
32950  * 
32951  * @constructor
32952  * Create a new Element
32953  * @param {Object} config The config object
32954  */
32955
32956 Roo.bootstrap.LayoutMasonryAuto = function(config){
32957     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32958 };
32959
32960 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32961     
32962       /**
32963      * @cfg {Boolean} isFitWidth  - resize the width..
32964      */   
32965     isFitWidth : false,  // options..
32966     /**
32967      * @cfg {Boolean} isOriginLeft = left align?
32968      */   
32969     isOriginLeft : true,
32970     /**
32971      * @cfg {Boolean} isOriginTop = top align?
32972      */   
32973     isOriginTop : false,
32974     /**
32975      * @cfg {Boolean} isLayoutInstant = no animation?
32976      */   
32977     isLayoutInstant : false, // needed?
32978     /**
32979      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32980      */   
32981     isResizingContainer : true,
32982     /**
32983      * @cfg {Number} columnWidth  width of the columns 
32984      */   
32985     
32986     columnWidth : 0,
32987     
32988     /**
32989      * @cfg {Number} maxCols maximum number of columns
32990      */   
32991     
32992     maxCols: 0,
32993     /**
32994      * @cfg {Number} padHeight padding below box..
32995      */   
32996     
32997     padHeight : 10, 
32998     
32999     /**
33000      * @cfg {Boolean} isAutoInitial defalut true
33001      */   
33002     
33003     isAutoInitial : true, 
33004     
33005     // private?
33006     gutter : 0,
33007     
33008     containerWidth: 0,
33009     initialColumnWidth : 0,
33010     currentSize : null,
33011     
33012     colYs : null, // array.
33013     maxY : 0,
33014     padWidth: 10,
33015     
33016     
33017     tag: 'div',
33018     cls: '',
33019     bricks: null, //CompositeElement
33020     cols : 0, // array?
33021     // element : null, // wrapped now this.el
33022     _isLayoutInited : null, 
33023     
33024     
33025     getAutoCreate : function(){
33026         
33027         var cfg = {
33028             tag: this.tag,
33029             cls: 'blog-masonary-wrapper ' + this.cls,
33030             cn : {
33031                 cls : 'mas-boxes masonary'
33032             }
33033         };
33034         
33035         return cfg;
33036     },
33037     
33038     getChildContainer: function( )
33039     {
33040         if (this.boxesEl) {
33041             return this.boxesEl;
33042         }
33043         
33044         this.boxesEl = this.el.select('.mas-boxes').first();
33045         
33046         return this.boxesEl;
33047     },
33048     
33049     
33050     initEvents : function()
33051     {
33052         var _this = this;
33053         
33054         if(this.isAutoInitial){
33055             Roo.log('hook children rendered');
33056             this.on('childrenrendered', function() {
33057                 Roo.log('children rendered');
33058                 _this.initial();
33059             } ,this);
33060         }
33061         
33062     },
33063     
33064     initial : function()
33065     {
33066         this.reloadItems();
33067
33068         this.currentSize = this.el.getBox(true);
33069
33070         /// was window resize... - let's see if this works..
33071         Roo.EventManager.onWindowResize(this.resize, this); 
33072
33073         if(!this.isAutoInitial){
33074             this.layout();
33075             return;
33076         }
33077         
33078         this.layout.defer(500,this);
33079     },
33080     
33081     reloadItems: function()
33082     {
33083         this.bricks = this.el.select('.masonry-brick', true);
33084         
33085         this.bricks.each(function(b) {
33086             //Roo.log(b.getSize());
33087             if (!b.attr('originalwidth')) {
33088                 b.attr('originalwidth',  b.getSize().width);
33089             }
33090             
33091         });
33092         
33093         Roo.log(this.bricks.elements.length);
33094     },
33095     
33096     resize : function()
33097     {
33098         Roo.log('resize');
33099         var cs = this.el.getBox(true);
33100         
33101         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
33102             Roo.log("no change in with or X");
33103             return;
33104         }
33105         this.currentSize = cs;
33106         this.layout();
33107     },
33108     
33109     layout : function()
33110     {
33111          Roo.log('layout');
33112         this._resetLayout();
33113         //this._manageStamps();
33114       
33115         // don't animate first layout
33116         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33117         this.layoutItems( isInstant );
33118       
33119         // flag for initalized
33120         this._isLayoutInited = true;
33121     },
33122     
33123     layoutItems : function( isInstant )
33124     {
33125         //var items = this._getItemsForLayout( this.items );
33126         // original code supports filtering layout items.. we just ignore it..
33127         
33128         this._layoutItems( this.bricks , isInstant );
33129       
33130         this._postLayout();
33131     },
33132     _layoutItems : function ( items , isInstant)
33133     {
33134        //this.fireEvent( 'layout', this, items );
33135     
33136
33137         if ( !items || !items.elements.length ) {
33138           // no items, emit event with empty array
33139             return;
33140         }
33141
33142         var queue = [];
33143         items.each(function(item) {
33144             Roo.log("layout item");
33145             Roo.log(item);
33146             // get x/y object from method
33147             var position = this._getItemLayoutPosition( item );
33148             // enqueue
33149             position.item = item;
33150             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
33151             queue.push( position );
33152         }, this);
33153       
33154         this._processLayoutQueue( queue );
33155     },
33156     /** Sets position of item in DOM
33157     * @param {Element} item
33158     * @param {Number} x - horizontal position
33159     * @param {Number} y - vertical position
33160     * @param {Boolean} isInstant - disables transitions
33161     */
33162     _processLayoutQueue : function( queue )
33163     {
33164         for ( var i=0, len = queue.length; i < len; i++ ) {
33165             var obj = queue[i];
33166             obj.item.position('absolute');
33167             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
33168         }
33169     },
33170       
33171     
33172     /**
33173     * Any logic you want to do after each layout,
33174     * i.e. size the container
33175     */
33176     _postLayout : function()
33177     {
33178         this.resizeContainer();
33179     },
33180     
33181     resizeContainer : function()
33182     {
33183         if ( !this.isResizingContainer ) {
33184             return;
33185         }
33186         var size = this._getContainerSize();
33187         if ( size ) {
33188             this.el.setSize(size.width,size.height);
33189             this.boxesEl.setSize(size.width,size.height);
33190         }
33191     },
33192     
33193     
33194     
33195     _resetLayout : function()
33196     {
33197         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
33198         this.colWidth = this.el.getWidth();
33199         //this.gutter = this.el.getWidth(); 
33200         
33201         this.measureColumns();
33202
33203         // reset column Y
33204         var i = this.cols;
33205         this.colYs = [];
33206         while (i--) {
33207             this.colYs.push( 0 );
33208         }
33209     
33210         this.maxY = 0;
33211     },
33212
33213     measureColumns : function()
33214     {
33215         this.getContainerWidth();
33216       // if columnWidth is 0, default to outerWidth of first item
33217         if ( !this.columnWidth ) {
33218             var firstItem = this.bricks.first();
33219             Roo.log(firstItem);
33220             this.columnWidth  = this.containerWidth;
33221             if (firstItem && firstItem.attr('originalwidth') ) {
33222                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
33223             }
33224             // columnWidth fall back to item of first element
33225             Roo.log("set column width?");
33226                         this.initialColumnWidth = this.columnWidth  ;
33227
33228             // if first elem has no width, default to size of container
33229             
33230         }
33231         
33232         
33233         if (this.initialColumnWidth) {
33234             this.columnWidth = this.initialColumnWidth;
33235         }
33236         
33237         
33238             
33239         // column width is fixed at the top - however if container width get's smaller we should
33240         // reduce it...
33241         
33242         // this bit calcs how man columns..
33243             
33244         var columnWidth = this.columnWidth += this.gutter;
33245       
33246         // calculate columns
33247         var containerWidth = this.containerWidth + this.gutter;
33248         
33249         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
33250         // fix rounding errors, typically with gutters
33251         var excess = columnWidth - containerWidth % columnWidth;
33252         
33253         
33254         // if overshoot is less than a pixel, round up, otherwise floor it
33255         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
33256         cols = Math[ mathMethod ]( cols );
33257         this.cols = Math.max( cols, 1 );
33258         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33259         
33260          // padding positioning..
33261         var totalColWidth = this.cols * this.columnWidth;
33262         var padavail = this.containerWidth - totalColWidth;
33263         // so for 2 columns - we need 3 'pads'
33264         
33265         var padNeeded = (1+this.cols) * this.padWidth;
33266         
33267         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
33268         
33269         this.columnWidth += padExtra
33270         //this.padWidth = Math.floor(padavail /  ( this.cols));
33271         
33272         // adjust colum width so that padding is fixed??
33273         
33274         // we have 3 columns ... total = width * 3
33275         // we have X left over... that should be used by 
33276         
33277         //if (this.expandC) {
33278             
33279         //}
33280         
33281         
33282         
33283     },
33284     
33285     getContainerWidth : function()
33286     {
33287        /* // container is parent if fit width
33288         var container = this.isFitWidth ? this.element.parentNode : this.element;
33289         // check that this.size and size are there
33290         // IE8 triggers resize on body size change, so they might not be
33291         
33292         var size = getSize( container );  //FIXME
33293         this.containerWidth = size && size.innerWidth; //FIXME
33294         */
33295          
33296         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33297         
33298     },
33299     
33300     _getItemLayoutPosition : function( item )  // what is item?
33301     {
33302         // we resize the item to our columnWidth..
33303       
33304         item.setWidth(this.columnWidth);
33305         item.autoBoxAdjust  = false;
33306         
33307         var sz = item.getSize();
33308  
33309         // how many columns does this brick span
33310         var remainder = this.containerWidth % this.columnWidth;
33311         
33312         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
33313         // round if off by 1 pixel, otherwise use ceil
33314         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
33315         colSpan = Math.min( colSpan, this.cols );
33316         
33317         // normally this should be '1' as we dont' currently allow multi width columns..
33318         
33319         var colGroup = this._getColGroup( colSpan );
33320         // get the minimum Y value from the columns
33321         var minimumY = Math.min.apply( Math, colGroup );
33322         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
33323         
33324         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
33325          
33326         // position the brick
33327         var position = {
33328             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
33329             y: this.currentSize.y + minimumY + this.padHeight
33330         };
33331         
33332         Roo.log(position);
33333         // apply setHeight to necessary columns
33334         var setHeight = minimumY + sz.height + this.padHeight;
33335         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
33336         
33337         var setSpan = this.cols + 1 - colGroup.length;
33338         for ( var i = 0; i < setSpan; i++ ) {
33339           this.colYs[ shortColIndex + i ] = setHeight ;
33340         }
33341       
33342         return position;
33343     },
33344     
33345     /**
33346      * @param {Number} colSpan - number of columns the element spans
33347      * @returns {Array} colGroup
33348      */
33349     _getColGroup : function( colSpan )
33350     {
33351         if ( colSpan < 2 ) {
33352           // if brick spans only one column, use all the column Ys
33353           return this.colYs;
33354         }
33355       
33356         var colGroup = [];
33357         // how many different places could this brick fit horizontally
33358         var groupCount = this.cols + 1 - colSpan;
33359         // for each group potential horizontal position
33360         for ( var i = 0; i < groupCount; i++ ) {
33361           // make an array of colY values for that one group
33362           var groupColYs = this.colYs.slice( i, i + colSpan );
33363           // and get the max value of the array
33364           colGroup[i] = Math.max.apply( Math, groupColYs );
33365         }
33366         return colGroup;
33367     },
33368     /*
33369     _manageStamp : function( stamp )
33370     {
33371         var stampSize =  stamp.getSize();
33372         var offset = stamp.getBox();
33373         // get the columns that this stamp affects
33374         var firstX = this.isOriginLeft ? offset.x : offset.right;
33375         var lastX = firstX + stampSize.width;
33376         var firstCol = Math.floor( firstX / this.columnWidth );
33377         firstCol = Math.max( 0, firstCol );
33378         
33379         var lastCol = Math.floor( lastX / this.columnWidth );
33380         // lastCol should not go over if multiple of columnWidth #425
33381         lastCol -= lastX % this.columnWidth ? 0 : 1;
33382         lastCol = Math.min( this.cols - 1, lastCol );
33383         
33384         // set colYs to bottom of the stamp
33385         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
33386             stampSize.height;
33387             
33388         for ( var i = firstCol; i <= lastCol; i++ ) {
33389           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33390         }
33391     },
33392     */
33393     
33394     _getContainerSize : function()
33395     {
33396         this.maxY = Math.max.apply( Math, this.colYs );
33397         var size = {
33398             height: this.maxY
33399         };
33400       
33401         if ( this.isFitWidth ) {
33402             size.width = this._getContainerFitWidth();
33403         }
33404       
33405         return size;
33406     },
33407     
33408     _getContainerFitWidth : function()
33409     {
33410         var unusedCols = 0;
33411         // count unused columns
33412         var i = this.cols;
33413         while ( --i ) {
33414           if ( this.colYs[i] !== 0 ) {
33415             break;
33416           }
33417           unusedCols++;
33418         }
33419         // fit container to columns that have been used
33420         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
33421     },
33422     
33423     needsResizeLayout : function()
33424     {
33425         var previousWidth = this.containerWidth;
33426         this.getContainerWidth();
33427         return previousWidth !== this.containerWidth;
33428     }
33429  
33430 });
33431
33432  
33433
33434  /*
33435  * - LGPL
33436  *
33437  * element
33438  * 
33439  */
33440
33441 /**
33442  * @class Roo.bootstrap.MasonryBrick
33443  * @extends Roo.bootstrap.Component
33444  * Bootstrap MasonryBrick class
33445  * 
33446  * @constructor
33447  * Create a new MasonryBrick
33448  * @param {Object} config The config object
33449  */
33450
33451 Roo.bootstrap.MasonryBrick = function(config){
33452     
33453     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
33454     
33455     Roo.bootstrap.MasonryBrick.register(this);
33456     
33457     this.addEvents({
33458         // raw events
33459         /**
33460          * @event click
33461          * When a MasonryBrick is clcik
33462          * @param {Roo.bootstrap.MasonryBrick} this
33463          * @param {Roo.EventObject} e
33464          */
33465         "click" : true
33466     });
33467 };
33468
33469 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
33470     
33471     /**
33472      * @cfg {String} title
33473      */   
33474     title : '',
33475     /**
33476      * @cfg {String} html
33477      */   
33478     html : '',
33479     /**
33480      * @cfg {String} bgimage
33481      */   
33482     bgimage : '',
33483     /**
33484      * @cfg {String} videourl
33485      */   
33486     videourl : '',
33487     /**
33488      * @cfg {String} cls
33489      */   
33490     cls : '',
33491     /**
33492      * @cfg {String} href
33493      */   
33494     href : '',
33495     /**
33496      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33497      */   
33498     size : 'xs',
33499     
33500     /**
33501      * @cfg {String} placetitle (center|bottom)
33502      */   
33503     placetitle : '',
33504     
33505     /**
33506      * @cfg {Boolean} isFitContainer defalut true
33507      */   
33508     isFitContainer : true, 
33509     
33510     /**
33511      * @cfg {Boolean} preventDefault defalut false
33512      */   
33513     preventDefault : false, 
33514     
33515     /**
33516      * @cfg {Boolean} inverse defalut false
33517      */   
33518     maskInverse : false, 
33519     
33520     getAutoCreate : function()
33521     {
33522         if(!this.isFitContainer){
33523             return this.getSplitAutoCreate();
33524         }
33525         
33526         var cls = 'masonry-brick masonry-brick-full';
33527         
33528         if(this.href.length){
33529             cls += ' masonry-brick-link';
33530         }
33531         
33532         if(this.bgimage.length){
33533             cls += ' masonry-brick-image';
33534         }
33535         
33536         if(this.maskInverse){
33537             cls += ' mask-inverse';
33538         }
33539         
33540         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33541             cls += ' enable-mask';
33542         }
33543         
33544         if(this.size){
33545             cls += ' masonry-' + this.size + '-brick';
33546         }
33547         
33548         if(this.placetitle.length){
33549             
33550             switch (this.placetitle) {
33551                 case 'center' :
33552                     cls += ' masonry-center-title';
33553                     break;
33554                 case 'bottom' :
33555                     cls += ' masonry-bottom-title';
33556                     break;
33557                 default:
33558                     break;
33559             }
33560             
33561         } else {
33562             if(!this.html.length && !this.bgimage.length){
33563                 cls += ' masonry-center-title';
33564             }
33565
33566             if(!this.html.length && this.bgimage.length){
33567                 cls += ' masonry-bottom-title';
33568             }
33569         }
33570         
33571         if(this.cls){
33572             cls += ' ' + this.cls;
33573         }
33574         
33575         var cfg = {
33576             tag: (this.href.length) ? 'a' : 'div',
33577             cls: cls,
33578             cn: [
33579                 {
33580                     tag: 'div',
33581                     cls: 'masonry-brick-mask'
33582                 },
33583                 {
33584                     tag: 'div',
33585                     cls: 'masonry-brick-paragraph',
33586                     cn: []
33587                 }
33588             ]
33589         };
33590         
33591         if(this.href.length){
33592             cfg.href = this.href;
33593         }
33594         
33595         var cn = cfg.cn[1].cn;
33596         
33597         if(this.title.length){
33598             cn.push({
33599                 tag: 'h4',
33600                 cls: 'masonry-brick-title',
33601                 html: this.title
33602             });
33603         }
33604         
33605         if(this.html.length){
33606             cn.push({
33607                 tag: 'p',
33608                 cls: 'masonry-brick-text',
33609                 html: this.html
33610             });
33611         }
33612         
33613         if (!this.title.length && !this.html.length) {
33614             cfg.cn[1].cls += ' hide';
33615         }
33616         
33617         if(this.bgimage.length){
33618             cfg.cn.push({
33619                 tag: 'img',
33620                 cls: 'masonry-brick-image-view',
33621                 src: this.bgimage
33622             });
33623         }
33624         
33625         if(this.videourl.length){
33626             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33627             // youtube support only?
33628             cfg.cn.push({
33629                 tag: 'iframe',
33630                 cls: 'masonry-brick-image-view',
33631                 src: vurl,
33632                 frameborder : 0,
33633                 allowfullscreen : true
33634             });
33635         }
33636         
33637         return cfg;
33638         
33639     },
33640     
33641     getSplitAutoCreate : function()
33642     {
33643         var cls = 'masonry-brick masonry-brick-split';
33644         
33645         if(this.href.length){
33646             cls += ' masonry-brick-link';
33647         }
33648         
33649         if(this.bgimage.length){
33650             cls += ' masonry-brick-image';
33651         }
33652         
33653         if(this.size){
33654             cls += ' masonry-' + this.size + '-brick';
33655         }
33656         
33657         switch (this.placetitle) {
33658             case 'center' :
33659                 cls += ' masonry-center-title';
33660                 break;
33661             case 'bottom' :
33662                 cls += ' masonry-bottom-title';
33663                 break;
33664             default:
33665                 if(!this.bgimage.length){
33666                     cls += ' masonry-center-title';
33667                 }
33668
33669                 if(this.bgimage.length){
33670                     cls += ' masonry-bottom-title';
33671                 }
33672                 break;
33673         }
33674         
33675         if(this.cls){
33676             cls += ' ' + this.cls;
33677         }
33678         
33679         var cfg = {
33680             tag: (this.href.length) ? 'a' : 'div',
33681             cls: cls,
33682             cn: [
33683                 {
33684                     tag: 'div',
33685                     cls: 'masonry-brick-split-head',
33686                     cn: [
33687                         {
33688                             tag: 'div',
33689                             cls: 'masonry-brick-paragraph',
33690                             cn: []
33691                         }
33692                     ]
33693                 },
33694                 {
33695                     tag: 'div',
33696                     cls: 'masonry-brick-split-body',
33697                     cn: []
33698                 }
33699             ]
33700         };
33701         
33702         if(this.href.length){
33703             cfg.href = this.href;
33704         }
33705         
33706         if(this.title.length){
33707             cfg.cn[0].cn[0].cn.push({
33708                 tag: 'h4',
33709                 cls: 'masonry-brick-title',
33710                 html: this.title
33711             });
33712         }
33713         
33714         if(this.html.length){
33715             cfg.cn[1].cn.push({
33716                 tag: 'p',
33717                 cls: 'masonry-brick-text',
33718                 html: this.html
33719             });
33720         }
33721
33722         if(this.bgimage.length){
33723             cfg.cn[0].cn.push({
33724                 tag: 'img',
33725                 cls: 'masonry-brick-image-view',
33726                 src: this.bgimage
33727             });
33728         }
33729         
33730         if(this.videourl.length){
33731             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33732             // youtube support only?
33733             cfg.cn[0].cn.cn.push({
33734                 tag: 'iframe',
33735                 cls: 'masonry-brick-image-view',
33736                 src: vurl,
33737                 frameborder : 0,
33738                 allowfullscreen : true
33739             });
33740         }
33741         
33742         return cfg;
33743     },
33744     
33745     initEvents: function() 
33746     {
33747         switch (this.size) {
33748             case 'xs' :
33749                 this.x = 1;
33750                 this.y = 1;
33751                 break;
33752             case 'sm' :
33753                 this.x = 2;
33754                 this.y = 2;
33755                 break;
33756             case 'md' :
33757             case 'md-left' :
33758             case 'md-right' :
33759                 this.x = 3;
33760                 this.y = 3;
33761                 break;
33762             case 'tall' :
33763                 this.x = 2;
33764                 this.y = 3;
33765                 break;
33766             case 'wide' :
33767                 this.x = 3;
33768                 this.y = 2;
33769                 break;
33770             case 'wide-thin' :
33771                 this.x = 3;
33772                 this.y = 1;
33773                 break;
33774                         
33775             default :
33776                 break;
33777         }
33778         
33779         if(Roo.isTouch){
33780             this.el.on('touchstart', this.onTouchStart, this);
33781             this.el.on('touchmove', this.onTouchMove, this);
33782             this.el.on('touchend', this.onTouchEnd, this);
33783             this.el.on('contextmenu', this.onContextMenu, this);
33784         } else {
33785             this.el.on('mouseenter'  ,this.enter, this);
33786             this.el.on('mouseleave', this.leave, this);
33787             this.el.on('click', this.onClick, this);
33788         }
33789         
33790         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33791             this.parent().bricks.push(this);   
33792         }
33793         
33794     },
33795     
33796     onClick: function(e, el)
33797     {
33798         var time = this.endTimer - this.startTimer;
33799         // Roo.log(e.preventDefault());
33800         if(Roo.isTouch){
33801             if(time > 1000){
33802                 e.preventDefault();
33803                 return;
33804             }
33805         }
33806         
33807         if(!this.preventDefault){
33808             return;
33809         }
33810         
33811         e.preventDefault();
33812         
33813         if (this.activeClass != '') {
33814             this.selectBrick();
33815         }
33816         
33817         this.fireEvent('click', this, e);
33818     },
33819     
33820     enter: function(e, el)
33821     {
33822         e.preventDefault();
33823         
33824         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33825             return;
33826         }
33827         
33828         if(this.bgimage.length && this.html.length){
33829             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33830         }
33831     },
33832     
33833     leave: function(e, el)
33834     {
33835         e.preventDefault();
33836         
33837         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33838             return;
33839         }
33840         
33841         if(this.bgimage.length && this.html.length){
33842             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33843         }
33844     },
33845     
33846     onTouchStart: function(e, el)
33847     {
33848 //        e.preventDefault();
33849         
33850         this.touchmoved = false;
33851         
33852         if(!this.isFitContainer){
33853             return;
33854         }
33855         
33856         if(!this.bgimage.length || !this.html.length){
33857             return;
33858         }
33859         
33860         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33861         
33862         this.timer = new Date().getTime();
33863         
33864     },
33865     
33866     onTouchMove: function(e, el)
33867     {
33868         this.touchmoved = true;
33869     },
33870     
33871     onContextMenu : function(e,el)
33872     {
33873         e.preventDefault();
33874         e.stopPropagation();
33875         return false;
33876     },
33877     
33878     onTouchEnd: function(e, el)
33879     {
33880 //        e.preventDefault();
33881         
33882         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33883         
33884             this.leave(e,el);
33885             
33886             return;
33887         }
33888         
33889         if(!this.bgimage.length || !this.html.length){
33890             
33891             if(this.href.length){
33892                 window.location.href = this.href;
33893             }
33894             
33895             return;
33896         }
33897         
33898         if(!this.isFitContainer){
33899             return;
33900         }
33901         
33902         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33903         
33904         window.location.href = this.href;
33905     },
33906     
33907     //selection on single brick only
33908     selectBrick : function() {
33909         
33910         if (!this.parentId) {
33911             return;
33912         }
33913         
33914         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33915         var index = m.selectedBrick.indexOf(this.id);
33916         
33917         if ( index > -1) {
33918             m.selectedBrick.splice(index,1);
33919             this.el.removeClass(this.activeClass);
33920             return;
33921         }
33922         
33923         for(var i = 0; i < m.selectedBrick.length; i++) {
33924             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33925             b.el.removeClass(b.activeClass);
33926         }
33927         
33928         m.selectedBrick = [];
33929         
33930         m.selectedBrick.push(this.id);
33931         this.el.addClass(this.activeClass);
33932         return;
33933     },
33934     
33935     isSelected : function(){
33936         return this.el.hasClass(this.activeClass);
33937         
33938     }
33939 });
33940
33941 Roo.apply(Roo.bootstrap.MasonryBrick, {
33942     
33943     //groups: {},
33944     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33945      /**
33946     * register a Masonry Brick
33947     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33948     */
33949     
33950     register : function(brick)
33951     {
33952         //this.groups[brick.id] = brick;
33953         this.groups.add(brick.id, brick);
33954     },
33955     /**
33956     * fetch a  masonry brick based on the masonry brick ID
33957     * @param {string} the masonry brick to add
33958     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33959     */
33960     
33961     get: function(brick_id) 
33962     {
33963         // if (typeof(this.groups[brick_id]) == 'undefined') {
33964         //     return false;
33965         // }
33966         // return this.groups[brick_id] ;
33967         
33968         if(this.groups.key(brick_id)) {
33969             return this.groups.key(brick_id);
33970         }
33971         
33972         return false;
33973     }
33974     
33975     
33976     
33977 });
33978
33979  /*
33980  * - LGPL
33981  *
33982  * element
33983  * 
33984  */
33985
33986 /**
33987  * @class Roo.bootstrap.Brick
33988  * @extends Roo.bootstrap.Component
33989  * Bootstrap Brick class
33990  * 
33991  * @constructor
33992  * Create a new Brick
33993  * @param {Object} config The config object
33994  */
33995
33996 Roo.bootstrap.Brick = function(config){
33997     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33998     
33999     this.addEvents({
34000         // raw events
34001         /**
34002          * @event click
34003          * When a Brick is click
34004          * @param {Roo.bootstrap.Brick} this
34005          * @param {Roo.EventObject} e
34006          */
34007         "click" : true
34008     });
34009 };
34010
34011 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
34012     
34013     /**
34014      * @cfg {String} title
34015      */   
34016     title : '',
34017     /**
34018      * @cfg {String} html
34019      */   
34020     html : '',
34021     /**
34022      * @cfg {String} bgimage
34023      */   
34024     bgimage : '',
34025     /**
34026      * @cfg {String} cls
34027      */   
34028     cls : '',
34029     /**
34030      * @cfg {String} href
34031      */   
34032     href : '',
34033     /**
34034      * @cfg {String} video
34035      */   
34036     video : '',
34037     /**
34038      * @cfg {Boolean} square
34039      */   
34040     square : true,
34041     
34042     getAutoCreate : function()
34043     {
34044         var cls = 'roo-brick';
34045         
34046         if(this.href.length){
34047             cls += ' roo-brick-link';
34048         }
34049         
34050         if(this.bgimage.length){
34051             cls += ' roo-brick-image';
34052         }
34053         
34054         if(!this.html.length && !this.bgimage.length){
34055             cls += ' roo-brick-center-title';
34056         }
34057         
34058         if(!this.html.length && this.bgimage.length){
34059             cls += ' roo-brick-bottom-title';
34060         }
34061         
34062         if(this.cls){
34063             cls += ' ' + this.cls;
34064         }
34065         
34066         var cfg = {
34067             tag: (this.href.length) ? 'a' : 'div',
34068             cls: cls,
34069             cn: [
34070                 {
34071                     tag: 'div',
34072                     cls: 'roo-brick-paragraph',
34073                     cn: []
34074                 }
34075             ]
34076         };
34077         
34078         if(this.href.length){
34079             cfg.href = this.href;
34080         }
34081         
34082         var cn = cfg.cn[0].cn;
34083         
34084         if(this.title.length){
34085             cn.push({
34086                 tag: 'h4',
34087                 cls: 'roo-brick-title',
34088                 html: this.title
34089             });
34090         }
34091         
34092         if(this.html.length){
34093             cn.push({
34094                 tag: 'p',
34095                 cls: 'roo-brick-text',
34096                 html: this.html
34097             });
34098         } else {
34099             cn.cls += ' hide';
34100         }
34101         
34102         if(this.bgimage.length){
34103             cfg.cn.push({
34104                 tag: 'img',
34105                 cls: 'roo-brick-image-view',
34106                 src: this.bgimage
34107             });
34108         }
34109         
34110         return cfg;
34111     },
34112     
34113     initEvents: function() 
34114     {
34115         if(this.title.length || this.html.length){
34116             this.el.on('mouseenter'  ,this.enter, this);
34117             this.el.on('mouseleave', this.leave, this);
34118         }
34119         
34120         Roo.EventManager.onWindowResize(this.resize, this); 
34121         
34122         if(this.bgimage.length){
34123             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
34124             this.imageEl.on('load', this.onImageLoad, this);
34125             return;
34126         }
34127         
34128         this.resize();
34129     },
34130     
34131     onImageLoad : function()
34132     {
34133         this.resize();
34134     },
34135     
34136     resize : function()
34137     {
34138         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
34139         
34140         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
34141         
34142         if(this.bgimage.length){
34143             var image = this.el.select('.roo-brick-image-view', true).first();
34144             
34145             image.setWidth(paragraph.getWidth());
34146             
34147             if(this.square){
34148                 image.setHeight(paragraph.getWidth());
34149             }
34150             
34151             this.el.setHeight(image.getHeight());
34152             paragraph.setHeight(image.getHeight());
34153             
34154         }
34155         
34156     },
34157     
34158     enter: function(e, el)
34159     {
34160         e.preventDefault();
34161         
34162         if(this.bgimage.length){
34163             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
34164             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
34165         }
34166     },
34167     
34168     leave: function(e, el)
34169     {
34170         e.preventDefault();
34171         
34172         if(this.bgimage.length){
34173             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
34174             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
34175         }
34176     }
34177     
34178 });
34179
34180  
34181
34182  /*
34183  * - LGPL
34184  *
34185  * Number field 
34186  */
34187
34188 /**
34189  * @class Roo.bootstrap.NumberField
34190  * @extends Roo.bootstrap.Input
34191  * Bootstrap NumberField class
34192  * 
34193  * 
34194  * 
34195  * 
34196  * @constructor
34197  * Create a new NumberField
34198  * @param {Object} config The config object
34199  */
34200
34201 Roo.bootstrap.NumberField = function(config){
34202     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
34203 };
34204
34205 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
34206     
34207     /**
34208      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
34209      */
34210     allowDecimals : true,
34211     /**
34212      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
34213      */
34214     decimalSeparator : ".",
34215     /**
34216      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
34217      */
34218     decimalPrecision : 2,
34219     /**
34220      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
34221      */
34222     allowNegative : true,
34223     
34224     /**
34225      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
34226      */
34227     allowZero: true,
34228     /**
34229      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
34230      */
34231     minValue : Number.NEGATIVE_INFINITY,
34232     /**
34233      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
34234      */
34235     maxValue : Number.MAX_VALUE,
34236     /**
34237      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
34238      */
34239     minText : "The minimum value for this field is {0}",
34240     /**
34241      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
34242      */
34243     maxText : "The maximum value for this field is {0}",
34244     /**
34245      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
34246      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
34247      */
34248     nanText : "{0} is not a valid number",
34249     /**
34250      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
34251      */
34252     thousandsDelimiter : false,
34253     /**
34254      * @cfg {String} valueAlign alignment of value
34255      */
34256     valueAlign : "left",
34257
34258     getAutoCreate : function()
34259     {
34260         var hiddenInput = {
34261             tag: 'input',
34262             type: 'hidden',
34263             id: Roo.id(),
34264             cls: 'hidden-number-input'
34265         };
34266         
34267         if (this.name) {
34268             hiddenInput.name = this.name;
34269         }
34270         
34271         this.name = '';
34272         
34273         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
34274         
34275         this.name = hiddenInput.name;
34276         
34277         if(cfg.cn.length > 0) {
34278             cfg.cn.push(hiddenInput);
34279         }
34280         
34281         return cfg;
34282     },
34283
34284     // private
34285     initEvents : function()
34286     {   
34287         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
34288         
34289         var allowed = "0123456789";
34290         
34291         if(this.allowDecimals){
34292             allowed += this.decimalSeparator;
34293         }
34294         
34295         if(this.allowNegative){
34296             allowed += "-";
34297         }
34298         
34299         if(this.thousandsDelimiter) {
34300             allowed += ",";
34301         }
34302         
34303         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
34304         
34305         var keyPress = function(e){
34306             
34307             var k = e.getKey();
34308             
34309             var c = e.getCharCode();
34310             
34311             if(
34312                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
34313                     allowed.indexOf(String.fromCharCode(c)) === -1
34314             ){
34315                 e.stopEvent();
34316                 return;
34317             }
34318             
34319             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
34320                 return;
34321             }
34322             
34323             if(allowed.indexOf(String.fromCharCode(c)) === -1){
34324                 e.stopEvent();
34325             }
34326         };
34327         
34328         this.el.on("keypress", keyPress, this);
34329     },
34330     
34331     validateValue : function(value)
34332     {
34333         
34334         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
34335             return false;
34336         }
34337         
34338         var num = this.parseValue(value);
34339         
34340         if(isNaN(num)){
34341             this.markInvalid(String.format(this.nanText, value));
34342             return false;
34343         }
34344         
34345         if(num < this.minValue){
34346             this.markInvalid(String.format(this.minText, this.minValue));
34347             return false;
34348         }
34349         
34350         if(num > this.maxValue){
34351             this.markInvalid(String.format(this.maxText, this.maxValue));
34352             return false;
34353         }
34354         
34355         return true;
34356     },
34357
34358     getValue : function()
34359     {
34360         var v = this.hiddenEl().getValue();
34361         
34362         return this.fixPrecision(this.parseValue(v));
34363     },
34364
34365     parseValue : function(value)
34366     {
34367         if(this.thousandsDelimiter) {
34368             value += "";
34369             r = new RegExp(",", "g");
34370             value = value.replace(r, "");
34371         }
34372         
34373         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
34374         return isNaN(value) ? '' : value;
34375     },
34376
34377     fixPrecision : function(value)
34378     {
34379         if(this.thousandsDelimiter) {
34380             value += "";
34381             r = new RegExp(",", "g");
34382             value = value.replace(r, "");
34383         }
34384         
34385         var nan = isNaN(value);
34386         
34387         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34388             return nan ? '' : value;
34389         }
34390         return parseFloat(value).toFixed(this.decimalPrecision);
34391     },
34392
34393     setValue : function(v)
34394     {
34395         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34396         
34397         this.value = v;
34398         
34399         if(this.rendered){
34400             
34401             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34402             
34403             this.inputEl().dom.value = (v == '') ? '' :
34404                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34405             
34406             if(!this.allowZero && v === '0') {
34407                 this.hiddenEl().dom.value = '';
34408                 this.inputEl().dom.value = '';
34409             }
34410             
34411             this.validate();
34412         }
34413     },
34414
34415     decimalPrecisionFcn : function(v)
34416     {
34417         return Math.floor(v);
34418     },
34419
34420     beforeBlur : function()
34421     {
34422         var v = this.parseValue(this.getRawValue());
34423         
34424         if(v || v === 0 || v === ''){
34425             this.setValue(v);
34426         }
34427     },
34428     
34429     hiddenEl : function()
34430     {
34431         return this.el.select('input.hidden-number-input',true).first();
34432     }
34433     
34434 });
34435
34436  
34437
34438 /*
34439 * Licence: LGPL
34440 */
34441
34442 /**
34443  * @class Roo.bootstrap.DocumentSlider
34444  * @extends Roo.bootstrap.Component
34445  * Bootstrap DocumentSlider class
34446  * 
34447  * @constructor
34448  * Create a new DocumentViewer
34449  * @param {Object} config The config object
34450  */
34451
34452 Roo.bootstrap.DocumentSlider = function(config){
34453     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
34454     
34455     this.files = [];
34456     
34457     this.addEvents({
34458         /**
34459          * @event initial
34460          * Fire after initEvent
34461          * @param {Roo.bootstrap.DocumentSlider} this
34462          */
34463         "initial" : true,
34464         /**
34465          * @event update
34466          * Fire after update
34467          * @param {Roo.bootstrap.DocumentSlider} this
34468          */
34469         "update" : true,
34470         /**
34471          * @event click
34472          * Fire after click
34473          * @param {Roo.bootstrap.DocumentSlider} this
34474          */
34475         "click" : true
34476     });
34477 };
34478
34479 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
34480     
34481     files : false,
34482     
34483     indicator : 0,
34484     
34485     getAutoCreate : function()
34486     {
34487         var cfg = {
34488             tag : 'div',
34489             cls : 'roo-document-slider',
34490             cn : [
34491                 {
34492                     tag : 'div',
34493                     cls : 'roo-document-slider-header',
34494                     cn : [
34495                         {
34496                             tag : 'div',
34497                             cls : 'roo-document-slider-header-title'
34498                         }
34499                     ]
34500                 },
34501                 {
34502                     tag : 'div',
34503                     cls : 'roo-document-slider-body',
34504                     cn : [
34505                         {
34506                             tag : 'div',
34507                             cls : 'roo-document-slider-prev',
34508                             cn : [
34509                                 {
34510                                     tag : 'i',
34511                                     cls : 'fa fa-chevron-left'
34512                                 }
34513                             ]
34514                         },
34515                         {
34516                             tag : 'div',
34517                             cls : 'roo-document-slider-thumb',
34518                             cn : [
34519                                 {
34520                                     tag : 'img',
34521                                     cls : 'roo-document-slider-image'
34522                                 }
34523                             ]
34524                         },
34525                         {
34526                             tag : 'div',
34527                             cls : 'roo-document-slider-next',
34528                             cn : [
34529                                 {
34530                                     tag : 'i',
34531                                     cls : 'fa fa-chevron-right'
34532                                 }
34533                             ]
34534                         }
34535                     ]
34536                 }
34537             ]
34538         };
34539         
34540         return cfg;
34541     },
34542     
34543     initEvents : function()
34544     {
34545         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34546         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34547         
34548         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34549         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34550         
34551         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34552         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34553         
34554         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34555         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34556         
34557         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34558         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34559         
34560         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34561         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34562         
34563         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34564         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34565         
34566         this.thumbEl.on('click', this.onClick, this);
34567         
34568         this.prevIndicator.on('click', this.prev, this);
34569         
34570         this.nextIndicator.on('click', this.next, this);
34571         
34572     },
34573     
34574     initial : function()
34575     {
34576         if(this.files.length){
34577             this.indicator = 1;
34578             this.update()
34579         }
34580         
34581         this.fireEvent('initial', this);
34582     },
34583     
34584     update : function()
34585     {
34586         this.imageEl.attr('src', this.files[this.indicator - 1]);
34587         
34588         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34589         
34590         this.prevIndicator.show();
34591         
34592         if(this.indicator == 1){
34593             this.prevIndicator.hide();
34594         }
34595         
34596         this.nextIndicator.show();
34597         
34598         if(this.indicator == this.files.length){
34599             this.nextIndicator.hide();
34600         }
34601         
34602         this.thumbEl.scrollTo('top');
34603         
34604         this.fireEvent('update', this);
34605     },
34606     
34607     onClick : function(e)
34608     {
34609         e.preventDefault();
34610         
34611         this.fireEvent('click', this);
34612     },
34613     
34614     prev : function(e)
34615     {
34616         e.preventDefault();
34617         
34618         this.indicator = Math.max(1, this.indicator - 1);
34619         
34620         this.update();
34621     },
34622     
34623     next : function(e)
34624     {
34625         e.preventDefault();
34626         
34627         this.indicator = Math.min(this.files.length, this.indicator + 1);
34628         
34629         this.update();
34630     }
34631 });
34632 /*
34633  * - LGPL
34634  *
34635  * RadioSet
34636  *
34637  *
34638  */
34639
34640 /**
34641  * @class Roo.bootstrap.RadioSet
34642  * @extends Roo.bootstrap.Input
34643  * Bootstrap RadioSet class
34644  * @cfg {String} indicatorpos (left|right) default left
34645  * @cfg {Boolean} inline (true|false) inline the element (default true)
34646  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34647  * @constructor
34648  * Create a new RadioSet
34649  * @param {Object} config The config object
34650  */
34651
34652 Roo.bootstrap.RadioSet = function(config){
34653     
34654     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34655     
34656     this.radioes = [];
34657     
34658     Roo.bootstrap.RadioSet.register(this);
34659     
34660     this.addEvents({
34661         /**
34662         * @event check
34663         * Fires when the element is checked or unchecked.
34664         * @param {Roo.bootstrap.RadioSet} this This radio
34665         * @param {Roo.bootstrap.Radio} item The checked item
34666         */
34667        check : true,
34668        /**
34669         * @event click
34670         * Fires when the element is click.
34671         * @param {Roo.bootstrap.RadioSet} this This radio set
34672         * @param {Roo.bootstrap.Radio} item The checked item
34673         * @param {Roo.EventObject} e The event object
34674         */
34675        click : true
34676     });
34677     
34678 };
34679
34680 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34681
34682     radioes : false,
34683     
34684     inline : true,
34685     
34686     weight : '',
34687     
34688     indicatorpos : 'left',
34689     
34690     getAutoCreate : function()
34691     {
34692         var label = {
34693             tag : 'label',
34694             cls : 'roo-radio-set-label',
34695             cn : [
34696                 {
34697                     tag : 'span',
34698                     html : this.fieldLabel
34699                 }
34700             ]
34701         };
34702         if (Roo.bootstrap.version == 3) {
34703             
34704             
34705             if(this.indicatorpos == 'left'){
34706                 label.cn.unshift({
34707                     tag : 'i',
34708                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34709                     tooltip : 'This field is required'
34710                 });
34711             } else {
34712                 label.cn.push({
34713                     tag : 'i',
34714                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34715                     tooltip : 'This field is required'
34716                 });
34717             }
34718         }
34719         var items = {
34720             tag : 'div',
34721             cls : 'roo-radio-set-items'
34722         };
34723         
34724         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34725         
34726         if (align === 'left' && this.fieldLabel.length) {
34727             
34728             items = {
34729                 cls : "roo-radio-set-right", 
34730                 cn: [
34731                     items
34732                 ]
34733             };
34734             
34735             if(this.labelWidth > 12){
34736                 label.style = "width: " + this.labelWidth + 'px';
34737             }
34738             
34739             if(this.labelWidth < 13 && this.labelmd == 0){
34740                 this.labelmd = this.labelWidth;
34741             }
34742             
34743             if(this.labellg > 0){
34744                 label.cls += ' col-lg-' + this.labellg;
34745                 items.cls += ' col-lg-' + (12 - this.labellg);
34746             }
34747             
34748             if(this.labelmd > 0){
34749                 label.cls += ' col-md-' + this.labelmd;
34750                 items.cls += ' col-md-' + (12 - this.labelmd);
34751             }
34752             
34753             if(this.labelsm > 0){
34754                 label.cls += ' col-sm-' + this.labelsm;
34755                 items.cls += ' col-sm-' + (12 - this.labelsm);
34756             }
34757             
34758             if(this.labelxs > 0){
34759                 label.cls += ' col-xs-' + this.labelxs;
34760                 items.cls += ' col-xs-' + (12 - this.labelxs);
34761             }
34762         }
34763         
34764         var cfg = {
34765             tag : 'div',
34766             cls : 'roo-radio-set',
34767             cn : [
34768                 {
34769                     tag : 'input',
34770                     cls : 'roo-radio-set-input',
34771                     type : 'hidden',
34772                     name : this.name,
34773                     value : this.value ? this.value :  ''
34774                 },
34775                 label,
34776                 items
34777             ]
34778         };
34779         
34780         if(this.weight.length){
34781             cfg.cls += ' roo-radio-' + this.weight;
34782         }
34783         
34784         if(this.inline) {
34785             cfg.cls += ' roo-radio-set-inline';
34786         }
34787         
34788         var settings=this;
34789         ['xs','sm','md','lg'].map(function(size){
34790             if (settings[size]) {
34791                 cfg.cls += ' col-' + size + '-' + settings[size];
34792             }
34793         });
34794         
34795         return cfg;
34796         
34797     },
34798
34799     initEvents : function()
34800     {
34801         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34802         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34803         
34804         if(!this.fieldLabel.length){
34805             this.labelEl.hide();
34806         }
34807         
34808         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34809         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34810         
34811         this.indicator = this.indicatorEl();
34812         
34813         if(this.indicator){
34814             this.indicator.addClass('invisible');
34815         }
34816         
34817         this.originalValue = this.getValue();
34818         
34819     },
34820     
34821     inputEl: function ()
34822     {
34823         return this.el.select('.roo-radio-set-input', true).first();
34824     },
34825     
34826     getChildContainer : function()
34827     {
34828         return this.itemsEl;
34829     },
34830     
34831     register : function(item)
34832     {
34833         this.radioes.push(item);
34834         
34835     },
34836     
34837     validate : function()
34838     {   
34839         if(this.getVisibilityEl().hasClass('hidden')){
34840             return true;
34841         }
34842         
34843         var valid = false;
34844         
34845         Roo.each(this.radioes, function(i){
34846             if(!i.checked){
34847                 return;
34848             }
34849             
34850             valid = true;
34851             return false;
34852         });
34853         
34854         if(this.allowBlank) {
34855             return true;
34856         }
34857         
34858         if(this.disabled || valid){
34859             this.markValid();
34860             return true;
34861         }
34862         
34863         this.markInvalid();
34864         return false;
34865         
34866     },
34867     
34868     markValid : function()
34869     {
34870         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34871             this.indicatorEl().removeClass('visible');
34872             this.indicatorEl().addClass('invisible');
34873         }
34874         
34875         
34876         if (Roo.bootstrap.version == 3) {
34877             this.el.removeClass([this.invalidClass, this.validClass]);
34878             this.el.addClass(this.validClass);
34879         } else {
34880             this.el.removeClass(['is-invalid','is-valid']);
34881             this.el.addClass(['is-valid']);
34882         }
34883         this.fireEvent('valid', this);
34884     },
34885     
34886     markInvalid : function(msg)
34887     {
34888         if(this.allowBlank || this.disabled){
34889             return;
34890         }
34891         
34892         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34893             this.indicatorEl().removeClass('invisible');
34894             this.indicatorEl().addClass('visible');
34895         }
34896         if (Roo.bootstrap.version == 3) {
34897             this.el.removeClass([this.invalidClass, this.validClass]);
34898             this.el.addClass(this.invalidClass);
34899         } else {
34900             this.el.removeClass(['is-invalid','is-valid']);
34901             this.el.addClass(['is-invalid']);
34902         }
34903         
34904         this.fireEvent('invalid', this, msg);
34905         
34906     },
34907     
34908     setValue : function(v, suppressEvent)
34909     {   
34910         if(this.value === v){
34911             return;
34912         }
34913         
34914         this.value = v;
34915         
34916         if(this.rendered){
34917             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34918         }
34919         
34920         Roo.each(this.radioes, function(i){
34921             i.checked = false;
34922             i.el.removeClass('checked');
34923         });
34924         
34925         Roo.each(this.radioes, function(i){
34926             
34927             if(i.value === v || i.value.toString() === v.toString()){
34928                 i.checked = true;
34929                 i.el.addClass('checked');
34930                 
34931                 if(suppressEvent !== true){
34932                     this.fireEvent('check', this, i);
34933                 }
34934                 
34935                 return false;
34936             }
34937             
34938         }, this);
34939         
34940         this.validate();
34941     },
34942     
34943     clearInvalid : function(){
34944         
34945         if(!this.el || this.preventMark){
34946             return;
34947         }
34948         
34949         this.el.removeClass([this.invalidClass]);
34950         
34951         this.fireEvent('valid', this);
34952     }
34953     
34954 });
34955
34956 Roo.apply(Roo.bootstrap.RadioSet, {
34957     
34958     groups: {},
34959     
34960     register : function(set)
34961     {
34962         this.groups[set.name] = set;
34963     },
34964     
34965     get: function(name) 
34966     {
34967         if (typeof(this.groups[name]) == 'undefined') {
34968             return false;
34969         }
34970         
34971         return this.groups[name] ;
34972     }
34973     
34974 });
34975 /*
34976  * Based on:
34977  * Ext JS Library 1.1.1
34978  * Copyright(c) 2006-2007, Ext JS, LLC.
34979  *
34980  * Originally Released Under LGPL - original licence link has changed is not relivant.
34981  *
34982  * Fork - LGPL
34983  * <script type="text/javascript">
34984  */
34985
34986
34987 /**
34988  * @class Roo.bootstrap.SplitBar
34989  * @extends Roo.util.Observable
34990  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34991  * <br><br>
34992  * Usage:
34993  * <pre><code>
34994 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34995                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34996 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34997 split.minSize = 100;
34998 split.maxSize = 600;
34999 split.animate = true;
35000 split.on('moved', splitterMoved);
35001 </code></pre>
35002  * @constructor
35003  * Create a new SplitBar
35004  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
35005  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
35006  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
35007  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
35008                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
35009                         position of the SplitBar).
35010  */
35011 Roo.bootstrap.SplitBar = function(cfg){
35012     
35013     /** @private */
35014     
35015     //{
35016     //  dragElement : elm
35017     //  resizingElement: el,
35018         // optional..
35019     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
35020     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
35021         // existingProxy ???
35022     //}
35023     
35024     this.el = Roo.get(cfg.dragElement, true);
35025     this.el.dom.unselectable = "on";
35026     /** @private */
35027     this.resizingEl = Roo.get(cfg.resizingElement, true);
35028
35029     /**
35030      * @private
35031      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
35032      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
35033      * @type Number
35034      */
35035     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
35036     
35037     /**
35038      * The minimum size of the resizing element. (Defaults to 0)
35039      * @type Number
35040      */
35041     this.minSize = 0;
35042     
35043     /**
35044      * The maximum size of the resizing element. (Defaults to 2000)
35045      * @type Number
35046      */
35047     this.maxSize = 2000;
35048     
35049     /**
35050      * Whether to animate the transition to the new size
35051      * @type Boolean
35052      */
35053     this.animate = false;
35054     
35055     /**
35056      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
35057      * @type Boolean
35058      */
35059     this.useShim = false;
35060     
35061     /** @private */
35062     this.shim = null;
35063     
35064     if(!cfg.existingProxy){
35065         /** @private */
35066         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
35067     }else{
35068         this.proxy = Roo.get(cfg.existingProxy).dom;
35069     }
35070     /** @private */
35071     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
35072     
35073     /** @private */
35074     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
35075     
35076     /** @private */
35077     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
35078     
35079     /** @private */
35080     this.dragSpecs = {};
35081     
35082     /**
35083      * @private The adapter to use to positon and resize elements
35084      */
35085     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35086     this.adapter.init(this);
35087     
35088     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35089         /** @private */
35090         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
35091         this.el.addClass("roo-splitbar-h");
35092     }else{
35093         /** @private */
35094         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
35095         this.el.addClass("roo-splitbar-v");
35096     }
35097     
35098     this.addEvents({
35099         /**
35100          * @event resize
35101          * Fires when the splitter is moved (alias for {@link #event-moved})
35102          * @param {Roo.bootstrap.SplitBar} this
35103          * @param {Number} newSize the new width or height
35104          */
35105         "resize" : true,
35106         /**
35107          * @event moved
35108          * Fires when the splitter is moved
35109          * @param {Roo.bootstrap.SplitBar} this
35110          * @param {Number} newSize the new width or height
35111          */
35112         "moved" : true,
35113         /**
35114          * @event beforeresize
35115          * Fires before the splitter is dragged
35116          * @param {Roo.bootstrap.SplitBar} this
35117          */
35118         "beforeresize" : true,
35119
35120         "beforeapply" : true
35121     });
35122
35123     Roo.util.Observable.call(this);
35124 };
35125
35126 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
35127     onStartProxyDrag : function(x, y){
35128         this.fireEvent("beforeresize", this);
35129         if(!this.overlay){
35130             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
35131             o.unselectable();
35132             o.enableDisplayMode("block");
35133             // all splitbars share the same overlay
35134             Roo.bootstrap.SplitBar.prototype.overlay = o;
35135         }
35136         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
35137         this.overlay.show();
35138         Roo.get(this.proxy).setDisplayed("block");
35139         var size = this.adapter.getElementSize(this);
35140         this.activeMinSize = this.getMinimumSize();;
35141         this.activeMaxSize = this.getMaximumSize();;
35142         var c1 = size - this.activeMinSize;
35143         var c2 = Math.max(this.activeMaxSize - size, 0);
35144         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35145             this.dd.resetConstraints();
35146             this.dd.setXConstraint(
35147                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
35148                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
35149             );
35150             this.dd.setYConstraint(0, 0);
35151         }else{
35152             this.dd.resetConstraints();
35153             this.dd.setXConstraint(0, 0);
35154             this.dd.setYConstraint(
35155                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
35156                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
35157             );
35158          }
35159         this.dragSpecs.startSize = size;
35160         this.dragSpecs.startPoint = [x, y];
35161         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
35162     },
35163     
35164     /** 
35165      * @private Called after the drag operation by the DDProxy
35166      */
35167     onEndProxyDrag : function(e){
35168         Roo.get(this.proxy).setDisplayed(false);
35169         var endPoint = Roo.lib.Event.getXY(e);
35170         if(this.overlay){
35171             this.overlay.hide();
35172         }
35173         var newSize;
35174         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35175             newSize = this.dragSpecs.startSize + 
35176                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
35177                     endPoint[0] - this.dragSpecs.startPoint[0] :
35178                     this.dragSpecs.startPoint[0] - endPoint[0]
35179                 );
35180         }else{
35181             newSize = this.dragSpecs.startSize + 
35182                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
35183                     endPoint[1] - this.dragSpecs.startPoint[1] :
35184                     this.dragSpecs.startPoint[1] - endPoint[1]
35185                 );
35186         }
35187         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
35188         if(newSize != this.dragSpecs.startSize){
35189             if(this.fireEvent('beforeapply', this, newSize) !== false){
35190                 this.adapter.setElementSize(this, newSize);
35191                 this.fireEvent("moved", this, newSize);
35192                 this.fireEvent("resize", this, newSize);
35193             }
35194         }
35195     },
35196     
35197     /**
35198      * Get the adapter this SplitBar uses
35199      * @return The adapter object
35200      */
35201     getAdapter : function(){
35202         return this.adapter;
35203     },
35204     
35205     /**
35206      * Set the adapter this SplitBar uses
35207      * @param {Object} adapter A SplitBar adapter object
35208      */
35209     setAdapter : function(adapter){
35210         this.adapter = adapter;
35211         this.adapter.init(this);
35212     },
35213     
35214     /**
35215      * Gets the minimum size for the resizing element
35216      * @return {Number} The minimum size
35217      */
35218     getMinimumSize : function(){
35219         return this.minSize;
35220     },
35221     
35222     /**
35223      * Sets the minimum size for the resizing element
35224      * @param {Number} minSize The minimum size
35225      */
35226     setMinimumSize : function(minSize){
35227         this.minSize = minSize;
35228     },
35229     
35230     /**
35231      * Gets the maximum size for the resizing element
35232      * @return {Number} The maximum size
35233      */
35234     getMaximumSize : function(){
35235         return this.maxSize;
35236     },
35237     
35238     /**
35239      * Sets the maximum size for the resizing element
35240      * @param {Number} maxSize The maximum size
35241      */
35242     setMaximumSize : function(maxSize){
35243         this.maxSize = maxSize;
35244     },
35245     
35246     /**
35247      * Sets the initialize size for the resizing element
35248      * @param {Number} size The initial size
35249      */
35250     setCurrentSize : function(size){
35251         var oldAnimate = this.animate;
35252         this.animate = false;
35253         this.adapter.setElementSize(this, size);
35254         this.animate = oldAnimate;
35255     },
35256     
35257     /**
35258      * Destroy this splitbar. 
35259      * @param {Boolean} removeEl True to remove the element
35260      */
35261     destroy : function(removeEl){
35262         if(this.shim){
35263             this.shim.remove();
35264         }
35265         this.dd.unreg();
35266         this.proxy.parentNode.removeChild(this.proxy);
35267         if(removeEl){
35268             this.el.remove();
35269         }
35270     }
35271 });
35272
35273 /**
35274  * @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.
35275  */
35276 Roo.bootstrap.SplitBar.createProxy = function(dir){
35277     var proxy = new Roo.Element(document.createElement("div"));
35278     proxy.unselectable();
35279     var cls = 'roo-splitbar-proxy';
35280     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
35281     document.body.appendChild(proxy.dom);
35282     return proxy.dom;
35283 };
35284
35285 /** 
35286  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
35287  * Default Adapter. It assumes the splitter and resizing element are not positioned
35288  * elements and only gets/sets the width of the element. Generally used for table based layouts.
35289  */
35290 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
35291 };
35292
35293 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
35294     // do nothing for now
35295     init : function(s){
35296     
35297     },
35298     /**
35299      * Called before drag operations to get the current size of the resizing element. 
35300      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35301      */
35302      getElementSize : function(s){
35303         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35304             return s.resizingEl.getWidth();
35305         }else{
35306             return s.resizingEl.getHeight();
35307         }
35308     },
35309     
35310     /**
35311      * Called after drag operations to set the size of the resizing element.
35312      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
35313      * @param {Number} newSize The new size to set
35314      * @param {Function} onComplete A function to be invoked when resizing is complete
35315      */
35316     setElementSize : function(s, newSize, onComplete){
35317         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
35318             if(!s.animate){
35319                 s.resizingEl.setWidth(newSize);
35320                 if(onComplete){
35321                     onComplete(s, newSize);
35322                 }
35323             }else{
35324                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
35325             }
35326         }else{
35327             
35328             if(!s.animate){
35329                 s.resizingEl.setHeight(newSize);
35330                 if(onComplete){
35331                     onComplete(s, newSize);
35332                 }
35333             }else{
35334                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
35335             }
35336         }
35337     }
35338 };
35339
35340 /** 
35341  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
35342  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
35343  * Adapter that  moves the splitter element to align with the resized sizing element. 
35344  * Used with an absolute positioned SplitBar.
35345  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
35346  * document.body, make sure you assign an id to the body element.
35347  */
35348 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
35349     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
35350     this.container = Roo.get(container);
35351 };
35352
35353 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
35354     init : function(s){
35355         this.basic.init(s);
35356     },
35357     
35358     getElementSize : function(s){
35359         return this.basic.getElementSize(s);
35360     },
35361     
35362     setElementSize : function(s, newSize, onComplete){
35363         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
35364     },
35365     
35366     moveSplitter : function(s){
35367         var yes = Roo.bootstrap.SplitBar;
35368         switch(s.placement){
35369             case yes.LEFT:
35370                 s.el.setX(s.resizingEl.getRight());
35371                 break;
35372             case yes.RIGHT:
35373                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
35374                 break;
35375             case yes.TOP:
35376                 s.el.setY(s.resizingEl.getBottom());
35377                 break;
35378             case yes.BOTTOM:
35379                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
35380                 break;
35381         }
35382     }
35383 };
35384
35385 /**
35386  * Orientation constant - Create a vertical SplitBar
35387  * @static
35388  * @type Number
35389  */
35390 Roo.bootstrap.SplitBar.VERTICAL = 1;
35391
35392 /**
35393  * Orientation constant - Create a horizontal SplitBar
35394  * @static
35395  * @type Number
35396  */
35397 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35398
35399 /**
35400  * Placement constant - The resizing element is to the left of the splitter element
35401  * @static
35402  * @type Number
35403  */
35404 Roo.bootstrap.SplitBar.LEFT = 1;
35405
35406 /**
35407  * Placement constant - The resizing element is to the right of the splitter element
35408  * @static
35409  * @type Number
35410  */
35411 Roo.bootstrap.SplitBar.RIGHT = 2;
35412
35413 /**
35414  * Placement constant - The resizing element is positioned above the splitter element
35415  * @static
35416  * @type Number
35417  */
35418 Roo.bootstrap.SplitBar.TOP = 3;
35419
35420 /**
35421  * Placement constant - The resizing element is positioned under splitter element
35422  * @static
35423  * @type Number
35424  */
35425 Roo.bootstrap.SplitBar.BOTTOM = 4;
35426 Roo.namespace("Roo.bootstrap.layout");/*
35427  * Based on:
35428  * Ext JS Library 1.1.1
35429  * Copyright(c) 2006-2007, Ext JS, LLC.
35430  *
35431  * Originally Released Under LGPL - original licence link has changed is not relivant.
35432  *
35433  * Fork - LGPL
35434  * <script type="text/javascript">
35435  */
35436
35437 /**
35438  * @class Roo.bootstrap.layout.Manager
35439  * @extends Roo.bootstrap.Component
35440  * Base class for layout managers.
35441  */
35442 Roo.bootstrap.layout.Manager = function(config)
35443 {
35444     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
35445
35446
35447
35448
35449
35450     /** false to disable window resize monitoring @type Boolean */
35451     this.monitorWindowResize = true;
35452     this.regions = {};
35453     this.addEvents({
35454         /**
35455          * @event layout
35456          * Fires when a layout is performed.
35457          * @param {Roo.LayoutManager} this
35458          */
35459         "layout" : true,
35460         /**
35461          * @event regionresized
35462          * Fires when the user resizes a region.
35463          * @param {Roo.LayoutRegion} region The resized region
35464          * @param {Number} newSize The new size (width for east/west, height for north/south)
35465          */
35466         "regionresized" : true,
35467         /**
35468          * @event regioncollapsed
35469          * Fires when a region is collapsed.
35470          * @param {Roo.LayoutRegion} region The collapsed region
35471          */
35472         "regioncollapsed" : true,
35473         /**
35474          * @event regionexpanded
35475          * Fires when a region is expanded.
35476          * @param {Roo.LayoutRegion} region The expanded region
35477          */
35478         "regionexpanded" : true
35479     });
35480     this.updating = false;
35481
35482     if (config.el) {
35483         this.el = Roo.get(config.el);
35484         this.initEvents();
35485     }
35486
35487 };
35488
35489 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35490
35491
35492     regions : null,
35493
35494     monitorWindowResize : true,
35495
35496
35497     updating : false,
35498
35499
35500     onRender : function(ct, position)
35501     {
35502         if(!this.el){
35503             this.el = Roo.get(ct);
35504             this.initEvents();
35505         }
35506         //this.fireEvent('render',this);
35507     },
35508
35509
35510     initEvents: function()
35511     {
35512
35513
35514         // ie scrollbar fix
35515         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35516             document.body.scroll = "no";
35517         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35518             this.el.position('relative');
35519         }
35520         this.id = this.el.id;
35521         this.el.addClass("roo-layout-container");
35522         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35523         if(this.el.dom != document.body ) {
35524             this.el.on('resize', this.layout,this);
35525             this.el.on('show', this.layout,this);
35526         }
35527
35528     },
35529
35530     /**
35531      * Returns true if this layout is currently being updated
35532      * @return {Boolean}
35533      */
35534     isUpdating : function(){
35535         return this.updating;
35536     },
35537
35538     /**
35539      * Suspend the LayoutManager from doing auto-layouts while
35540      * making multiple add or remove calls
35541      */
35542     beginUpdate : function(){
35543         this.updating = true;
35544     },
35545
35546     /**
35547      * Restore auto-layouts and optionally disable the manager from performing a layout
35548      * @param {Boolean} noLayout true to disable a layout update
35549      */
35550     endUpdate : function(noLayout){
35551         this.updating = false;
35552         if(!noLayout){
35553             this.layout();
35554         }
35555     },
35556
35557     layout: function(){
35558         // abstract...
35559     },
35560
35561     onRegionResized : function(region, newSize){
35562         this.fireEvent("regionresized", region, newSize);
35563         this.layout();
35564     },
35565
35566     onRegionCollapsed : function(region){
35567         this.fireEvent("regioncollapsed", region);
35568     },
35569
35570     onRegionExpanded : function(region){
35571         this.fireEvent("regionexpanded", region);
35572     },
35573
35574     /**
35575      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35576      * performs box-model adjustments.
35577      * @return {Object} The size as an object {width: (the width), height: (the height)}
35578      */
35579     getViewSize : function()
35580     {
35581         var size;
35582         if(this.el.dom != document.body){
35583             size = this.el.getSize();
35584         }else{
35585             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35586         }
35587         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35588         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35589         return size;
35590     },
35591
35592     /**
35593      * Returns the Element this layout is bound to.
35594      * @return {Roo.Element}
35595      */
35596     getEl : function(){
35597         return this.el;
35598     },
35599
35600     /**
35601      * Returns the specified region.
35602      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35603      * @return {Roo.LayoutRegion}
35604      */
35605     getRegion : function(target){
35606         return this.regions[target.toLowerCase()];
35607     },
35608
35609     onWindowResize : function(){
35610         if(this.monitorWindowResize){
35611             this.layout();
35612         }
35613     }
35614 });
35615 /*
35616  * Based on:
35617  * Ext JS Library 1.1.1
35618  * Copyright(c) 2006-2007, Ext JS, LLC.
35619  *
35620  * Originally Released Under LGPL - original licence link has changed is not relivant.
35621  *
35622  * Fork - LGPL
35623  * <script type="text/javascript">
35624  */
35625 /**
35626  * @class Roo.bootstrap.layout.Border
35627  * @extends Roo.bootstrap.layout.Manager
35628  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35629  * please see: examples/bootstrap/nested.html<br><br>
35630  
35631 <b>The container the layout is rendered into can be either the body element or any other element.
35632 If it is not the body element, the container needs to either be an absolute positioned element,
35633 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35634 the container size if it is not the body element.</b>
35635
35636 * @constructor
35637 * Create a new Border
35638 * @param {Object} config Configuration options
35639  */
35640 Roo.bootstrap.layout.Border = function(config){
35641     config = config || {};
35642     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35643     
35644     
35645     
35646     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35647         if(config[region]){
35648             config[region].region = region;
35649             this.addRegion(config[region]);
35650         }
35651     },this);
35652     
35653 };
35654
35655 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35656
35657 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35658     
35659     parent : false, // this might point to a 'nest' or a ???
35660     
35661     /**
35662      * Creates and adds a new region if it doesn't already exist.
35663      * @param {String} target The target region key (north, south, east, west or center).
35664      * @param {Object} config The regions config object
35665      * @return {BorderLayoutRegion} The new region
35666      */
35667     addRegion : function(config)
35668     {
35669         if(!this.regions[config.region]){
35670             var r = this.factory(config);
35671             this.bindRegion(r);
35672         }
35673         return this.regions[config.region];
35674     },
35675
35676     // private (kinda)
35677     bindRegion : function(r){
35678         this.regions[r.config.region] = r;
35679         
35680         r.on("visibilitychange",    this.layout, this);
35681         r.on("paneladded",          this.layout, this);
35682         r.on("panelremoved",        this.layout, this);
35683         r.on("invalidated",         this.layout, this);
35684         r.on("resized",             this.onRegionResized, this);
35685         r.on("collapsed",           this.onRegionCollapsed, this);
35686         r.on("expanded",            this.onRegionExpanded, this);
35687     },
35688
35689     /**
35690      * Performs a layout update.
35691      */
35692     layout : function()
35693     {
35694         if(this.updating) {
35695             return;
35696         }
35697         
35698         // render all the rebions if they have not been done alreayd?
35699         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35700             if(this.regions[region] && !this.regions[region].bodyEl){
35701                 this.regions[region].onRender(this.el)
35702             }
35703         },this);
35704         
35705         var size = this.getViewSize();
35706         var w = size.width;
35707         var h = size.height;
35708         var centerW = w;
35709         var centerH = h;
35710         var centerY = 0;
35711         var centerX = 0;
35712         //var x = 0, y = 0;
35713
35714         var rs = this.regions;
35715         var north = rs["north"];
35716         var south = rs["south"]; 
35717         var west = rs["west"];
35718         var east = rs["east"];
35719         var center = rs["center"];
35720         //if(this.hideOnLayout){ // not supported anymore
35721             //c.el.setStyle("display", "none");
35722         //}
35723         if(north && north.isVisible()){
35724             var b = north.getBox();
35725             var m = north.getMargins();
35726             b.width = w - (m.left+m.right);
35727             b.x = m.left;
35728             b.y = m.top;
35729             centerY = b.height + b.y + m.bottom;
35730             centerH -= centerY;
35731             north.updateBox(this.safeBox(b));
35732         }
35733         if(south && south.isVisible()){
35734             var b = south.getBox();
35735             var m = south.getMargins();
35736             b.width = w - (m.left+m.right);
35737             b.x = m.left;
35738             var totalHeight = (b.height + m.top + m.bottom);
35739             b.y = h - totalHeight + m.top;
35740             centerH -= totalHeight;
35741             south.updateBox(this.safeBox(b));
35742         }
35743         if(west && west.isVisible()){
35744             var b = west.getBox();
35745             var m = west.getMargins();
35746             b.height = centerH - (m.top+m.bottom);
35747             b.x = m.left;
35748             b.y = centerY + m.top;
35749             var totalWidth = (b.width + m.left + m.right);
35750             centerX += totalWidth;
35751             centerW -= totalWidth;
35752             west.updateBox(this.safeBox(b));
35753         }
35754         if(east && east.isVisible()){
35755             var b = east.getBox();
35756             var m = east.getMargins();
35757             b.height = centerH - (m.top+m.bottom);
35758             var totalWidth = (b.width + m.left + m.right);
35759             b.x = w - totalWidth + m.left;
35760             b.y = centerY + m.top;
35761             centerW -= totalWidth;
35762             east.updateBox(this.safeBox(b));
35763         }
35764         if(center){
35765             var m = center.getMargins();
35766             var centerBox = {
35767                 x: centerX + m.left,
35768                 y: centerY + m.top,
35769                 width: centerW - (m.left+m.right),
35770                 height: centerH - (m.top+m.bottom)
35771             };
35772             //if(this.hideOnLayout){
35773                 //center.el.setStyle("display", "block");
35774             //}
35775             center.updateBox(this.safeBox(centerBox));
35776         }
35777         this.el.repaint();
35778         this.fireEvent("layout", this);
35779     },
35780
35781     // private
35782     safeBox : function(box){
35783         box.width = Math.max(0, box.width);
35784         box.height = Math.max(0, box.height);
35785         return box;
35786     },
35787
35788     /**
35789      * Adds a ContentPanel (or subclass) to this layout.
35790      * @param {String} target The target region key (north, south, east, west or center).
35791      * @param {Roo.ContentPanel} panel The panel to add
35792      * @return {Roo.ContentPanel} The added panel
35793      */
35794     add : function(target, panel){
35795          
35796         target = target.toLowerCase();
35797         return this.regions[target].add(panel);
35798     },
35799
35800     /**
35801      * Remove a ContentPanel (or subclass) to this layout.
35802      * @param {String} target The target region key (north, south, east, west or center).
35803      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35804      * @return {Roo.ContentPanel} The removed panel
35805      */
35806     remove : function(target, panel){
35807         target = target.toLowerCase();
35808         return this.regions[target].remove(panel);
35809     },
35810
35811     /**
35812      * Searches all regions for a panel with the specified id
35813      * @param {String} panelId
35814      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35815      */
35816     findPanel : function(panelId){
35817         var rs = this.regions;
35818         for(var target in rs){
35819             if(typeof rs[target] != "function"){
35820                 var p = rs[target].getPanel(panelId);
35821                 if(p){
35822                     return p;
35823                 }
35824             }
35825         }
35826         return null;
35827     },
35828
35829     /**
35830      * Searches all regions for a panel with the specified id and activates (shows) it.
35831      * @param {String/ContentPanel} panelId The panels id or the panel itself
35832      * @return {Roo.ContentPanel} The shown panel or null
35833      */
35834     showPanel : function(panelId) {
35835       var rs = this.regions;
35836       for(var target in rs){
35837          var r = rs[target];
35838          if(typeof r != "function"){
35839             if(r.hasPanel(panelId)){
35840                return r.showPanel(panelId);
35841             }
35842          }
35843       }
35844       return null;
35845    },
35846
35847    /**
35848      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35849      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35850      */
35851    /*
35852     restoreState : function(provider){
35853         if(!provider){
35854             provider = Roo.state.Manager;
35855         }
35856         var sm = new Roo.LayoutStateManager();
35857         sm.init(this, provider);
35858     },
35859 */
35860  
35861  
35862     /**
35863      * Adds a xtype elements to the layout.
35864      * <pre><code>
35865
35866 layout.addxtype({
35867        xtype : 'ContentPanel',
35868        region: 'west',
35869        items: [ .... ]
35870    }
35871 );
35872
35873 layout.addxtype({
35874         xtype : 'NestedLayoutPanel',
35875         region: 'west',
35876         layout: {
35877            center: { },
35878            west: { }   
35879         },
35880         items : [ ... list of content panels or nested layout panels.. ]
35881    }
35882 );
35883 </code></pre>
35884      * @param {Object} cfg Xtype definition of item to add.
35885      */
35886     addxtype : function(cfg)
35887     {
35888         // basically accepts a pannel...
35889         // can accept a layout region..!?!?
35890         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35891         
35892         
35893         // theory?  children can only be panels??
35894         
35895         //if (!cfg.xtype.match(/Panel$/)) {
35896         //    return false;
35897         //}
35898         var ret = false;
35899         
35900         if (typeof(cfg.region) == 'undefined') {
35901             Roo.log("Failed to add Panel, region was not set");
35902             Roo.log(cfg);
35903             return false;
35904         }
35905         var region = cfg.region;
35906         delete cfg.region;
35907         
35908           
35909         var xitems = [];
35910         if (cfg.items) {
35911             xitems = cfg.items;
35912             delete cfg.items;
35913         }
35914         var nb = false;
35915         
35916         if ( region == 'center') {
35917             Roo.log("Center: " + cfg.title);
35918         }
35919         
35920         
35921         switch(cfg.xtype) 
35922         {
35923             case 'Content':  // ContentPanel (el, cfg)
35924             case 'Scroll':  // ContentPanel (el, cfg)
35925             case 'View': 
35926                 cfg.autoCreate = cfg.autoCreate || true;
35927                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35928                 //} else {
35929                 //    var el = this.el.createChild();
35930                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35931                 //}
35932                 
35933                 this.add(region, ret);
35934                 break;
35935             
35936             /*
35937             case 'TreePanel': // our new panel!
35938                 cfg.el = this.el.createChild();
35939                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35940                 this.add(region, ret);
35941                 break;
35942             */
35943             
35944             case 'Nest': 
35945                 // create a new Layout (which is  a Border Layout...
35946                 
35947                 var clayout = cfg.layout;
35948                 clayout.el  = this.el.createChild();
35949                 clayout.items   = clayout.items  || [];
35950                 
35951                 delete cfg.layout;
35952                 
35953                 // replace this exitems with the clayout ones..
35954                 xitems = clayout.items;
35955                  
35956                 // force background off if it's in center...
35957                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35958                     cfg.background = false;
35959                 }
35960                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35961                 
35962                 
35963                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35964                 //console.log('adding nested layout panel '  + cfg.toSource());
35965                 this.add(region, ret);
35966                 nb = {}; /// find first...
35967                 break;
35968             
35969             case 'Grid':
35970                 
35971                 // needs grid and region
35972                 
35973                 //var el = this.getRegion(region).el.createChild();
35974                 /*
35975                  *var el = this.el.createChild();
35976                 // create the grid first...
35977                 cfg.grid.container = el;
35978                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35979                 */
35980                 
35981                 if (region == 'center' && this.active ) {
35982                     cfg.background = false;
35983                 }
35984                 
35985                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35986                 
35987                 this.add(region, ret);
35988                 /*
35989                 if (cfg.background) {
35990                     // render grid on panel activation (if panel background)
35991                     ret.on('activate', function(gp) {
35992                         if (!gp.grid.rendered) {
35993                     //        gp.grid.render(el);
35994                         }
35995                     });
35996                 } else {
35997                   //  cfg.grid.render(el);
35998                 }
35999                 */
36000                 break;
36001            
36002            
36003             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
36004                 // it was the old xcomponent building that caused this before.
36005                 // espeically if border is the top element in the tree.
36006                 ret = this;
36007                 break; 
36008                 
36009                     
36010                 
36011                 
36012                 
36013             default:
36014                 /*
36015                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
36016                     
36017                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
36018                     this.add(region, ret);
36019                 } else {
36020                 */
36021                     Roo.log(cfg);
36022                     throw "Can not add '" + cfg.xtype + "' to Border";
36023                     return null;
36024              
36025                                 
36026              
36027         }
36028         this.beginUpdate();
36029         // add children..
36030         var region = '';
36031         var abn = {};
36032         Roo.each(xitems, function(i)  {
36033             region = nb && i.region ? i.region : false;
36034             
36035             var add = ret.addxtype(i);
36036            
36037             if (region) {
36038                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
36039                 if (!i.background) {
36040                     abn[region] = nb[region] ;
36041                 }
36042             }
36043             
36044         });
36045         this.endUpdate();
36046
36047         // make the last non-background panel active..
36048         //if (nb) { Roo.log(abn); }
36049         if (nb) {
36050             
36051             for(var r in abn) {
36052                 region = this.getRegion(r);
36053                 if (region) {
36054                     // tried using nb[r], but it does not work..
36055                      
36056                     region.showPanel(abn[r]);
36057                    
36058                 }
36059             }
36060         }
36061         return ret;
36062         
36063     },
36064     
36065     
36066 // private
36067     factory : function(cfg)
36068     {
36069         
36070         var validRegions = Roo.bootstrap.layout.Border.regions;
36071
36072         var target = cfg.region;
36073         cfg.mgr = this;
36074         
36075         var r = Roo.bootstrap.layout;
36076         Roo.log(target);
36077         switch(target){
36078             case "north":
36079                 return new r.North(cfg);
36080             case "south":
36081                 return new r.South(cfg);
36082             case "east":
36083                 return new r.East(cfg);
36084             case "west":
36085                 return new r.West(cfg);
36086             case "center":
36087                 return new r.Center(cfg);
36088         }
36089         throw 'Layout region "'+target+'" not supported.';
36090     }
36091     
36092     
36093 });
36094  /*
36095  * Based on:
36096  * Ext JS Library 1.1.1
36097  * Copyright(c) 2006-2007, Ext JS, LLC.
36098  *
36099  * Originally Released Under LGPL - original licence link has changed is not relivant.
36100  *
36101  * Fork - LGPL
36102  * <script type="text/javascript">
36103  */
36104  
36105 /**
36106  * @class Roo.bootstrap.layout.Basic
36107  * @extends Roo.util.Observable
36108  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
36109  * and does not have a titlebar, tabs or any other features. All it does is size and position 
36110  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
36111  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
36112  * @cfg {string}   region  the region that it inhabits..
36113  * @cfg {bool}   skipConfig skip config?
36114  * 
36115
36116  */
36117 Roo.bootstrap.layout.Basic = function(config){
36118     
36119     this.mgr = config.mgr;
36120     
36121     this.position = config.region;
36122     
36123     var skipConfig = config.skipConfig;
36124     
36125     this.events = {
36126         /**
36127          * @scope Roo.BasicLayoutRegion
36128          */
36129         
36130         /**
36131          * @event beforeremove
36132          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
36133          * @param {Roo.LayoutRegion} this
36134          * @param {Roo.ContentPanel} panel The panel
36135          * @param {Object} e The cancel event object
36136          */
36137         "beforeremove" : true,
36138         /**
36139          * @event invalidated
36140          * Fires when the layout for this region is changed.
36141          * @param {Roo.LayoutRegion} this
36142          */
36143         "invalidated" : true,
36144         /**
36145          * @event visibilitychange
36146          * Fires when this region is shown or hidden 
36147          * @param {Roo.LayoutRegion} this
36148          * @param {Boolean} visibility true or false
36149          */
36150         "visibilitychange" : true,
36151         /**
36152          * @event paneladded
36153          * Fires when a panel is added. 
36154          * @param {Roo.LayoutRegion} this
36155          * @param {Roo.ContentPanel} panel The panel
36156          */
36157         "paneladded" : true,
36158         /**
36159          * @event panelremoved
36160          * Fires when a panel is removed. 
36161          * @param {Roo.LayoutRegion} this
36162          * @param {Roo.ContentPanel} panel The panel
36163          */
36164         "panelremoved" : true,
36165         /**
36166          * @event beforecollapse
36167          * Fires when this region before collapse.
36168          * @param {Roo.LayoutRegion} this
36169          */
36170         "beforecollapse" : true,
36171         /**
36172          * @event collapsed
36173          * Fires when this region is collapsed.
36174          * @param {Roo.LayoutRegion} this
36175          */
36176         "collapsed" : true,
36177         /**
36178          * @event expanded
36179          * Fires when this region is expanded.
36180          * @param {Roo.LayoutRegion} this
36181          */
36182         "expanded" : true,
36183         /**
36184          * @event slideshow
36185          * Fires when this region is slid into view.
36186          * @param {Roo.LayoutRegion} this
36187          */
36188         "slideshow" : true,
36189         /**
36190          * @event slidehide
36191          * Fires when this region slides out of view. 
36192          * @param {Roo.LayoutRegion} this
36193          */
36194         "slidehide" : true,
36195         /**
36196          * @event panelactivated
36197          * Fires when a panel is activated. 
36198          * @param {Roo.LayoutRegion} this
36199          * @param {Roo.ContentPanel} panel The activated panel
36200          */
36201         "panelactivated" : true,
36202         /**
36203          * @event resized
36204          * Fires when the user resizes this region. 
36205          * @param {Roo.LayoutRegion} this
36206          * @param {Number} newSize The new size (width for east/west, height for north/south)
36207          */
36208         "resized" : true
36209     };
36210     /** A collection of panels in this region. @type Roo.util.MixedCollection */
36211     this.panels = new Roo.util.MixedCollection();
36212     this.panels.getKey = this.getPanelId.createDelegate(this);
36213     this.box = null;
36214     this.activePanel = null;
36215     // ensure listeners are added...
36216     
36217     if (config.listeners || config.events) {
36218         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
36219             listeners : config.listeners || {},
36220             events : config.events || {}
36221         });
36222     }
36223     
36224     if(skipConfig !== true){
36225         this.applyConfig(config);
36226     }
36227 };
36228
36229 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
36230 {
36231     getPanelId : function(p){
36232         return p.getId();
36233     },
36234     
36235     applyConfig : function(config){
36236         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36237         this.config = config;
36238         
36239     },
36240     
36241     /**
36242      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
36243      * the width, for horizontal (north, south) the height.
36244      * @param {Number} newSize The new width or height
36245      */
36246     resizeTo : function(newSize){
36247         var el = this.el ? this.el :
36248                  (this.activePanel ? this.activePanel.getEl() : null);
36249         if(el){
36250             switch(this.position){
36251                 case "east":
36252                 case "west":
36253                     el.setWidth(newSize);
36254                     this.fireEvent("resized", this, newSize);
36255                 break;
36256                 case "north":
36257                 case "south":
36258                     el.setHeight(newSize);
36259                     this.fireEvent("resized", this, newSize);
36260                 break;                
36261             }
36262         }
36263     },
36264     
36265     getBox : function(){
36266         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
36267     },
36268     
36269     getMargins : function(){
36270         return this.margins;
36271     },
36272     
36273     updateBox : function(box){
36274         this.box = box;
36275         var el = this.activePanel.getEl();
36276         el.dom.style.left = box.x + "px";
36277         el.dom.style.top = box.y + "px";
36278         this.activePanel.setSize(box.width, box.height);
36279     },
36280     
36281     /**
36282      * Returns the container element for this region.
36283      * @return {Roo.Element}
36284      */
36285     getEl : function(){
36286         return this.activePanel;
36287     },
36288     
36289     /**
36290      * Returns true if this region is currently visible.
36291      * @return {Boolean}
36292      */
36293     isVisible : function(){
36294         return this.activePanel ? true : false;
36295     },
36296     
36297     setActivePanel : function(panel){
36298         panel = this.getPanel(panel);
36299         if(this.activePanel && this.activePanel != panel){
36300             this.activePanel.setActiveState(false);
36301             this.activePanel.getEl().setLeftTop(-10000,-10000);
36302         }
36303         this.activePanel = panel;
36304         panel.setActiveState(true);
36305         if(this.box){
36306             panel.setSize(this.box.width, this.box.height);
36307         }
36308         this.fireEvent("panelactivated", this, panel);
36309         this.fireEvent("invalidated");
36310     },
36311     
36312     /**
36313      * Show the specified panel.
36314      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
36315      * @return {Roo.ContentPanel} The shown panel or null
36316      */
36317     showPanel : function(panel){
36318         panel = this.getPanel(panel);
36319         if(panel){
36320             this.setActivePanel(panel);
36321         }
36322         return panel;
36323     },
36324     
36325     /**
36326      * Get the active panel for this region.
36327      * @return {Roo.ContentPanel} The active panel or null
36328      */
36329     getActivePanel : function(){
36330         return this.activePanel;
36331     },
36332     
36333     /**
36334      * Add the passed ContentPanel(s)
36335      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36336      * @return {Roo.ContentPanel} The panel added (if only one was added)
36337      */
36338     add : function(panel){
36339         if(arguments.length > 1){
36340             for(var i = 0, len = arguments.length; i < len; i++) {
36341                 this.add(arguments[i]);
36342             }
36343             return null;
36344         }
36345         if(this.hasPanel(panel)){
36346             this.showPanel(panel);
36347             return panel;
36348         }
36349         var el = panel.getEl();
36350         if(el.dom.parentNode != this.mgr.el.dom){
36351             this.mgr.el.dom.appendChild(el.dom);
36352         }
36353         if(panel.setRegion){
36354             panel.setRegion(this);
36355         }
36356         this.panels.add(panel);
36357         el.setStyle("position", "absolute");
36358         if(!panel.background){
36359             this.setActivePanel(panel);
36360             if(this.config.initialSize && this.panels.getCount()==1){
36361                 this.resizeTo(this.config.initialSize);
36362             }
36363         }
36364         this.fireEvent("paneladded", this, panel);
36365         return panel;
36366     },
36367     
36368     /**
36369      * Returns true if the panel is in this region.
36370      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36371      * @return {Boolean}
36372      */
36373     hasPanel : function(panel){
36374         if(typeof panel == "object"){ // must be panel obj
36375             panel = panel.getId();
36376         }
36377         return this.getPanel(panel) ? true : false;
36378     },
36379     
36380     /**
36381      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36382      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36383      * @param {Boolean} preservePanel Overrides the config preservePanel option
36384      * @return {Roo.ContentPanel} The panel that was removed
36385      */
36386     remove : function(panel, preservePanel){
36387         panel = this.getPanel(panel);
36388         if(!panel){
36389             return null;
36390         }
36391         var e = {};
36392         this.fireEvent("beforeremove", this, panel, e);
36393         if(e.cancel === true){
36394             return null;
36395         }
36396         var panelId = panel.getId();
36397         this.panels.removeKey(panelId);
36398         return panel;
36399     },
36400     
36401     /**
36402      * Returns the panel specified or null if it's not in this region.
36403      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36404      * @return {Roo.ContentPanel}
36405      */
36406     getPanel : function(id){
36407         if(typeof id == "object"){ // must be panel obj
36408             return id;
36409         }
36410         return this.panels.get(id);
36411     },
36412     
36413     /**
36414      * Returns this regions position (north/south/east/west/center).
36415      * @return {String} 
36416      */
36417     getPosition: function(){
36418         return this.position;    
36419     }
36420 });/*
36421  * Based on:
36422  * Ext JS Library 1.1.1
36423  * Copyright(c) 2006-2007, Ext JS, LLC.
36424  *
36425  * Originally Released Under LGPL - original licence link has changed is not relivant.
36426  *
36427  * Fork - LGPL
36428  * <script type="text/javascript">
36429  */
36430  
36431 /**
36432  * @class Roo.bootstrap.layout.Region
36433  * @extends Roo.bootstrap.layout.Basic
36434  * This class represents a region in a layout manager.
36435  
36436  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36437  * @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})
36438  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
36439  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
36440  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
36441  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
36442  * @cfg {String}    title           The title for the region (overrides panel titles)
36443  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
36444  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36445  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
36446  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36447  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
36448  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36449  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
36450  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
36451  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
36452  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
36453
36454  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
36455  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
36456  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
36457  * @cfg {Number}    width           For East/West panels
36458  * @cfg {Number}    height          For North/South panels
36459  * @cfg {Boolean}   split           To show the splitter
36460  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
36461  * 
36462  * @cfg {string}   cls             Extra CSS classes to add to region
36463  * 
36464  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
36465  * @cfg {string}   region  the region that it inhabits..
36466  *
36467
36468  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
36469  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
36470
36471  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
36472  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
36473  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
36474  */
36475 Roo.bootstrap.layout.Region = function(config)
36476 {
36477     this.applyConfig(config);
36478
36479     var mgr = config.mgr;
36480     var pos = config.region;
36481     config.skipConfig = true;
36482     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36483     
36484     if (mgr.el) {
36485         this.onRender(mgr.el);   
36486     }
36487      
36488     this.visible = true;
36489     this.collapsed = false;
36490     this.unrendered_panels = [];
36491 };
36492
36493 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36494
36495     position: '', // set by wrapper (eg. north/south etc..)
36496     unrendered_panels : null,  // unrendered panels.
36497     
36498     tabPosition : false,
36499     
36500     mgr: false, // points to 'Border'
36501     
36502     
36503     createBody : function(){
36504         /** This region's body element 
36505         * @type Roo.Element */
36506         this.bodyEl = this.el.createChild({
36507                 tag: "div",
36508                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36509         });
36510     },
36511
36512     onRender: function(ctr, pos)
36513     {
36514         var dh = Roo.DomHelper;
36515         /** This region's container element 
36516         * @type Roo.Element */
36517         this.el = dh.append(ctr.dom, {
36518                 tag: "div",
36519                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36520             }, true);
36521         /** This region's title element 
36522         * @type Roo.Element */
36523     
36524         this.titleEl = dh.append(this.el.dom,  {
36525                 tag: "div",
36526                 unselectable: "on",
36527                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36528                 children:[
36529                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
36530                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36531                 ]
36532             }, true);
36533         
36534         this.titleEl.enableDisplayMode();
36535         /** This region's title text element 
36536         * @type HTMLElement */
36537         this.titleTextEl = this.titleEl.dom.firstChild;
36538         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36539         /*
36540         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36541         this.closeBtn.enableDisplayMode();
36542         this.closeBtn.on("click", this.closeClicked, this);
36543         this.closeBtn.hide();
36544     */
36545         this.createBody(this.config);
36546         if(this.config.hideWhenEmpty){
36547             this.hide();
36548             this.on("paneladded", this.validateVisibility, this);
36549             this.on("panelremoved", this.validateVisibility, this);
36550         }
36551         if(this.autoScroll){
36552             this.bodyEl.setStyle("overflow", "auto");
36553         }else{
36554             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36555         }
36556         //if(c.titlebar !== false){
36557             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36558                 this.titleEl.hide();
36559             }else{
36560                 this.titleEl.show();
36561                 if(this.config.title){
36562                     this.titleTextEl.innerHTML = this.config.title;
36563                 }
36564             }
36565         //}
36566         if(this.config.collapsed){
36567             this.collapse(true);
36568         }
36569         if(this.config.hidden){
36570             this.hide();
36571         }
36572         
36573         if (this.unrendered_panels && this.unrendered_panels.length) {
36574             for (var i =0;i< this.unrendered_panels.length; i++) {
36575                 this.add(this.unrendered_panels[i]);
36576             }
36577             this.unrendered_panels = null;
36578             
36579         }
36580         
36581     },
36582     
36583     applyConfig : function(c)
36584     {
36585         /*
36586          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36587             var dh = Roo.DomHelper;
36588             if(c.titlebar !== false){
36589                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36590                 this.collapseBtn.on("click", this.collapse, this);
36591                 this.collapseBtn.enableDisplayMode();
36592                 /*
36593                 if(c.showPin === true || this.showPin){
36594                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36595                     this.stickBtn.enableDisplayMode();
36596                     this.stickBtn.on("click", this.expand, this);
36597                     this.stickBtn.hide();
36598                 }
36599                 
36600             }
36601             */
36602             /** This region's collapsed element
36603             * @type Roo.Element */
36604             /*
36605              *
36606             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36607                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36608             ]}, true);
36609             
36610             if(c.floatable !== false){
36611                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36612                this.collapsedEl.on("click", this.collapseClick, this);
36613             }
36614
36615             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36616                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36617                    id: "message", unselectable: "on", style:{"float":"left"}});
36618                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36619              }
36620             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36621             this.expandBtn.on("click", this.expand, this);
36622             
36623         }
36624         
36625         if(this.collapseBtn){
36626             this.collapseBtn.setVisible(c.collapsible == true);
36627         }
36628         
36629         this.cmargins = c.cmargins || this.cmargins ||
36630                          (this.position == "west" || this.position == "east" ?
36631                              {top: 0, left: 2, right:2, bottom: 0} :
36632                              {top: 2, left: 0, right:0, bottom: 2});
36633         */
36634         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36635         
36636         
36637         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36638         
36639         this.autoScroll = c.autoScroll || false;
36640         
36641         
36642        
36643         
36644         this.duration = c.duration || .30;
36645         this.slideDuration = c.slideDuration || .45;
36646         this.config = c;
36647        
36648     },
36649     /**
36650      * Returns true if this region is currently visible.
36651      * @return {Boolean}
36652      */
36653     isVisible : function(){
36654         return this.visible;
36655     },
36656
36657     /**
36658      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36659      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36660      */
36661     //setCollapsedTitle : function(title){
36662     //    title = title || "&#160;";
36663      //   if(this.collapsedTitleTextEl){
36664       //      this.collapsedTitleTextEl.innerHTML = title;
36665        // }
36666     //},
36667
36668     getBox : function(){
36669         var b;
36670       //  if(!this.collapsed){
36671             b = this.el.getBox(false, true);
36672        // }else{
36673           //  b = this.collapsedEl.getBox(false, true);
36674         //}
36675         return b;
36676     },
36677
36678     getMargins : function(){
36679         return this.margins;
36680         //return this.collapsed ? this.cmargins : this.margins;
36681     },
36682 /*
36683     highlight : function(){
36684         this.el.addClass("x-layout-panel-dragover");
36685     },
36686
36687     unhighlight : function(){
36688         this.el.removeClass("x-layout-panel-dragover");
36689     },
36690 */
36691     updateBox : function(box)
36692     {
36693         if (!this.bodyEl) {
36694             return; // not rendered yet..
36695         }
36696         
36697         this.box = box;
36698         if(!this.collapsed){
36699             this.el.dom.style.left = box.x + "px";
36700             this.el.dom.style.top = box.y + "px";
36701             this.updateBody(box.width, box.height);
36702         }else{
36703             this.collapsedEl.dom.style.left = box.x + "px";
36704             this.collapsedEl.dom.style.top = box.y + "px";
36705             this.collapsedEl.setSize(box.width, box.height);
36706         }
36707         if(this.tabs){
36708             this.tabs.autoSizeTabs();
36709         }
36710     },
36711
36712     updateBody : function(w, h)
36713     {
36714         if(w !== null){
36715             this.el.setWidth(w);
36716             w -= this.el.getBorderWidth("rl");
36717             if(this.config.adjustments){
36718                 w += this.config.adjustments[0];
36719             }
36720         }
36721         if(h !== null && h > 0){
36722             this.el.setHeight(h);
36723             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36724             h -= this.el.getBorderWidth("tb");
36725             if(this.config.adjustments){
36726                 h += this.config.adjustments[1];
36727             }
36728             this.bodyEl.setHeight(h);
36729             if(this.tabs){
36730                 h = this.tabs.syncHeight(h);
36731             }
36732         }
36733         if(this.panelSize){
36734             w = w !== null ? w : this.panelSize.width;
36735             h = h !== null ? h : this.panelSize.height;
36736         }
36737         if(this.activePanel){
36738             var el = this.activePanel.getEl();
36739             w = w !== null ? w : el.getWidth();
36740             h = h !== null ? h : el.getHeight();
36741             this.panelSize = {width: w, height: h};
36742             this.activePanel.setSize(w, h);
36743         }
36744         if(Roo.isIE && this.tabs){
36745             this.tabs.el.repaint();
36746         }
36747     },
36748
36749     /**
36750      * Returns the container element for this region.
36751      * @return {Roo.Element}
36752      */
36753     getEl : function(){
36754         return this.el;
36755     },
36756
36757     /**
36758      * Hides this region.
36759      */
36760     hide : function(){
36761         //if(!this.collapsed){
36762             this.el.dom.style.left = "-2000px";
36763             this.el.hide();
36764         //}else{
36765          //   this.collapsedEl.dom.style.left = "-2000px";
36766          //   this.collapsedEl.hide();
36767        // }
36768         this.visible = false;
36769         this.fireEvent("visibilitychange", this, false);
36770     },
36771
36772     /**
36773      * Shows this region if it was previously hidden.
36774      */
36775     show : function(){
36776         //if(!this.collapsed){
36777             this.el.show();
36778         //}else{
36779         //    this.collapsedEl.show();
36780        // }
36781         this.visible = true;
36782         this.fireEvent("visibilitychange", this, true);
36783     },
36784 /*
36785     closeClicked : function(){
36786         if(this.activePanel){
36787             this.remove(this.activePanel);
36788         }
36789     },
36790
36791     collapseClick : function(e){
36792         if(this.isSlid){
36793            e.stopPropagation();
36794            this.slideIn();
36795         }else{
36796            e.stopPropagation();
36797            this.slideOut();
36798         }
36799     },
36800 */
36801     /**
36802      * Collapses this region.
36803      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36804      */
36805     /*
36806     collapse : function(skipAnim, skipCheck = false){
36807         if(this.collapsed) {
36808             return;
36809         }
36810         
36811         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36812             
36813             this.collapsed = true;
36814             if(this.split){
36815                 this.split.el.hide();
36816             }
36817             if(this.config.animate && skipAnim !== true){
36818                 this.fireEvent("invalidated", this);
36819                 this.animateCollapse();
36820             }else{
36821                 this.el.setLocation(-20000,-20000);
36822                 this.el.hide();
36823                 this.collapsedEl.show();
36824                 this.fireEvent("collapsed", this);
36825                 this.fireEvent("invalidated", this);
36826             }
36827         }
36828         
36829     },
36830 */
36831     animateCollapse : function(){
36832         // overridden
36833     },
36834
36835     /**
36836      * Expands this region if it was previously collapsed.
36837      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36838      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36839      */
36840     /*
36841     expand : function(e, skipAnim){
36842         if(e) {
36843             e.stopPropagation();
36844         }
36845         if(!this.collapsed || this.el.hasActiveFx()) {
36846             return;
36847         }
36848         if(this.isSlid){
36849             this.afterSlideIn();
36850             skipAnim = true;
36851         }
36852         this.collapsed = false;
36853         if(this.config.animate && skipAnim !== true){
36854             this.animateExpand();
36855         }else{
36856             this.el.show();
36857             if(this.split){
36858                 this.split.el.show();
36859             }
36860             this.collapsedEl.setLocation(-2000,-2000);
36861             this.collapsedEl.hide();
36862             this.fireEvent("invalidated", this);
36863             this.fireEvent("expanded", this);
36864         }
36865     },
36866 */
36867     animateExpand : function(){
36868         // overridden
36869     },
36870
36871     initTabs : function()
36872     {
36873         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36874         
36875         var ts = new Roo.bootstrap.panel.Tabs({
36876             el: this.bodyEl.dom,
36877             region : this,
36878             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
36879             disableTooltips: this.config.disableTabTips,
36880             toolbar : this.config.toolbar
36881         });
36882         
36883         if(this.config.hideTabs){
36884             ts.stripWrap.setDisplayed(false);
36885         }
36886         this.tabs = ts;
36887         ts.resizeTabs = this.config.resizeTabs === true;
36888         ts.minTabWidth = this.config.minTabWidth || 40;
36889         ts.maxTabWidth = this.config.maxTabWidth || 250;
36890         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36891         ts.monitorResize = false;
36892         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36893         ts.bodyEl.addClass('roo-layout-tabs-body');
36894         this.panels.each(this.initPanelAsTab, this);
36895     },
36896
36897     initPanelAsTab : function(panel){
36898         var ti = this.tabs.addTab(
36899             panel.getEl().id,
36900             panel.getTitle(),
36901             null,
36902             this.config.closeOnTab && panel.isClosable(),
36903             panel.tpl
36904         );
36905         if(panel.tabTip !== undefined){
36906             ti.setTooltip(panel.tabTip);
36907         }
36908         ti.on("activate", function(){
36909               this.setActivePanel(panel);
36910         }, this);
36911         
36912         if(this.config.closeOnTab){
36913             ti.on("beforeclose", function(t, e){
36914                 e.cancel = true;
36915                 this.remove(panel);
36916             }, this);
36917         }
36918         
36919         panel.tabItem = ti;
36920         
36921         return ti;
36922     },
36923
36924     updatePanelTitle : function(panel, title)
36925     {
36926         if(this.activePanel == panel){
36927             this.updateTitle(title);
36928         }
36929         if(this.tabs){
36930             var ti = this.tabs.getTab(panel.getEl().id);
36931             ti.setText(title);
36932             if(panel.tabTip !== undefined){
36933                 ti.setTooltip(panel.tabTip);
36934             }
36935         }
36936     },
36937
36938     updateTitle : function(title){
36939         if(this.titleTextEl && !this.config.title){
36940             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36941         }
36942     },
36943
36944     setActivePanel : function(panel)
36945     {
36946         panel = this.getPanel(panel);
36947         if(this.activePanel && this.activePanel != panel){
36948             if(this.activePanel.setActiveState(false) === false){
36949                 return;
36950             }
36951         }
36952         this.activePanel = panel;
36953         panel.setActiveState(true);
36954         if(this.panelSize){
36955             panel.setSize(this.panelSize.width, this.panelSize.height);
36956         }
36957         if(this.closeBtn){
36958             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36959         }
36960         this.updateTitle(panel.getTitle());
36961         if(this.tabs){
36962             this.fireEvent("invalidated", this);
36963         }
36964         this.fireEvent("panelactivated", this, panel);
36965     },
36966
36967     /**
36968      * Shows the specified panel.
36969      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36970      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36971      */
36972     showPanel : function(panel)
36973     {
36974         panel = this.getPanel(panel);
36975         if(panel){
36976             if(this.tabs){
36977                 var tab = this.tabs.getTab(panel.getEl().id);
36978                 if(tab.isHidden()){
36979                     this.tabs.unhideTab(tab.id);
36980                 }
36981                 tab.activate();
36982             }else{
36983                 this.setActivePanel(panel);
36984             }
36985         }
36986         return panel;
36987     },
36988
36989     /**
36990      * Get the active panel for this region.
36991      * @return {Roo.ContentPanel} The active panel or null
36992      */
36993     getActivePanel : function(){
36994         return this.activePanel;
36995     },
36996
36997     validateVisibility : function(){
36998         if(this.panels.getCount() < 1){
36999             this.updateTitle("&#160;");
37000             this.closeBtn.hide();
37001             this.hide();
37002         }else{
37003             if(!this.isVisible()){
37004                 this.show();
37005             }
37006         }
37007     },
37008
37009     /**
37010      * Adds the passed ContentPanel(s) to this region.
37011      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37012      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
37013      */
37014     add : function(panel)
37015     {
37016         if(arguments.length > 1){
37017             for(var i = 0, len = arguments.length; i < len; i++) {
37018                 this.add(arguments[i]);
37019             }
37020             return null;
37021         }
37022         
37023         // if we have not been rendered yet, then we can not really do much of this..
37024         if (!this.bodyEl) {
37025             this.unrendered_panels.push(panel);
37026             return panel;
37027         }
37028         
37029         
37030         
37031         
37032         if(this.hasPanel(panel)){
37033             this.showPanel(panel);
37034             return panel;
37035         }
37036         panel.setRegion(this);
37037         this.panels.add(panel);
37038        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
37039             // sinle panel - no tab...?? would it not be better to render it with the tabs,
37040             // and hide them... ???
37041             this.bodyEl.dom.appendChild(panel.getEl().dom);
37042             if(panel.background !== true){
37043                 this.setActivePanel(panel);
37044             }
37045             this.fireEvent("paneladded", this, panel);
37046             return panel;
37047         }
37048         */
37049         if(!this.tabs){
37050             this.initTabs();
37051         }else{
37052             this.initPanelAsTab(panel);
37053         }
37054         
37055         
37056         if(panel.background !== true){
37057             this.tabs.activate(panel.getEl().id);
37058         }
37059         this.fireEvent("paneladded", this, panel);
37060         return panel;
37061     },
37062
37063     /**
37064      * Hides the tab for the specified panel.
37065      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37066      */
37067     hidePanel : function(panel){
37068         if(this.tabs && (panel = this.getPanel(panel))){
37069             this.tabs.hideTab(panel.getEl().id);
37070         }
37071     },
37072
37073     /**
37074      * Unhides the tab for a previously hidden panel.
37075      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37076      */
37077     unhidePanel : function(panel){
37078         if(this.tabs && (panel = this.getPanel(panel))){
37079             this.tabs.unhideTab(panel.getEl().id);
37080         }
37081     },
37082
37083     clearPanels : function(){
37084         while(this.panels.getCount() > 0){
37085              this.remove(this.panels.first());
37086         }
37087     },
37088
37089     /**
37090      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37091      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
37092      * @param {Boolean} preservePanel Overrides the config preservePanel option
37093      * @return {Roo.ContentPanel} The panel that was removed
37094      */
37095     remove : function(panel, preservePanel)
37096     {
37097         panel = this.getPanel(panel);
37098         if(!panel){
37099             return null;
37100         }
37101         var e = {};
37102         this.fireEvent("beforeremove", this, panel, e);
37103         if(e.cancel === true){
37104             return null;
37105         }
37106         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
37107         var panelId = panel.getId();
37108         this.panels.removeKey(panelId);
37109         if(preservePanel){
37110             document.body.appendChild(panel.getEl().dom);
37111         }
37112         if(this.tabs){
37113             this.tabs.removeTab(panel.getEl().id);
37114         }else if (!preservePanel){
37115             this.bodyEl.dom.removeChild(panel.getEl().dom);
37116         }
37117         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
37118             var p = this.panels.first();
37119             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
37120             tempEl.appendChild(p.getEl().dom);
37121             this.bodyEl.update("");
37122             this.bodyEl.dom.appendChild(p.getEl().dom);
37123             tempEl = null;
37124             this.updateTitle(p.getTitle());
37125             this.tabs = null;
37126             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
37127             this.setActivePanel(p);
37128         }
37129         panel.setRegion(null);
37130         if(this.activePanel == panel){
37131             this.activePanel = null;
37132         }
37133         if(this.config.autoDestroy !== false && preservePanel !== true){
37134             try{panel.destroy();}catch(e){}
37135         }
37136         this.fireEvent("panelremoved", this, panel);
37137         return panel;
37138     },
37139
37140     /**
37141      * Returns the TabPanel component used by this region
37142      * @return {Roo.TabPanel}
37143      */
37144     getTabs : function(){
37145         return this.tabs;
37146     },
37147
37148     createTool : function(parentEl, className){
37149         var btn = Roo.DomHelper.append(parentEl, {
37150             tag: "div",
37151             cls: "x-layout-tools-button",
37152             children: [ {
37153                 tag: "div",
37154                 cls: "roo-layout-tools-button-inner " + className,
37155                 html: "&#160;"
37156             }]
37157         }, true);
37158         btn.addClassOnOver("roo-layout-tools-button-over");
37159         return btn;
37160     }
37161 });/*
37162  * Based on:
37163  * Ext JS Library 1.1.1
37164  * Copyright(c) 2006-2007, Ext JS, LLC.
37165  *
37166  * Originally Released Under LGPL - original licence link has changed is not relivant.
37167  *
37168  * Fork - LGPL
37169  * <script type="text/javascript">
37170  */
37171  
37172
37173
37174 /**
37175  * @class Roo.SplitLayoutRegion
37176  * @extends Roo.LayoutRegion
37177  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
37178  */
37179 Roo.bootstrap.layout.Split = function(config){
37180     this.cursor = config.cursor;
37181     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
37182 };
37183
37184 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
37185 {
37186     splitTip : "Drag to resize.",
37187     collapsibleSplitTip : "Drag to resize. Double click to hide.",
37188     useSplitTips : false,
37189
37190     applyConfig : function(config){
37191         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
37192     },
37193     
37194     onRender : function(ctr,pos) {
37195         
37196         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
37197         if(!this.config.split){
37198             return;
37199         }
37200         if(!this.split){
37201             
37202             var splitEl = Roo.DomHelper.append(ctr.dom,  {
37203                             tag: "div",
37204                             id: this.el.id + "-split",
37205                             cls: "roo-layout-split roo-layout-split-"+this.position,
37206                             html: "&#160;"
37207             });
37208             /** The SplitBar for this region 
37209             * @type Roo.SplitBar */
37210             // does not exist yet...
37211             Roo.log([this.position, this.orientation]);
37212             
37213             this.split = new Roo.bootstrap.SplitBar({
37214                 dragElement : splitEl,
37215                 resizingElement: this.el,
37216                 orientation : this.orientation
37217             });
37218             
37219             this.split.on("moved", this.onSplitMove, this);
37220             this.split.useShim = this.config.useShim === true;
37221             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
37222             if(this.useSplitTips){
37223                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
37224             }
37225             //if(config.collapsible){
37226             //    this.split.el.on("dblclick", this.collapse,  this);
37227             //}
37228         }
37229         if(typeof this.config.minSize != "undefined"){
37230             this.split.minSize = this.config.minSize;
37231         }
37232         if(typeof this.config.maxSize != "undefined"){
37233             this.split.maxSize = this.config.maxSize;
37234         }
37235         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
37236             this.hideSplitter();
37237         }
37238         
37239     },
37240
37241     getHMaxSize : function(){
37242          var cmax = this.config.maxSize || 10000;
37243          var center = this.mgr.getRegion("center");
37244          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
37245     },
37246
37247     getVMaxSize : function(){
37248          var cmax = this.config.maxSize || 10000;
37249          var center = this.mgr.getRegion("center");
37250          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
37251     },
37252
37253     onSplitMove : function(split, newSize){
37254         this.fireEvent("resized", this, newSize);
37255     },
37256     
37257     /** 
37258      * Returns the {@link Roo.SplitBar} for this region.
37259      * @return {Roo.SplitBar}
37260      */
37261     getSplitBar : function(){
37262         return this.split;
37263     },
37264     
37265     hide : function(){
37266         this.hideSplitter();
37267         Roo.bootstrap.layout.Split.superclass.hide.call(this);
37268     },
37269
37270     hideSplitter : function(){
37271         if(this.split){
37272             this.split.el.setLocation(-2000,-2000);
37273             this.split.el.hide();
37274         }
37275     },
37276
37277     show : function(){
37278         if(this.split){
37279             this.split.el.show();
37280         }
37281         Roo.bootstrap.layout.Split.superclass.show.call(this);
37282     },
37283     
37284     beforeSlide: function(){
37285         if(Roo.isGecko){// firefox overflow auto bug workaround
37286             this.bodyEl.clip();
37287             if(this.tabs) {
37288                 this.tabs.bodyEl.clip();
37289             }
37290             if(this.activePanel){
37291                 this.activePanel.getEl().clip();
37292                 
37293                 if(this.activePanel.beforeSlide){
37294                     this.activePanel.beforeSlide();
37295                 }
37296             }
37297         }
37298     },
37299     
37300     afterSlide : function(){
37301         if(Roo.isGecko){// firefox overflow auto bug workaround
37302             this.bodyEl.unclip();
37303             if(this.tabs) {
37304                 this.tabs.bodyEl.unclip();
37305             }
37306             if(this.activePanel){
37307                 this.activePanel.getEl().unclip();
37308                 if(this.activePanel.afterSlide){
37309                     this.activePanel.afterSlide();
37310                 }
37311             }
37312         }
37313     },
37314
37315     initAutoHide : function(){
37316         if(this.autoHide !== false){
37317             if(!this.autoHideHd){
37318                 var st = new Roo.util.DelayedTask(this.slideIn, this);
37319                 this.autoHideHd = {
37320                     "mouseout": function(e){
37321                         if(!e.within(this.el, true)){
37322                             st.delay(500);
37323                         }
37324                     },
37325                     "mouseover" : function(e){
37326                         st.cancel();
37327                     },
37328                     scope : this
37329                 };
37330             }
37331             this.el.on(this.autoHideHd);
37332         }
37333     },
37334
37335     clearAutoHide : function(){
37336         if(this.autoHide !== false){
37337             this.el.un("mouseout", this.autoHideHd.mouseout);
37338             this.el.un("mouseover", this.autoHideHd.mouseover);
37339         }
37340     },
37341
37342     clearMonitor : function(){
37343         Roo.get(document).un("click", this.slideInIf, this);
37344     },
37345
37346     // these names are backwards but not changed for compat
37347     slideOut : function(){
37348         if(this.isSlid || this.el.hasActiveFx()){
37349             return;
37350         }
37351         this.isSlid = true;
37352         if(this.collapseBtn){
37353             this.collapseBtn.hide();
37354         }
37355         this.closeBtnState = this.closeBtn.getStyle('display');
37356         this.closeBtn.hide();
37357         if(this.stickBtn){
37358             this.stickBtn.show();
37359         }
37360         this.el.show();
37361         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
37362         this.beforeSlide();
37363         this.el.setStyle("z-index", 10001);
37364         this.el.slideIn(this.getSlideAnchor(), {
37365             callback: function(){
37366                 this.afterSlide();
37367                 this.initAutoHide();
37368                 Roo.get(document).on("click", this.slideInIf, this);
37369                 this.fireEvent("slideshow", this);
37370             },
37371             scope: this,
37372             block: true
37373         });
37374     },
37375
37376     afterSlideIn : function(){
37377         this.clearAutoHide();
37378         this.isSlid = false;
37379         this.clearMonitor();
37380         this.el.setStyle("z-index", "");
37381         if(this.collapseBtn){
37382             this.collapseBtn.show();
37383         }
37384         this.closeBtn.setStyle('display', this.closeBtnState);
37385         if(this.stickBtn){
37386             this.stickBtn.hide();
37387         }
37388         this.fireEvent("slidehide", this);
37389     },
37390
37391     slideIn : function(cb){
37392         if(!this.isSlid || this.el.hasActiveFx()){
37393             Roo.callback(cb);
37394             return;
37395         }
37396         this.isSlid = false;
37397         this.beforeSlide();
37398         this.el.slideOut(this.getSlideAnchor(), {
37399             callback: function(){
37400                 this.el.setLeftTop(-10000, -10000);
37401                 this.afterSlide();
37402                 this.afterSlideIn();
37403                 Roo.callback(cb);
37404             },
37405             scope: this,
37406             block: true
37407         });
37408     },
37409     
37410     slideInIf : function(e){
37411         if(!e.within(this.el)){
37412             this.slideIn();
37413         }
37414     },
37415
37416     animateCollapse : function(){
37417         this.beforeSlide();
37418         this.el.setStyle("z-index", 20000);
37419         var anchor = this.getSlideAnchor();
37420         this.el.slideOut(anchor, {
37421             callback : function(){
37422                 this.el.setStyle("z-index", "");
37423                 this.collapsedEl.slideIn(anchor, {duration:.3});
37424                 this.afterSlide();
37425                 this.el.setLocation(-10000,-10000);
37426                 this.el.hide();
37427                 this.fireEvent("collapsed", this);
37428             },
37429             scope: this,
37430             block: true
37431         });
37432     },
37433
37434     animateExpand : function(){
37435         this.beforeSlide();
37436         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37437         this.el.setStyle("z-index", 20000);
37438         this.collapsedEl.hide({
37439             duration:.1
37440         });
37441         this.el.slideIn(this.getSlideAnchor(), {
37442             callback : function(){
37443                 this.el.setStyle("z-index", "");
37444                 this.afterSlide();
37445                 if(this.split){
37446                     this.split.el.show();
37447                 }
37448                 this.fireEvent("invalidated", this);
37449                 this.fireEvent("expanded", this);
37450             },
37451             scope: this,
37452             block: true
37453         });
37454     },
37455
37456     anchors : {
37457         "west" : "left",
37458         "east" : "right",
37459         "north" : "top",
37460         "south" : "bottom"
37461     },
37462
37463     sanchors : {
37464         "west" : "l",
37465         "east" : "r",
37466         "north" : "t",
37467         "south" : "b"
37468     },
37469
37470     canchors : {
37471         "west" : "tl-tr",
37472         "east" : "tr-tl",
37473         "north" : "tl-bl",
37474         "south" : "bl-tl"
37475     },
37476
37477     getAnchor : function(){
37478         return this.anchors[this.position];
37479     },
37480
37481     getCollapseAnchor : function(){
37482         return this.canchors[this.position];
37483     },
37484
37485     getSlideAnchor : function(){
37486         return this.sanchors[this.position];
37487     },
37488
37489     getAlignAdj : function(){
37490         var cm = this.cmargins;
37491         switch(this.position){
37492             case "west":
37493                 return [0, 0];
37494             break;
37495             case "east":
37496                 return [0, 0];
37497             break;
37498             case "north":
37499                 return [0, 0];
37500             break;
37501             case "south":
37502                 return [0, 0];
37503             break;
37504         }
37505     },
37506
37507     getExpandAdj : function(){
37508         var c = this.collapsedEl, cm = this.cmargins;
37509         switch(this.position){
37510             case "west":
37511                 return [-(cm.right+c.getWidth()+cm.left), 0];
37512             break;
37513             case "east":
37514                 return [cm.right+c.getWidth()+cm.left, 0];
37515             break;
37516             case "north":
37517                 return [0, -(cm.top+cm.bottom+c.getHeight())];
37518             break;
37519             case "south":
37520                 return [0, cm.top+cm.bottom+c.getHeight()];
37521             break;
37522         }
37523     }
37524 });/*
37525  * Based on:
37526  * Ext JS Library 1.1.1
37527  * Copyright(c) 2006-2007, Ext JS, LLC.
37528  *
37529  * Originally Released Under LGPL - original licence link has changed is not relivant.
37530  *
37531  * Fork - LGPL
37532  * <script type="text/javascript">
37533  */
37534 /*
37535  * These classes are private internal classes
37536  */
37537 Roo.bootstrap.layout.Center = function(config){
37538     config.region = "center";
37539     Roo.bootstrap.layout.Region.call(this, config);
37540     this.visible = true;
37541     this.minWidth = config.minWidth || 20;
37542     this.minHeight = config.minHeight || 20;
37543 };
37544
37545 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37546     hide : function(){
37547         // center panel can't be hidden
37548     },
37549     
37550     show : function(){
37551         // center panel can't be hidden
37552     },
37553     
37554     getMinWidth: function(){
37555         return this.minWidth;
37556     },
37557     
37558     getMinHeight: function(){
37559         return this.minHeight;
37560     }
37561 });
37562
37563
37564
37565
37566  
37567
37568
37569
37570
37571
37572
37573 Roo.bootstrap.layout.North = function(config)
37574 {
37575     config.region = 'north';
37576     config.cursor = 'n-resize';
37577     
37578     Roo.bootstrap.layout.Split.call(this, config);
37579     
37580     
37581     if(this.split){
37582         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37583         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37584         this.split.el.addClass("roo-layout-split-v");
37585     }
37586     var size = config.initialSize || config.height;
37587     if(typeof size != "undefined"){
37588         this.el.setHeight(size);
37589     }
37590 };
37591 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37592 {
37593     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37594     
37595     
37596     
37597     getBox : function(){
37598         if(this.collapsed){
37599             return this.collapsedEl.getBox();
37600         }
37601         var box = this.el.getBox();
37602         if(this.split){
37603             box.height += this.split.el.getHeight();
37604         }
37605         return box;
37606     },
37607     
37608     updateBox : function(box){
37609         if(this.split && !this.collapsed){
37610             box.height -= this.split.el.getHeight();
37611             this.split.el.setLeft(box.x);
37612             this.split.el.setTop(box.y+box.height);
37613             this.split.el.setWidth(box.width);
37614         }
37615         if(this.collapsed){
37616             this.updateBody(box.width, null);
37617         }
37618         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37619     }
37620 });
37621
37622
37623
37624
37625
37626 Roo.bootstrap.layout.South = function(config){
37627     config.region = 'south';
37628     config.cursor = 's-resize';
37629     Roo.bootstrap.layout.Split.call(this, config);
37630     if(this.split){
37631         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37632         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37633         this.split.el.addClass("roo-layout-split-v");
37634     }
37635     var size = config.initialSize || config.height;
37636     if(typeof size != "undefined"){
37637         this.el.setHeight(size);
37638     }
37639 };
37640
37641 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37642     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37643     getBox : function(){
37644         if(this.collapsed){
37645             return this.collapsedEl.getBox();
37646         }
37647         var box = this.el.getBox();
37648         if(this.split){
37649             var sh = this.split.el.getHeight();
37650             box.height += sh;
37651             box.y -= sh;
37652         }
37653         return box;
37654     },
37655     
37656     updateBox : function(box){
37657         if(this.split && !this.collapsed){
37658             var sh = this.split.el.getHeight();
37659             box.height -= sh;
37660             box.y += sh;
37661             this.split.el.setLeft(box.x);
37662             this.split.el.setTop(box.y-sh);
37663             this.split.el.setWidth(box.width);
37664         }
37665         if(this.collapsed){
37666             this.updateBody(box.width, null);
37667         }
37668         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37669     }
37670 });
37671
37672 Roo.bootstrap.layout.East = function(config){
37673     config.region = "east";
37674     config.cursor = "e-resize";
37675     Roo.bootstrap.layout.Split.call(this, config);
37676     if(this.split){
37677         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37678         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37679         this.split.el.addClass("roo-layout-split-h");
37680     }
37681     var size = config.initialSize || config.width;
37682     if(typeof size != "undefined"){
37683         this.el.setWidth(size);
37684     }
37685 };
37686 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37687     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37688     getBox : function(){
37689         if(this.collapsed){
37690             return this.collapsedEl.getBox();
37691         }
37692         var box = this.el.getBox();
37693         if(this.split){
37694             var sw = this.split.el.getWidth();
37695             box.width += sw;
37696             box.x -= sw;
37697         }
37698         return box;
37699     },
37700
37701     updateBox : function(box){
37702         if(this.split && !this.collapsed){
37703             var sw = this.split.el.getWidth();
37704             box.width -= sw;
37705             this.split.el.setLeft(box.x);
37706             this.split.el.setTop(box.y);
37707             this.split.el.setHeight(box.height);
37708             box.x += sw;
37709         }
37710         if(this.collapsed){
37711             this.updateBody(null, box.height);
37712         }
37713         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37714     }
37715 });
37716
37717 Roo.bootstrap.layout.West = function(config){
37718     config.region = "west";
37719     config.cursor = "w-resize";
37720     
37721     Roo.bootstrap.layout.Split.call(this, config);
37722     if(this.split){
37723         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37724         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37725         this.split.el.addClass("roo-layout-split-h");
37726     }
37727     
37728 };
37729 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37730     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37731     
37732     onRender: function(ctr, pos)
37733     {
37734         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37735         var size = this.config.initialSize || this.config.width;
37736         if(typeof size != "undefined"){
37737             this.el.setWidth(size);
37738         }
37739     },
37740     
37741     getBox : function(){
37742         if(this.collapsed){
37743             return this.collapsedEl.getBox();
37744         }
37745         var box = this.el.getBox();
37746         if(this.split){
37747             box.width += this.split.el.getWidth();
37748         }
37749         return box;
37750     },
37751     
37752     updateBox : function(box){
37753         if(this.split && !this.collapsed){
37754             var sw = this.split.el.getWidth();
37755             box.width -= sw;
37756             this.split.el.setLeft(box.x+box.width);
37757             this.split.el.setTop(box.y);
37758             this.split.el.setHeight(box.height);
37759         }
37760         if(this.collapsed){
37761             this.updateBody(null, box.height);
37762         }
37763         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37764     }
37765 });Roo.namespace("Roo.bootstrap.panel");/*
37766  * Based on:
37767  * Ext JS Library 1.1.1
37768  * Copyright(c) 2006-2007, Ext JS, LLC.
37769  *
37770  * Originally Released Under LGPL - original licence link has changed is not relivant.
37771  *
37772  * Fork - LGPL
37773  * <script type="text/javascript">
37774  */
37775 /**
37776  * @class Roo.ContentPanel
37777  * @extends Roo.util.Observable
37778  * A basic ContentPanel element.
37779  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37780  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37781  * @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
37782  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37783  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37784  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37785  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37786  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37787  * @cfg {String} title          The title for this panel
37788  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37789  * @cfg {String} url            Calls {@link #setUrl} with this value
37790  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37791  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37792  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37793  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37794  * @cfg {Boolean} badges render the badges
37795
37796  * @constructor
37797  * Create a new ContentPanel.
37798  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37799  * @param {String/Object} config A string to set only the title or a config object
37800  * @param {String} content (optional) Set the HTML content for this panel
37801  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37802  */
37803 Roo.bootstrap.panel.Content = function( config){
37804     
37805     this.tpl = config.tpl || false;
37806     
37807     var el = config.el;
37808     var content = config.content;
37809
37810     if(config.autoCreate){ // xtype is available if this is called from factory
37811         el = Roo.id();
37812     }
37813     this.el = Roo.get(el);
37814     if(!this.el && config && config.autoCreate){
37815         if(typeof config.autoCreate == "object"){
37816             if(!config.autoCreate.id){
37817                 config.autoCreate.id = config.id||el;
37818             }
37819             this.el = Roo.DomHelper.append(document.body,
37820                         config.autoCreate, true);
37821         }else{
37822             var elcfg =  {   tag: "div",
37823                             cls: "roo-layout-inactive-content",
37824                             id: config.id||el
37825                             };
37826             if (config.html) {
37827                 elcfg.html = config.html;
37828                 
37829             }
37830                         
37831             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37832         }
37833     } 
37834     this.closable = false;
37835     this.loaded = false;
37836     this.active = false;
37837    
37838       
37839     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37840         
37841         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37842         
37843         this.wrapEl = this.el; //this.el.wrap();
37844         var ti = [];
37845         if (config.toolbar.items) {
37846             ti = config.toolbar.items ;
37847             delete config.toolbar.items ;
37848         }
37849         
37850         var nitems = [];
37851         this.toolbar.render(this.wrapEl, 'before');
37852         for(var i =0;i < ti.length;i++) {
37853           //  Roo.log(['add child', items[i]]);
37854             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37855         }
37856         this.toolbar.items = nitems;
37857         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37858         delete config.toolbar;
37859         
37860     }
37861     /*
37862     // xtype created footer. - not sure if will work as we normally have to render first..
37863     if (this.footer && !this.footer.el && this.footer.xtype) {
37864         if (!this.wrapEl) {
37865             this.wrapEl = this.el.wrap();
37866         }
37867     
37868         this.footer.container = this.wrapEl.createChild();
37869          
37870         this.footer = Roo.factory(this.footer, Roo);
37871         
37872     }
37873     */
37874     
37875      if(typeof config == "string"){
37876         this.title = config;
37877     }else{
37878         Roo.apply(this, config);
37879     }
37880     
37881     if(this.resizeEl){
37882         this.resizeEl = Roo.get(this.resizeEl, true);
37883     }else{
37884         this.resizeEl = this.el;
37885     }
37886     // handle view.xtype
37887     
37888  
37889     
37890     
37891     this.addEvents({
37892         /**
37893          * @event activate
37894          * Fires when this panel is activated. 
37895          * @param {Roo.ContentPanel} this
37896          */
37897         "activate" : true,
37898         /**
37899          * @event deactivate
37900          * Fires when this panel is activated. 
37901          * @param {Roo.ContentPanel} this
37902          */
37903         "deactivate" : true,
37904
37905         /**
37906          * @event resize
37907          * Fires when this panel is resized if fitToFrame is true.
37908          * @param {Roo.ContentPanel} this
37909          * @param {Number} width The width after any component adjustments
37910          * @param {Number} height The height after any component adjustments
37911          */
37912         "resize" : true,
37913         
37914          /**
37915          * @event render
37916          * Fires when this tab is created
37917          * @param {Roo.ContentPanel} this
37918          */
37919         "render" : true
37920         
37921         
37922         
37923     });
37924     
37925
37926     
37927     
37928     if(this.autoScroll){
37929         this.resizeEl.setStyle("overflow", "auto");
37930     } else {
37931         // fix randome scrolling
37932         //this.el.on('scroll', function() {
37933         //    Roo.log('fix random scolling');
37934         //    this.scrollTo('top',0); 
37935         //});
37936     }
37937     content = content || this.content;
37938     if(content){
37939         this.setContent(content);
37940     }
37941     if(config && config.url){
37942         this.setUrl(this.url, this.params, this.loadOnce);
37943     }
37944     
37945     
37946     
37947     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37948     
37949     if (this.view && typeof(this.view.xtype) != 'undefined') {
37950         this.view.el = this.el.appendChild(document.createElement("div"));
37951         this.view = Roo.factory(this.view); 
37952         this.view.render  &&  this.view.render(false, '');  
37953     }
37954     
37955     
37956     this.fireEvent('render', this);
37957 };
37958
37959 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37960     
37961     tabTip : '',
37962     
37963     setRegion : function(region){
37964         this.region = region;
37965         this.setActiveClass(region && !this.background);
37966     },
37967     
37968     
37969     setActiveClass: function(state)
37970     {
37971         if(state){
37972            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37973            this.el.setStyle('position','relative');
37974         }else{
37975            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37976            this.el.setStyle('position', 'absolute');
37977         } 
37978     },
37979     
37980     /**
37981      * Returns the toolbar for this Panel if one was configured. 
37982      * @return {Roo.Toolbar} 
37983      */
37984     getToolbar : function(){
37985         return this.toolbar;
37986     },
37987     
37988     setActiveState : function(active)
37989     {
37990         this.active = active;
37991         this.setActiveClass(active);
37992         if(!active){
37993             if(this.fireEvent("deactivate", this) === false){
37994                 return false;
37995             }
37996             return true;
37997         }
37998         this.fireEvent("activate", this);
37999         return true;
38000     },
38001     /**
38002      * Updates this panel's element
38003      * @param {String} content The new content
38004      * @param {Boolean} loadScripts (optional) true to look for and process scripts
38005     */
38006     setContent : function(content, loadScripts){
38007         this.el.update(content, loadScripts);
38008     },
38009
38010     ignoreResize : function(w, h){
38011         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
38012             return true;
38013         }else{
38014             this.lastSize = {width: w, height: h};
38015             return false;
38016         }
38017     },
38018     /**
38019      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
38020      * @return {Roo.UpdateManager} The UpdateManager
38021      */
38022     getUpdateManager : function(){
38023         return this.el.getUpdateManager();
38024     },
38025      /**
38026      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
38027      * @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:
38028 <pre><code>
38029 panel.load({
38030     url: "your-url.php",
38031     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
38032     callback: yourFunction,
38033     scope: yourObject, //(optional scope)
38034     discardUrl: false,
38035     nocache: false,
38036     text: "Loading...",
38037     timeout: 30,
38038     scripts: false
38039 });
38040 </code></pre>
38041      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
38042      * 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.
38043      * @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}
38044      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
38045      * @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.
38046      * @return {Roo.ContentPanel} this
38047      */
38048     load : function(){
38049         var um = this.el.getUpdateManager();
38050         um.update.apply(um, arguments);
38051         return this;
38052     },
38053
38054
38055     /**
38056      * 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.
38057      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
38058      * @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)
38059      * @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)
38060      * @return {Roo.UpdateManager} The UpdateManager
38061      */
38062     setUrl : function(url, params, loadOnce){
38063         if(this.refreshDelegate){
38064             this.removeListener("activate", this.refreshDelegate);
38065         }
38066         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38067         this.on("activate", this.refreshDelegate);
38068         return this.el.getUpdateManager();
38069     },
38070     
38071     _handleRefresh : function(url, params, loadOnce){
38072         if(!loadOnce || !this.loaded){
38073             var updater = this.el.getUpdateManager();
38074             updater.update(url, params, this._setLoaded.createDelegate(this));
38075         }
38076     },
38077     
38078     _setLoaded : function(){
38079         this.loaded = true;
38080     }, 
38081     
38082     /**
38083      * Returns this panel's id
38084      * @return {String} 
38085      */
38086     getId : function(){
38087         return this.el.id;
38088     },
38089     
38090     /** 
38091      * Returns this panel's element - used by regiosn to add.
38092      * @return {Roo.Element} 
38093      */
38094     getEl : function(){
38095         return this.wrapEl || this.el;
38096     },
38097     
38098    
38099     
38100     adjustForComponents : function(width, height)
38101     {
38102         //Roo.log('adjustForComponents ');
38103         if(this.resizeEl != this.el){
38104             width -= this.el.getFrameWidth('lr');
38105             height -= this.el.getFrameWidth('tb');
38106         }
38107         if(this.toolbar){
38108             var te = this.toolbar.getEl();
38109             te.setWidth(width);
38110             height -= te.getHeight();
38111         }
38112         if(this.footer){
38113             var te = this.footer.getEl();
38114             te.setWidth(width);
38115             height -= te.getHeight();
38116         }
38117         
38118         
38119         if(this.adjustments){
38120             width += this.adjustments[0];
38121             height += this.adjustments[1];
38122         }
38123         return {"width": width, "height": height};
38124     },
38125     
38126     setSize : function(width, height){
38127         if(this.fitToFrame && !this.ignoreResize(width, height)){
38128             if(this.fitContainer && this.resizeEl != this.el){
38129                 this.el.setSize(width, height);
38130             }
38131             var size = this.adjustForComponents(width, height);
38132             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
38133             this.fireEvent('resize', this, size.width, size.height);
38134         }
38135     },
38136     
38137     /**
38138      * Returns this panel's title
38139      * @return {String} 
38140      */
38141     getTitle : function(){
38142         
38143         if (typeof(this.title) != 'object') {
38144             return this.title;
38145         }
38146         
38147         var t = '';
38148         for (var k in this.title) {
38149             if (!this.title.hasOwnProperty(k)) {
38150                 continue;
38151             }
38152             
38153             if (k.indexOf('-') >= 0) {
38154                 var s = k.split('-');
38155                 for (var i = 0; i<s.length; i++) {
38156                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
38157                 }
38158             } else {
38159                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
38160             }
38161         }
38162         return t;
38163     },
38164     
38165     /**
38166      * Set this panel's title
38167      * @param {String} title
38168      */
38169     setTitle : function(title){
38170         this.title = title;
38171         if(this.region){
38172             this.region.updatePanelTitle(this, title);
38173         }
38174     },
38175     
38176     /**
38177      * Returns true is this panel was configured to be closable
38178      * @return {Boolean} 
38179      */
38180     isClosable : function(){
38181         return this.closable;
38182     },
38183     
38184     beforeSlide : function(){
38185         this.el.clip();
38186         this.resizeEl.clip();
38187     },
38188     
38189     afterSlide : function(){
38190         this.el.unclip();
38191         this.resizeEl.unclip();
38192     },
38193     
38194     /**
38195      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
38196      *   Will fail silently if the {@link #setUrl} method has not been called.
38197      *   This does not activate the panel, just updates its content.
38198      */
38199     refresh : function(){
38200         if(this.refreshDelegate){
38201            this.loaded = false;
38202            this.refreshDelegate();
38203         }
38204     },
38205     
38206     /**
38207      * Destroys this panel
38208      */
38209     destroy : function(){
38210         this.el.removeAllListeners();
38211         var tempEl = document.createElement("span");
38212         tempEl.appendChild(this.el.dom);
38213         tempEl.innerHTML = "";
38214         this.el.remove();
38215         this.el = null;
38216     },
38217     
38218     /**
38219      * form - if the content panel contains a form - this is a reference to it.
38220      * @type {Roo.form.Form}
38221      */
38222     form : false,
38223     /**
38224      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
38225      *    This contains a reference to it.
38226      * @type {Roo.View}
38227      */
38228     view : false,
38229     
38230       /**
38231      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
38232      * <pre><code>
38233
38234 layout.addxtype({
38235        xtype : 'Form',
38236        items: [ .... ]
38237    }
38238 );
38239
38240 </code></pre>
38241      * @param {Object} cfg Xtype definition of item to add.
38242      */
38243     
38244     
38245     getChildContainer: function () {
38246         return this.getEl();
38247     }
38248     
38249     
38250     /*
38251         var  ret = new Roo.factory(cfg);
38252         return ret;
38253         
38254         
38255         // add form..
38256         if (cfg.xtype.match(/^Form$/)) {
38257             
38258             var el;
38259             //if (this.footer) {
38260             //    el = this.footer.container.insertSibling(false, 'before');
38261             //} else {
38262                 el = this.el.createChild();
38263             //}
38264
38265             this.form = new  Roo.form.Form(cfg);
38266             
38267             
38268             if ( this.form.allItems.length) {
38269                 this.form.render(el.dom);
38270             }
38271             return this.form;
38272         }
38273         // should only have one of theses..
38274         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
38275             // views.. should not be just added - used named prop 'view''
38276             
38277             cfg.el = this.el.appendChild(document.createElement("div"));
38278             // factory?
38279             
38280             var ret = new Roo.factory(cfg);
38281              
38282              ret.render && ret.render(false, ''); // render blank..
38283             this.view = ret;
38284             return ret;
38285         }
38286         return false;
38287     }
38288     \*/
38289 });
38290  
38291 /**
38292  * @class Roo.bootstrap.panel.Grid
38293  * @extends Roo.bootstrap.panel.Content
38294  * @constructor
38295  * Create a new GridPanel.
38296  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
38297  * @param {Object} config A the config object
38298   
38299  */
38300
38301
38302
38303 Roo.bootstrap.panel.Grid = function(config)
38304 {
38305     
38306       
38307     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
38308         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
38309
38310     config.el = this.wrapper;
38311     //this.el = this.wrapper;
38312     
38313       if (config.container) {
38314         // ctor'ed from a Border/panel.grid
38315         
38316         
38317         this.wrapper.setStyle("overflow", "hidden");
38318         this.wrapper.addClass('roo-grid-container');
38319
38320     }
38321     
38322     
38323     if(config.toolbar){
38324         var tool_el = this.wrapper.createChild();    
38325         this.toolbar = Roo.factory(config.toolbar);
38326         var ti = [];
38327         if (config.toolbar.items) {
38328             ti = config.toolbar.items ;
38329             delete config.toolbar.items ;
38330         }
38331         
38332         var nitems = [];
38333         this.toolbar.render(tool_el);
38334         for(var i =0;i < ti.length;i++) {
38335           //  Roo.log(['add child', items[i]]);
38336             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
38337         }
38338         this.toolbar.items = nitems;
38339         
38340         delete config.toolbar;
38341     }
38342     
38343     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
38344     config.grid.scrollBody = true;;
38345     config.grid.monitorWindowResize = false; // turn off autosizing
38346     config.grid.autoHeight = false;
38347     config.grid.autoWidth = false;
38348     
38349     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
38350     
38351     if (config.background) {
38352         // render grid on panel activation (if panel background)
38353         this.on('activate', function(gp) {
38354             if (!gp.grid.rendered) {
38355                 gp.grid.render(this.wrapper);
38356                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
38357             }
38358         });
38359             
38360     } else {
38361         this.grid.render(this.wrapper);
38362         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
38363
38364     }
38365     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
38366     // ??? needed ??? config.el = this.wrapper;
38367     
38368     
38369     
38370   
38371     // xtype created footer. - not sure if will work as we normally have to render first..
38372     if (this.footer && !this.footer.el && this.footer.xtype) {
38373         
38374         var ctr = this.grid.getView().getFooterPanel(true);
38375         this.footer.dataSource = this.grid.dataSource;
38376         this.footer = Roo.factory(this.footer, Roo);
38377         this.footer.render(ctr);
38378         
38379     }
38380     
38381     
38382     
38383     
38384      
38385 };
38386
38387 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38388     getId : function(){
38389         return this.grid.id;
38390     },
38391     
38392     /**
38393      * Returns the grid for this panel
38394      * @return {Roo.bootstrap.Table} 
38395      */
38396     getGrid : function(){
38397         return this.grid;    
38398     },
38399     
38400     setSize : function(width, height){
38401         if(!this.ignoreResize(width, height)){
38402             var grid = this.grid;
38403             var size = this.adjustForComponents(width, height);
38404             var gridel = grid.getGridEl();
38405             gridel.setSize(size.width, size.height);
38406             /*
38407             var thd = grid.getGridEl().select('thead',true).first();
38408             var tbd = grid.getGridEl().select('tbody', true).first();
38409             if (tbd) {
38410                 tbd.setSize(width, height - thd.getHeight());
38411             }
38412             */
38413             grid.autoSize();
38414         }
38415     },
38416      
38417     
38418     
38419     beforeSlide : function(){
38420         this.grid.getView().scroller.clip();
38421     },
38422     
38423     afterSlide : function(){
38424         this.grid.getView().scroller.unclip();
38425     },
38426     
38427     destroy : function(){
38428         this.grid.destroy();
38429         delete this.grid;
38430         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
38431     }
38432 });
38433
38434 /**
38435  * @class Roo.bootstrap.panel.Nest
38436  * @extends Roo.bootstrap.panel.Content
38437  * @constructor
38438  * Create a new Panel, that can contain a layout.Border.
38439  * 
38440  * 
38441  * @param {Roo.BorderLayout} layout The layout for this panel
38442  * @param {String/Object} config A string to set only the title or a config object
38443  */
38444 Roo.bootstrap.panel.Nest = function(config)
38445 {
38446     // construct with only one argument..
38447     /* FIXME - implement nicer consturctors
38448     if (layout.layout) {
38449         config = layout;
38450         layout = config.layout;
38451         delete config.layout;
38452     }
38453     if (layout.xtype && !layout.getEl) {
38454         // then layout needs constructing..
38455         layout = Roo.factory(layout, Roo);
38456     }
38457     */
38458     
38459     config.el =  config.layout.getEl();
38460     
38461     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
38462     
38463     config.layout.monitorWindowResize = false; // turn off autosizing
38464     this.layout = config.layout;
38465     this.layout.getEl().addClass("roo-layout-nested-layout");
38466     this.layout.parent = this;
38467     
38468     
38469     
38470     
38471 };
38472
38473 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38474
38475     setSize : function(width, height){
38476         if(!this.ignoreResize(width, height)){
38477             var size = this.adjustForComponents(width, height);
38478             var el = this.layout.getEl();
38479             if (size.height < 1) {
38480                 el.setWidth(size.width);   
38481             } else {
38482                 el.setSize(size.width, size.height);
38483             }
38484             var touch = el.dom.offsetWidth;
38485             this.layout.layout();
38486             // ie requires a double layout on the first pass
38487             if(Roo.isIE && !this.initialized){
38488                 this.initialized = true;
38489                 this.layout.layout();
38490             }
38491         }
38492     },
38493     
38494     // activate all subpanels if not currently active..
38495     
38496     setActiveState : function(active){
38497         this.active = active;
38498         this.setActiveClass(active);
38499         
38500         if(!active){
38501             this.fireEvent("deactivate", this);
38502             return;
38503         }
38504         
38505         this.fireEvent("activate", this);
38506         // not sure if this should happen before or after..
38507         if (!this.layout) {
38508             return; // should not happen..
38509         }
38510         var reg = false;
38511         for (var r in this.layout.regions) {
38512             reg = this.layout.getRegion(r);
38513             if (reg.getActivePanel()) {
38514                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
38515                 reg.setActivePanel(reg.getActivePanel());
38516                 continue;
38517             }
38518             if (!reg.panels.length) {
38519                 continue;
38520             }
38521             reg.showPanel(reg.getPanel(0));
38522         }
38523         
38524         
38525         
38526         
38527     },
38528     
38529     /**
38530      * Returns the nested BorderLayout for this panel
38531      * @return {Roo.BorderLayout} 
38532      */
38533     getLayout : function(){
38534         return this.layout;
38535     },
38536     
38537      /**
38538      * Adds a xtype elements to the layout of the nested panel
38539      * <pre><code>
38540
38541 panel.addxtype({
38542        xtype : 'ContentPanel',
38543        region: 'west',
38544        items: [ .... ]
38545    }
38546 );
38547
38548 panel.addxtype({
38549         xtype : 'NestedLayoutPanel',
38550         region: 'west',
38551         layout: {
38552            center: { },
38553            west: { }   
38554         },
38555         items : [ ... list of content panels or nested layout panels.. ]
38556    }
38557 );
38558 </code></pre>
38559      * @param {Object} cfg Xtype definition of item to add.
38560      */
38561     addxtype : function(cfg) {
38562         return this.layout.addxtype(cfg);
38563     
38564     }
38565 });/*
38566  * Based on:
38567  * Ext JS Library 1.1.1
38568  * Copyright(c) 2006-2007, Ext JS, LLC.
38569  *
38570  * Originally Released Under LGPL - original licence link has changed is not relivant.
38571  *
38572  * Fork - LGPL
38573  * <script type="text/javascript">
38574  */
38575 /**
38576  * @class Roo.TabPanel
38577  * @extends Roo.util.Observable
38578  * A lightweight tab container.
38579  * <br><br>
38580  * Usage:
38581  * <pre><code>
38582 // basic tabs 1, built from existing content
38583 var tabs = new Roo.TabPanel("tabs1");
38584 tabs.addTab("script", "View Script");
38585 tabs.addTab("markup", "View Markup");
38586 tabs.activate("script");
38587
38588 // more advanced tabs, built from javascript
38589 var jtabs = new Roo.TabPanel("jtabs");
38590 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38591
38592 // set up the UpdateManager
38593 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38594 var updater = tab2.getUpdateManager();
38595 updater.setDefaultUrl("ajax1.htm");
38596 tab2.on('activate', updater.refresh, updater, true);
38597
38598 // Use setUrl for Ajax loading
38599 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38600 tab3.setUrl("ajax2.htm", null, true);
38601
38602 // Disabled tab
38603 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38604 tab4.disable();
38605
38606 jtabs.activate("jtabs-1");
38607  * </code></pre>
38608  * @constructor
38609  * Create a new TabPanel.
38610  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38611  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38612  */
38613 Roo.bootstrap.panel.Tabs = function(config){
38614     /**
38615     * The container element for this TabPanel.
38616     * @type Roo.Element
38617     */
38618     this.el = Roo.get(config.el);
38619     delete config.el;
38620     if(config){
38621         if(typeof config == "boolean"){
38622             this.tabPosition = config ? "bottom" : "top";
38623         }else{
38624             Roo.apply(this, config);
38625         }
38626     }
38627     
38628     if(this.tabPosition == "bottom"){
38629         // if tabs are at the bottom = create the body first.
38630         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38631         this.el.addClass("roo-tabs-bottom");
38632     }
38633     // next create the tabs holders
38634     
38635     if (this.tabPosition == "west"){
38636         
38637         var reg = this.region; // fake it..
38638         while (reg) {
38639             if (!reg.mgr.parent) {
38640                 break;
38641             }
38642             reg = reg.mgr.parent.region;
38643         }
38644         Roo.log("got nest?");
38645         Roo.log(reg);
38646         if (reg.mgr.getRegion('west')) {
38647             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38648             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38649             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38650             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38651             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38652         
38653             
38654         }
38655         
38656         
38657     } else {
38658      
38659         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38660         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38661         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38662         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38663     }
38664     
38665     
38666     if(Roo.isIE){
38667         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38668     }
38669     
38670     // finally - if tabs are at the top, then create the body last..
38671     if(this.tabPosition != "bottom"){
38672         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38673          * @type Roo.Element
38674          */
38675         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38676         this.el.addClass("roo-tabs-top");
38677     }
38678     this.items = [];
38679
38680     this.bodyEl.setStyle("position", "relative");
38681
38682     this.active = null;
38683     this.activateDelegate = this.activate.createDelegate(this);
38684
38685     this.addEvents({
38686         /**
38687          * @event tabchange
38688          * Fires when the active tab changes
38689          * @param {Roo.TabPanel} this
38690          * @param {Roo.TabPanelItem} activePanel The new active tab
38691          */
38692         "tabchange": true,
38693         /**
38694          * @event beforetabchange
38695          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38696          * @param {Roo.TabPanel} this
38697          * @param {Object} e Set cancel to true on this object to cancel the tab change
38698          * @param {Roo.TabPanelItem} tab The tab being changed to
38699          */
38700         "beforetabchange" : true
38701     });
38702
38703     Roo.EventManager.onWindowResize(this.onResize, this);
38704     this.cpad = this.el.getPadding("lr");
38705     this.hiddenCount = 0;
38706
38707
38708     // toolbar on the tabbar support...
38709     if (this.toolbar) {
38710         alert("no toolbar support yet");
38711         this.toolbar  = false;
38712         /*
38713         var tcfg = this.toolbar;
38714         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38715         this.toolbar = new Roo.Toolbar(tcfg);
38716         if (Roo.isSafari) {
38717             var tbl = tcfg.container.child('table', true);
38718             tbl.setAttribute('width', '100%');
38719         }
38720         */
38721         
38722     }
38723    
38724
38725
38726     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38727 };
38728
38729 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38730     /*
38731      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38732      */
38733     tabPosition : "top",
38734     /*
38735      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38736      */
38737     currentTabWidth : 0,
38738     /*
38739      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38740      */
38741     minTabWidth : 40,
38742     /*
38743      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38744      */
38745     maxTabWidth : 250,
38746     /*
38747      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38748      */
38749     preferredTabWidth : 175,
38750     /*
38751      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38752      */
38753     resizeTabs : false,
38754     /*
38755      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38756      */
38757     monitorResize : true,
38758     /*
38759      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38760      */
38761     toolbar : false,  // set by caller..
38762     
38763     region : false, /// set by caller
38764     
38765     disableTooltips : true, // not used yet...
38766
38767     /**
38768      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38769      * @param {String} id The id of the div to use <b>or create</b>
38770      * @param {String} text The text for the tab
38771      * @param {String} content (optional) Content to put in the TabPanelItem body
38772      * @param {Boolean} closable (optional) True to create a close icon on the tab
38773      * @return {Roo.TabPanelItem} The created TabPanelItem
38774      */
38775     addTab : function(id, text, content, closable, tpl)
38776     {
38777         var item = new Roo.bootstrap.panel.TabItem({
38778             panel: this,
38779             id : id,
38780             text : text,
38781             closable : closable,
38782             tpl : tpl
38783         });
38784         this.addTabItem(item);
38785         if(content){
38786             item.setContent(content);
38787         }
38788         return item;
38789     },
38790
38791     /**
38792      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38793      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38794      * @return {Roo.TabPanelItem}
38795      */
38796     getTab : function(id){
38797         return this.items[id];
38798     },
38799
38800     /**
38801      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38802      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38803      */
38804     hideTab : function(id){
38805         var t = this.items[id];
38806         if(!t.isHidden()){
38807            t.setHidden(true);
38808            this.hiddenCount++;
38809            this.autoSizeTabs();
38810         }
38811     },
38812
38813     /**
38814      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38815      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38816      */
38817     unhideTab : function(id){
38818         var t = this.items[id];
38819         if(t.isHidden()){
38820            t.setHidden(false);
38821            this.hiddenCount--;
38822            this.autoSizeTabs();
38823         }
38824     },
38825
38826     /**
38827      * Adds an existing {@link Roo.TabPanelItem}.
38828      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38829      */
38830     addTabItem : function(item)
38831     {
38832         this.items[item.id] = item;
38833         this.items.push(item);
38834         this.autoSizeTabs();
38835       //  if(this.resizeTabs){
38836     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38837   //         this.autoSizeTabs();
38838 //        }else{
38839 //            item.autoSize();
38840        // }
38841     },
38842
38843     /**
38844      * Removes a {@link Roo.TabPanelItem}.
38845      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38846      */
38847     removeTab : function(id){
38848         var items = this.items;
38849         var tab = items[id];
38850         if(!tab) { return; }
38851         var index = items.indexOf(tab);
38852         if(this.active == tab && items.length > 1){
38853             var newTab = this.getNextAvailable(index);
38854             if(newTab) {
38855                 newTab.activate();
38856             }
38857         }
38858         this.stripEl.dom.removeChild(tab.pnode.dom);
38859         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38860             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38861         }
38862         items.splice(index, 1);
38863         delete this.items[tab.id];
38864         tab.fireEvent("close", tab);
38865         tab.purgeListeners();
38866         this.autoSizeTabs();
38867     },
38868
38869     getNextAvailable : function(start){
38870         var items = this.items;
38871         var index = start;
38872         // look for a next tab that will slide over to
38873         // replace the one being removed
38874         while(index < items.length){
38875             var item = items[++index];
38876             if(item && !item.isHidden()){
38877                 return item;
38878             }
38879         }
38880         // if one isn't found select the previous tab (on the left)
38881         index = start;
38882         while(index >= 0){
38883             var item = items[--index];
38884             if(item && !item.isHidden()){
38885                 return item;
38886             }
38887         }
38888         return null;
38889     },
38890
38891     /**
38892      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38893      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38894      */
38895     disableTab : function(id){
38896         var tab = this.items[id];
38897         if(tab && this.active != tab){
38898             tab.disable();
38899         }
38900     },
38901
38902     /**
38903      * Enables a {@link Roo.TabPanelItem} that is disabled.
38904      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38905      */
38906     enableTab : function(id){
38907         var tab = this.items[id];
38908         tab.enable();
38909     },
38910
38911     /**
38912      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38913      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38914      * @return {Roo.TabPanelItem} The TabPanelItem.
38915      */
38916     activate : function(id)
38917     {
38918         //Roo.log('activite:'  + id);
38919         
38920         var tab = this.items[id];
38921         if(!tab){
38922             return null;
38923         }
38924         if(tab == this.active || tab.disabled){
38925             return tab;
38926         }
38927         var e = {};
38928         this.fireEvent("beforetabchange", this, e, tab);
38929         if(e.cancel !== true && !tab.disabled){
38930             if(this.active){
38931                 this.active.hide();
38932             }
38933             this.active = this.items[id];
38934             this.active.show();
38935             this.fireEvent("tabchange", this, this.active);
38936         }
38937         return tab;
38938     },
38939
38940     /**
38941      * Gets the active {@link Roo.TabPanelItem}.
38942      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38943      */
38944     getActiveTab : function(){
38945         return this.active;
38946     },
38947
38948     /**
38949      * Updates the tab body element to fit the height of the container element
38950      * for overflow scrolling
38951      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38952      */
38953     syncHeight : function(targetHeight){
38954         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38955         var bm = this.bodyEl.getMargins();
38956         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38957         this.bodyEl.setHeight(newHeight);
38958         return newHeight;
38959     },
38960
38961     onResize : function(){
38962         if(this.monitorResize){
38963             this.autoSizeTabs();
38964         }
38965     },
38966
38967     /**
38968      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38969      */
38970     beginUpdate : function(){
38971         this.updating = true;
38972     },
38973
38974     /**
38975      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38976      */
38977     endUpdate : function(){
38978         this.updating = false;
38979         this.autoSizeTabs();
38980     },
38981
38982     /**
38983      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38984      */
38985     autoSizeTabs : function()
38986     {
38987         var count = this.items.length;
38988         var vcount = count - this.hiddenCount;
38989         
38990         if (vcount < 2) {
38991             this.stripEl.hide();
38992         } else {
38993             this.stripEl.show();
38994         }
38995         
38996         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38997             return;
38998         }
38999         
39000         
39001         var w = Math.max(this.el.getWidth() - this.cpad, 10);
39002         var availWidth = Math.floor(w / vcount);
39003         var b = this.stripBody;
39004         if(b.getWidth() > w){
39005             var tabs = this.items;
39006             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
39007             if(availWidth < this.minTabWidth){
39008                 /*if(!this.sleft){    // incomplete scrolling code
39009                     this.createScrollButtons();
39010                 }
39011                 this.showScroll();
39012                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
39013             }
39014         }else{
39015             if(this.currentTabWidth < this.preferredTabWidth){
39016                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
39017             }
39018         }
39019     },
39020
39021     /**
39022      * Returns the number of tabs in this TabPanel.
39023      * @return {Number}
39024      */
39025      getCount : function(){
39026          return this.items.length;
39027      },
39028
39029     /**
39030      * Resizes all the tabs to the passed width
39031      * @param {Number} The new width
39032      */
39033     setTabWidth : function(width){
39034         this.currentTabWidth = width;
39035         for(var i = 0, len = this.items.length; i < len; i++) {
39036                 if(!this.items[i].isHidden()) {
39037                 this.items[i].setWidth(width);
39038             }
39039         }
39040     },
39041
39042     /**
39043      * Destroys this TabPanel
39044      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
39045      */
39046     destroy : function(removeEl){
39047         Roo.EventManager.removeResizeListener(this.onResize, this);
39048         for(var i = 0, len = this.items.length; i < len; i++){
39049             this.items[i].purgeListeners();
39050         }
39051         if(removeEl === true){
39052             this.el.update("");
39053             this.el.remove();
39054         }
39055     },
39056     
39057     createStrip : function(container)
39058     {
39059         var strip = document.createElement("nav");
39060         strip.className = Roo.bootstrap.version == 4 ?
39061             "navbar-light bg-light" : 
39062             "navbar navbar-default"; //"x-tabs-wrap";
39063         container.appendChild(strip);
39064         return strip;
39065     },
39066     
39067     createStripList : function(strip)
39068     {
39069         // div wrapper for retard IE
39070         // returns the "tr" element.
39071         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
39072         //'<div class="x-tabs-strip-wrap">'+
39073           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
39074           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
39075         return strip.firstChild; //.firstChild.firstChild.firstChild;
39076     },
39077     createBody : function(container)
39078     {
39079         var body = document.createElement("div");
39080         Roo.id(body, "tab-body");
39081         //Roo.fly(body).addClass("x-tabs-body");
39082         Roo.fly(body).addClass("tab-content");
39083         container.appendChild(body);
39084         return body;
39085     },
39086     createItemBody :function(bodyEl, id){
39087         var body = Roo.getDom(id);
39088         if(!body){
39089             body = document.createElement("div");
39090             body.id = id;
39091         }
39092         //Roo.fly(body).addClass("x-tabs-item-body");
39093         Roo.fly(body).addClass("tab-pane");
39094          bodyEl.insertBefore(body, bodyEl.firstChild);
39095         return body;
39096     },
39097     /** @private */
39098     createStripElements :  function(stripEl, text, closable, tpl)
39099     {
39100         var td = document.createElement("li"); // was td..
39101         td.className = 'nav-item';
39102         
39103         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
39104         
39105         
39106         stripEl.appendChild(td);
39107         /*if(closable){
39108             td.className = "x-tabs-closable";
39109             if(!this.closeTpl){
39110                 this.closeTpl = new Roo.Template(
39111                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39112                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
39113                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
39114                 );
39115             }
39116             var el = this.closeTpl.overwrite(td, {"text": text});
39117             var close = el.getElementsByTagName("div")[0];
39118             var inner = el.getElementsByTagName("em")[0];
39119             return {"el": el, "close": close, "inner": inner};
39120         } else {
39121         */
39122         // not sure what this is..
39123 //            if(!this.tabTpl){
39124                 //this.tabTpl = new Roo.Template(
39125                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
39126                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
39127                 //);
39128 //                this.tabTpl = new Roo.Template(
39129 //                   '<a href="#">' +
39130 //                   '<span unselectable="on"' +
39131 //                            (this.disableTooltips ? '' : ' title="{text}"') +
39132 //                            ' >{text}</span></a>'
39133 //                );
39134 //                
39135 //            }
39136
39137
39138             var template = tpl || this.tabTpl || false;
39139             
39140             if(!template){
39141                 template =  new Roo.Template(
39142                         Roo.bootstrap.version == 4 ? 
39143                             (
39144                                 '<a class="nav-link" href="#" unselectable="on"' +
39145                                      (this.disableTooltips ? '' : ' title="{text}"') +
39146                                      ' >{text}</a>'
39147                             ) : (
39148                                 '<a class="nav-link" href="#">' +
39149                                 '<span unselectable="on"' +
39150                                          (this.disableTooltips ? '' : ' title="{text}"') +
39151                                     ' >{text}</span></a>'
39152                             )
39153                 );
39154             }
39155             
39156             switch (typeof(template)) {
39157                 case 'object' :
39158                     break;
39159                 case 'string' :
39160                     template = new Roo.Template(template);
39161                     break;
39162                 default :
39163                     break;
39164             }
39165             
39166             var el = template.overwrite(td, {"text": text});
39167             
39168             var inner = el.getElementsByTagName("span")[0];
39169             
39170             return {"el": el, "inner": inner};
39171             
39172     }
39173         
39174     
39175 });
39176
39177 /**
39178  * @class Roo.TabPanelItem
39179  * @extends Roo.util.Observable
39180  * Represents an individual item (tab plus body) in a TabPanel.
39181  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
39182  * @param {String} id The id of this TabPanelItem
39183  * @param {String} text The text for the tab of this TabPanelItem
39184  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
39185  */
39186 Roo.bootstrap.panel.TabItem = function(config){
39187     /**
39188      * The {@link Roo.TabPanel} this TabPanelItem belongs to
39189      * @type Roo.TabPanel
39190      */
39191     this.tabPanel = config.panel;
39192     /**
39193      * The id for this TabPanelItem
39194      * @type String
39195      */
39196     this.id = config.id;
39197     /** @private */
39198     this.disabled = false;
39199     /** @private */
39200     this.text = config.text;
39201     /** @private */
39202     this.loaded = false;
39203     this.closable = config.closable;
39204
39205     /**
39206      * The body element for this TabPanelItem.
39207      * @type Roo.Element
39208      */
39209     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
39210     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
39211     this.bodyEl.setStyle("display", "block");
39212     this.bodyEl.setStyle("zoom", "1");
39213     //this.hideAction();
39214
39215     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
39216     /** @private */
39217     this.el = Roo.get(els.el);
39218     this.inner = Roo.get(els.inner, true);
39219      this.textEl = Roo.bootstrap.version == 4 ?
39220         this.el : Roo.get(this.el.dom.firstChild, true);
39221
39222     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
39223     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
39224
39225     
39226 //    this.el.on("mousedown", this.onTabMouseDown, this);
39227     this.el.on("click", this.onTabClick, this);
39228     /** @private */
39229     if(config.closable){
39230         var c = Roo.get(els.close, true);
39231         c.dom.title = this.closeText;
39232         c.addClassOnOver("close-over");
39233         c.on("click", this.closeClick, this);
39234      }
39235
39236     this.addEvents({
39237          /**
39238          * @event activate
39239          * Fires when this tab becomes the active tab.
39240          * @param {Roo.TabPanel} tabPanel The parent TabPanel
39241          * @param {Roo.TabPanelItem} this
39242          */
39243         "activate": true,
39244         /**
39245          * @event beforeclose
39246          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
39247          * @param {Roo.TabPanelItem} this
39248          * @param {Object} e Set cancel to true on this object to cancel the close.
39249          */
39250         "beforeclose": true,
39251         /**
39252          * @event close
39253          * Fires when this tab is closed.
39254          * @param {Roo.TabPanelItem} this
39255          */
39256          "close": true,
39257         /**
39258          * @event deactivate
39259          * Fires when this tab is no longer the active tab.
39260          * @param {Roo.TabPanel} tabPanel The parent TabPanel
39261          * @param {Roo.TabPanelItem} this
39262          */
39263          "deactivate" : true
39264     });
39265     this.hidden = false;
39266
39267     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
39268 };
39269
39270 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
39271            {
39272     purgeListeners : function(){
39273        Roo.util.Observable.prototype.purgeListeners.call(this);
39274        this.el.removeAllListeners();
39275     },
39276     /**
39277      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
39278      */
39279     show : function(){
39280         this.status_node.addClass("active");
39281         this.showAction();
39282         if(Roo.isOpera){
39283             this.tabPanel.stripWrap.repaint();
39284         }
39285         this.fireEvent("activate", this.tabPanel, this);
39286     },
39287
39288     /**
39289      * Returns true if this tab is the active tab.
39290      * @return {Boolean}
39291      */
39292     isActive : function(){
39293         return this.tabPanel.getActiveTab() == this;
39294     },
39295
39296     /**
39297      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
39298      */
39299     hide : function(){
39300         this.status_node.removeClass("active");
39301         this.hideAction();
39302         this.fireEvent("deactivate", this.tabPanel, this);
39303     },
39304
39305     hideAction : function(){
39306         this.bodyEl.hide();
39307         this.bodyEl.setStyle("position", "absolute");
39308         this.bodyEl.setLeft("-20000px");
39309         this.bodyEl.setTop("-20000px");
39310     },
39311
39312     showAction : function(){
39313         this.bodyEl.setStyle("position", "relative");
39314         this.bodyEl.setTop("");
39315         this.bodyEl.setLeft("");
39316         this.bodyEl.show();
39317     },
39318
39319     /**
39320      * Set the tooltip for the tab.
39321      * @param {String} tooltip The tab's tooltip
39322      */
39323     setTooltip : function(text){
39324         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
39325             this.textEl.dom.qtip = text;
39326             this.textEl.dom.removeAttribute('title');
39327         }else{
39328             this.textEl.dom.title = text;
39329         }
39330     },
39331
39332     onTabClick : function(e){
39333         e.preventDefault();
39334         this.tabPanel.activate(this.id);
39335     },
39336
39337     onTabMouseDown : function(e){
39338         e.preventDefault();
39339         this.tabPanel.activate(this.id);
39340     },
39341 /*
39342     getWidth : function(){
39343         return this.inner.getWidth();
39344     },
39345
39346     setWidth : function(width){
39347         var iwidth = width - this.linode.getPadding("lr");
39348         this.inner.setWidth(iwidth);
39349         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
39350         this.linode.setWidth(width);
39351     },
39352 */
39353     /**
39354      * Show or hide the tab
39355      * @param {Boolean} hidden True to hide or false to show.
39356      */
39357     setHidden : function(hidden){
39358         this.hidden = hidden;
39359         this.linode.setStyle("display", hidden ? "none" : "");
39360     },
39361
39362     /**
39363      * Returns true if this tab is "hidden"
39364      * @return {Boolean}
39365      */
39366     isHidden : function(){
39367         return this.hidden;
39368     },
39369
39370     /**
39371      * Returns the text for this tab
39372      * @return {String}
39373      */
39374     getText : function(){
39375         return this.text;
39376     },
39377     /*
39378     autoSize : function(){
39379         //this.el.beginMeasure();
39380         this.textEl.setWidth(1);
39381         /*
39382          *  #2804 [new] Tabs in Roojs
39383          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
39384          */
39385         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
39386         //this.el.endMeasure();
39387     //},
39388
39389     /**
39390      * Sets the text for the tab (Note: this also sets the tooltip text)
39391      * @param {String} text The tab's text and tooltip
39392      */
39393     setText : function(text){
39394         this.text = text;
39395         this.textEl.update(text);
39396         this.setTooltip(text);
39397         //if(!this.tabPanel.resizeTabs){
39398         //    this.autoSize();
39399         //}
39400     },
39401     /**
39402      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39403      */
39404     activate : function(){
39405         this.tabPanel.activate(this.id);
39406     },
39407
39408     /**
39409      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39410      */
39411     disable : function(){
39412         if(this.tabPanel.active != this){
39413             this.disabled = true;
39414             this.status_node.addClass("disabled");
39415         }
39416     },
39417
39418     /**
39419      * Enables this TabPanelItem if it was previously disabled.
39420      */
39421     enable : function(){
39422         this.disabled = false;
39423         this.status_node.removeClass("disabled");
39424     },
39425
39426     /**
39427      * Sets the content for this TabPanelItem.
39428      * @param {String} content The content
39429      * @param {Boolean} loadScripts true to look for and load scripts
39430      */
39431     setContent : function(content, loadScripts){
39432         this.bodyEl.update(content, loadScripts);
39433     },
39434
39435     /**
39436      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
39437      * @return {Roo.UpdateManager} The UpdateManager
39438      */
39439     getUpdateManager : function(){
39440         return this.bodyEl.getUpdateManager();
39441     },
39442
39443     /**
39444      * Set a URL to be used to load the content for this TabPanelItem.
39445      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39446      * @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)
39447      * @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)
39448      * @return {Roo.UpdateManager} The UpdateManager
39449      */
39450     setUrl : function(url, params, loadOnce){
39451         if(this.refreshDelegate){
39452             this.un('activate', this.refreshDelegate);
39453         }
39454         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39455         this.on("activate", this.refreshDelegate);
39456         return this.bodyEl.getUpdateManager();
39457     },
39458
39459     /** @private */
39460     _handleRefresh : function(url, params, loadOnce){
39461         if(!loadOnce || !this.loaded){
39462             var updater = this.bodyEl.getUpdateManager();
39463             updater.update(url, params, this._setLoaded.createDelegate(this));
39464         }
39465     },
39466
39467     /**
39468      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
39469      *   Will fail silently if the setUrl method has not been called.
39470      *   This does not activate the panel, just updates its content.
39471      */
39472     refresh : function(){
39473         if(this.refreshDelegate){
39474            this.loaded = false;
39475            this.refreshDelegate();
39476         }
39477     },
39478
39479     /** @private */
39480     _setLoaded : function(){
39481         this.loaded = true;
39482     },
39483
39484     /** @private */
39485     closeClick : function(e){
39486         var o = {};
39487         e.stopEvent();
39488         this.fireEvent("beforeclose", this, o);
39489         if(o.cancel !== true){
39490             this.tabPanel.removeTab(this.id);
39491         }
39492     },
39493     /**
39494      * The text displayed in the tooltip for the close icon.
39495      * @type String
39496      */
39497     closeText : "Close this tab"
39498 });
39499 /**
39500 *    This script refer to:
39501 *    Title: International Telephone Input
39502 *    Author: Jack O'Connor
39503 *    Code version:  v12.1.12
39504 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39505 **/
39506
39507 Roo.bootstrap.PhoneInputData = function() {
39508     var d = [
39509       [
39510         "Afghanistan (‫افغانستان‬‎)",
39511         "af",
39512         "93"
39513       ],
39514       [
39515         "Albania (Shqipëri)",
39516         "al",
39517         "355"
39518       ],
39519       [
39520         "Algeria (‫الجزائر‬‎)",
39521         "dz",
39522         "213"
39523       ],
39524       [
39525         "American Samoa",
39526         "as",
39527         "1684"
39528       ],
39529       [
39530         "Andorra",
39531         "ad",
39532         "376"
39533       ],
39534       [
39535         "Angola",
39536         "ao",
39537         "244"
39538       ],
39539       [
39540         "Anguilla",
39541         "ai",
39542         "1264"
39543       ],
39544       [
39545         "Antigua and Barbuda",
39546         "ag",
39547         "1268"
39548       ],
39549       [
39550         "Argentina",
39551         "ar",
39552         "54"
39553       ],
39554       [
39555         "Armenia (Հայաստան)",
39556         "am",
39557         "374"
39558       ],
39559       [
39560         "Aruba",
39561         "aw",
39562         "297"
39563       ],
39564       [
39565         "Australia",
39566         "au",
39567         "61",
39568         0
39569       ],
39570       [
39571         "Austria (Österreich)",
39572         "at",
39573         "43"
39574       ],
39575       [
39576         "Azerbaijan (Azərbaycan)",
39577         "az",
39578         "994"
39579       ],
39580       [
39581         "Bahamas",
39582         "bs",
39583         "1242"
39584       ],
39585       [
39586         "Bahrain (‫البحرين‬‎)",
39587         "bh",
39588         "973"
39589       ],
39590       [
39591         "Bangladesh (বাংলাদেশ)",
39592         "bd",
39593         "880"
39594       ],
39595       [
39596         "Barbados",
39597         "bb",
39598         "1246"
39599       ],
39600       [
39601         "Belarus (Беларусь)",
39602         "by",
39603         "375"
39604       ],
39605       [
39606         "Belgium (België)",
39607         "be",
39608         "32"
39609       ],
39610       [
39611         "Belize",
39612         "bz",
39613         "501"
39614       ],
39615       [
39616         "Benin (Bénin)",
39617         "bj",
39618         "229"
39619       ],
39620       [
39621         "Bermuda",
39622         "bm",
39623         "1441"
39624       ],
39625       [
39626         "Bhutan (འབྲུག)",
39627         "bt",
39628         "975"
39629       ],
39630       [
39631         "Bolivia",
39632         "bo",
39633         "591"
39634       ],
39635       [
39636         "Bosnia and Herzegovina (Босна и Херцеговина)",
39637         "ba",
39638         "387"
39639       ],
39640       [
39641         "Botswana",
39642         "bw",
39643         "267"
39644       ],
39645       [
39646         "Brazil (Brasil)",
39647         "br",
39648         "55"
39649       ],
39650       [
39651         "British Indian Ocean Territory",
39652         "io",
39653         "246"
39654       ],
39655       [
39656         "British Virgin Islands",
39657         "vg",
39658         "1284"
39659       ],
39660       [
39661         "Brunei",
39662         "bn",
39663         "673"
39664       ],
39665       [
39666         "Bulgaria (България)",
39667         "bg",
39668         "359"
39669       ],
39670       [
39671         "Burkina Faso",
39672         "bf",
39673         "226"
39674       ],
39675       [
39676         "Burundi (Uburundi)",
39677         "bi",
39678         "257"
39679       ],
39680       [
39681         "Cambodia (កម្ពុជា)",
39682         "kh",
39683         "855"
39684       ],
39685       [
39686         "Cameroon (Cameroun)",
39687         "cm",
39688         "237"
39689       ],
39690       [
39691         "Canada",
39692         "ca",
39693         "1",
39694         1,
39695         ["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"]
39696       ],
39697       [
39698         "Cape Verde (Kabu Verdi)",
39699         "cv",
39700         "238"
39701       ],
39702       [
39703         "Caribbean Netherlands",
39704         "bq",
39705         "599",
39706         1
39707       ],
39708       [
39709         "Cayman Islands",
39710         "ky",
39711         "1345"
39712       ],
39713       [
39714         "Central African Republic (République centrafricaine)",
39715         "cf",
39716         "236"
39717       ],
39718       [
39719         "Chad (Tchad)",
39720         "td",
39721         "235"
39722       ],
39723       [
39724         "Chile",
39725         "cl",
39726         "56"
39727       ],
39728       [
39729         "China (中国)",
39730         "cn",
39731         "86"
39732       ],
39733       [
39734         "Christmas Island",
39735         "cx",
39736         "61",
39737         2
39738       ],
39739       [
39740         "Cocos (Keeling) Islands",
39741         "cc",
39742         "61",
39743         1
39744       ],
39745       [
39746         "Colombia",
39747         "co",
39748         "57"
39749       ],
39750       [
39751         "Comoros (‫جزر القمر‬‎)",
39752         "km",
39753         "269"
39754       ],
39755       [
39756         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39757         "cd",
39758         "243"
39759       ],
39760       [
39761         "Congo (Republic) (Congo-Brazzaville)",
39762         "cg",
39763         "242"
39764       ],
39765       [
39766         "Cook Islands",
39767         "ck",
39768         "682"
39769       ],
39770       [
39771         "Costa Rica",
39772         "cr",
39773         "506"
39774       ],
39775       [
39776         "Côte d’Ivoire",
39777         "ci",
39778         "225"
39779       ],
39780       [
39781         "Croatia (Hrvatska)",
39782         "hr",
39783         "385"
39784       ],
39785       [
39786         "Cuba",
39787         "cu",
39788         "53"
39789       ],
39790       [
39791         "Curaçao",
39792         "cw",
39793         "599",
39794         0
39795       ],
39796       [
39797         "Cyprus (Κύπρος)",
39798         "cy",
39799         "357"
39800       ],
39801       [
39802         "Czech Republic (Česká republika)",
39803         "cz",
39804         "420"
39805       ],
39806       [
39807         "Denmark (Danmark)",
39808         "dk",
39809         "45"
39810       ],
39811       [
39812         "Djibouti",
39813         "dj",
39814         "253"
39815       ],
39816       [
39817         "Dominica",
39818         "dm",
39819         "1767"
39820       ],
39821       [
39822         "Dominican Republic (República Dominicana)",
39823         "do",
39824         "1",
39825         2,
39826         ["809", "829", "849"]
39827       ],
39828       [
39829         "Ecuador",
39830         "ec",
39831         "593"
39832       ],
39833       [
39834         "Egypt (‫مصر‬‎)",
39835         "eg",
39836         "20"
39837       ],
39838       [
39839         "El Salvador",
39840         "sv",
39841         "503"
39842       ],
39843       [
39844         "Equatorial Guinea (Guinea Ecuatorial)",
39845         "gq",
39846         "240"
39847       ],
39848       [
39849         "Eritrea",
39850         "er",
39851         "291"
39852       ],
39853       [
39854         "Estonia (Eesti)",
39855         "ee",
39856         "372"
39857       ],
39858       [
39859         "Ethiopia",
39860         "et",
39861         "251"
39862       ],
39863       [
39864         "Falkland Islands (Islas Malvinas)",
39865         "fk",
39866         "500"
39867       ],
39868       [
39869         "Faroe Islands (Føroyar)",
39870         "fo",
39871         "298"
39872       ],
39873       [
39874         "Fiji",
39875         "fj",
39876         "679"
39877       ],
39878       [
39879         "Finland (Suomi)",
39880         "fi",
39881         "358",
39882         0
39883       ],
39884       [
39885         "France",
39886         "fr",
39887         "33"
39888       ],
39889       [
39890         "French Guiana (Guyane française)",
39891         "gf",
39892         "594"
39893       ],
39894       [
39895         "French Polynesia (Polynésie française)",
39896         "pf",
39897         "689"
39898       ],
39899       [
39900         "Gabon",
39901         "ga",
39902         "241"
39903       ],
39904       [
39905         "Gambia",
39906         "gm",
39907         "220"
39908       ],
39909       [
39910         "Georgia (საქართველო)",
39911         "ge",
39912         "995"
39913       ],
39914       [
39915         "Germany (Deutschland)",
39916         "de",
39917         "49"
39918       ],
39919       [
39920         "Ghana (Gaana)",
39921         "gh",
39922         "233"
39923       ],
39924       [
39925         "Gibraltar",
39926         "gi",
39927         "350"
39928       ],
39929       [
39930         "Greece (Ελλάδα)",
39931         "gr",
39932         "30"
39933       ],
39934       [
39935         "Greenland (Kalaallit Nunaat)",
39936         "gl",
39937         "299"
39938       ],
39939       [
39940         "Grenada",
39941         "gd",
39942         "1473"
39943       ],
39944       [
39945         "Guadeloupe",
39946         "gp",
39947         "590",
39948         0
39949       ],
39950       [
39951         "Guam",
39952         "gu",
39953         "1671"
39954       ],
39955       [
39956         "Guatemala",
39957         "gt",
39958         "502"
39959       ],
39960       [
39961         "Guernsey",
39962         "gg",
39963         "44",
39964         1
39965       ],
39966       [
39967         "Guinea (Guinée)",
39968         "gn",
39969         "224"
39970       ],
39971       [
39972         "Guinea-Bissau (Guiné Bissau)",
39973         "gw",
39974         "245"
39975       ],
39976       [
39977         "Guyana",
39978         "gy",
39979         "592"
39980       ],
39981       [
39982         "Haiti",
39983         "ht",
39984         "509"
39985       ],
39986       [
39987         "Honduras",
39988         "hn",
39989         "504"
39990       ],
39991       [
39992         "Hong Kong (香港)",
39993         "hk",
39994         "852"
39995       ],
39996       [
39997         "Hungary (Magyarország)",
39998         "hu",
39999         "36"
40000       ],
40001       [
40002         "Iceland (Ísland)",
40003         "is",
40004         "354"
40005       ],
40006       [
40007         "India (भारत)",
40008         "in",
40009         "91"
40010       ],
40011       [
40012         "Indonesia",
40013         "id",
40014         "62"
40015       ],
40016       [
40017         "Iran (‫ایران‬‎)",
40018         "ir",
40019         "98"
40020       ],
40021       [
40022         "Iraq (‫العراق‬‎)",
40023         "iq",
40024         "964"
40025       ],
40026       [
40027         "Ireland",
40028         "ie",
40029         "353"
40030       ],
40031       [
40032         "Isle of Man",
40033         "im",
40034         "44",
40035         2
40036       ],
40037       [
40038         "Israel (‫ישראל‬‎)",
40039         "il",
40040         "972"
40041       ],
40042       [
40043         "Italy (Italia)",
40044         "it",
40045         "39",
40046         0
40047       ],
40048       [
40049         "Jamaica",
40050         "jm",
40051         "1876"
40052       ],
40053       [
40054         "Japan (日本)",
40055         "jp",
40056         "81"
40057       ],
40058       [
40059         "Jersey",
40060         "je",
40061         "44",
40062         3
40063       ],
40064       [
40065         "Jordan (‫الأردن‬‎)",
40066         "jo",
40067         "962"
40068       ],
40069       [
40070         "Kazakhstan (Казахстан)",
40071         "kz",
40072         "7",
40073         1
40074       ],
40075       [
40076         "Kenya",
40077         "ke",
40078         "254"
40079       ],
40080       [
40081         "Kiribati",
40082         "ki",
40083         "686"
40084       ],
40085       [
40086         "Kosovo",
40087         "xk",
40088         "383"
40089       ],
40090       [
40091         "Kuwait (‫الكويت‬‎)",
40092         "kw",
40093         "965"
40094       ],
40095       [
40096         "Kyrgyzstan (Кыргызстан)",
40097         "kg",
40098         "996"
40099       ],
40100       [
40101         "Laos (ລາວ)",
40102         "la",
40103         "856"
40104       ],
40105       [
40106         "Latvia (Latvija)",
40107         "lv",
40108         "371"
40109       ],
40110       [
40111         "Lebanon (‫لبنان‬‎)",
40112         "lb",
40113         "961"
40114       ],
40115       [
40116         "Lesotho",
40117         "ls",
40118         "266"
40119       ],
40120       [
40121         "Liberia",
40122         "lr",
40123         "231"
40124       ],
40125       [
40126         "Libya (‫ليبيا‬‎)",
40127         "ly",
40128         "218"
40129       ],
40130       [
40131         "Liechtenstein",
40132         "li",
40133         "423"
40134       ],
40135       [
40136         "Lithuania (Lietuva)",
40137         "lt",
40138         "370"
40139       ],
40140       [
40141         "Luxembourg",
40142         "lu",
40143         "352"
40144       ],
40145       [
40146         "Macau (澳門)",
40147         "mo",
40148         "853"
40149       ],
40150       [
40151         "Macedonia (FYROM) (Македонија)",
40152         "mk",
40153         "389"
40154       ],
40155       [
40156         "Madagascar (Madagasikara)",
40157         "mg",
40158         "261"
40159       ],
40160       [
40161         "Malawi",
40162         "mw",
40163         "265"
40164       ],
40165       [
40166         "Malaysia",
40167         "my",
40168         "60"
40169       ],
40170       [
40171         "Maldives",
40172         "mv",
40173         "960"
40174       ],
40175       [
40176         "Mali",
40177         "ml",
40178         "223"
40179       ],
40180       [
40181         "Malta",
40182         "mt",
40183         "356"
40184       ],
40185       [
40186         "Marshall Islands",
40187         "mh",
40188         "692"
40189       ],
40190       [
40191         "Martinique",
40192         "mq",
40193         "596"
40194       ],
40195       [
40196         "Mauritania (‫موريتانيا‬‎)",
40197         "mr",
40198         "222"
40199       ],
40200       [
40201         "Mauritius (Moris)",
40202         "mu",
40203         "230"
40204       ],
40205       [
40206         "Mayotte",
40207         "yt",
40208         "262",
40209         1
40210       ],
40211       [
40212         "Mexico (México)",
40213         "mx",
40214         "52"
40215       ],
40216       [
40217         "Micronesia",
40218         "fm",
40219         "691"
40220       ],
40221       [
40222         "Moldova (Republica Moldova)",
40223         "md",
40224         "373"
40225       ],
40226       [
40227         "Monaco",
40228         "mc",
40229         "377"
40230       ],
40231       [
40232         "Mongolia (Монгол)",
40233         "mn",
40234         "976"
40235       ],
40236       [
40237         "Montenegro (Crna Gora)",
40238         "me",
40239         "382"
40240       ],
40241       [
40242         "Montserrat",
40243         "ms",
40244         "1664"
40245       ],
40246       [
40247         "Morocco (‫المغرب‬‎)",
40248         "ma",
40249         "212",
40250         0
40251       ],
40252       [
40253         "Mozambique (Moçambique)",
40254         "mz",
40255         "258"
40256       ],
40257       [
40258         "Myanmar (Burma) (မြန်မာ)",
40259         "mm",
40260         "95"
40261       ],
40262       [
40263         "Namibia (Namibië)",
40264         "na",
40265         "264"
40266       ],
40267       [
40268         "Nauru",
40269         "nr",
40270         "674"
40271       ],
40272       [
40273         "Nepal (नेपाल)",
40274         "np",
40275         "977"
40276       ],
40277       [
40278         "Netherlands (Nederland)",
40279         "nl",
40280         "31"
40281       ],
40282       [
40283         "New Caledonia (Nouvelle-Calédonie)",
40284         "nc",
40285         "687"
40286       ],
40287       [
40288         "New Zealand",
40289         "nz",
40290         "64"
40291       ],
40292       [
40293         "Nicaragua",
40294         "ni",
40295         "505"
40296       ],
40297       [
40298         "Niger (Nijar)",
40299         "ne",
40300         "227"
40301       ],
40302       [
40303         "Nigeria",
40304         "ng",
40305         "234"
40306       ],
40307       [
40308         "Niue",
40309         "nu",
40310         "683"
40311       ],
40312       [
40313         "Norfolk Island",
40314         "nf",
40315         "672"
40316       ],
40317       [
40318         "North Korea (조선 민주주의 인민 공화국)",
40319         "kp",
40320         "850"
40321       ],
40322       [
40323         "Northern Mariana Islands",
40324         "mp",
40325         "1670"
40326       ],
40327       [
40328         "Norway (Norge)",
40329         "no",
40330         "47",
40331         0
40332       ],
40333       [
40334         "Oman (‫عُمان‬‎)",
40335         "om",
40336         "968"
40337       ],
40338       [
40339         "Pakistan (‫پاکستان‬‎)",
40340         "pk",
40341         "92"
40342       ],
40343       [
40344         "Palau",
40345         "pw",
40346         "680"
40347       ],
40348       [
40349         "Palestine (‫فلسطين‬‎)",
40350         "ps",
40351         "970"
40352       ],
40353       [
40354         "Panama (Panamá)",
40355         "pa",
40356         "507"
40357       ],
40358       [
40359         "Papua New Guinea",
40360         "pg",
40361         "675"
40362       ],
40363       [
40364         "Paraguay",
40365         "py",
40366         "595"
40367       ],
40368       [
40369         "Peru (Perú)",
40370         "pe",
40371         "51"
40372       ],
40373       [
40374         "Philippines",
40375         "ph",
40376         "63"
40377       ],
40378       [
40379         "Poland (Polska)",
40380         "pl",
40381         "48"
40382       ],
40383       [
40384         "Portugal",
40385         "pt",
40386         "351"
40387       ],
40388       [
40389         "Puerto Rico",
40390         "pr",
40391         "1",
40392         3,
40393         ["787", "939"]
40394       ],
40395       [
40396         "Qatar (‫قطر‬‎)",
40397         "qa",
40398         "974"
40399       ],
40400       [
40401         "Réunion (La Réunion)",
40402         "re",
40403         "262",
40404         0
40405       ],
40406       [
40407         "Romania (România)",
40408         "ro",
40409         "40"
40410       ],
40411       [
40412         "Russia (Россия)",
40413         "ru",
40414         "7",
40415         0
40416       ],
40417       [
40418         "Rwanda",
40419         "rw",
40420         "250"
40421       ],
40422       [
40423         "Saint Barthélemy",
40424         "bl",
40425         "590",
40426         1
40427       ],
40428       [
40429         "Saint Helena",
40430         "sh",
40431         "290"
40432       ],
40433       [
40434         "Saint Kitts and Nevis",
40435         "kn",
40436         "1869"
40437       ],
40438       [
40439         "Saint Lucia",
40440         "lc",
40441         "1758"
40442       ],
40443       [
40444         "Saint Martin (Saint-Martin (partie française))",
40445         "mf",
40446         "590",
40447         2
40448       ],
40449       [
40450         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
40451         "pm",
40452         "508"
40453       ],
40454       [
40455         "Saint Vincent and the Grenadines",
40456         "vc",
40457         "1784"
40458       ],
40459       [
40460         "Samoa",
40461         "ws",
40462         "685"
40463       ],
40464       [
40465         "San Marino",
40466         "sm",
40467         "378"
40468       ],
40469       [
40470         "São Tomé and Príncipe (São Tomé e Príncipe)",
40471         "st",
40472         "239"
40473       ],
40474       [
40475         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
40476         "sa",
40477         "966"
40478       ],
40479       [
40480         "Senegal (Sénégal)",
40481         "sn",
40482         "221"
40483       ],
40484       [
40485         "Serbia (Србија)",
40486         "rs",
40487         "381"
40488       ],
40489       [
40490         "Seychelles",
40491         "sc",
40492         "248"
40493       ],
40494       [
40495         "Sierra Leone",
40496         "sl",
40497         "232"
40498       ],
40499       [
40500         "Singapore",
40501         "sg",
40502         "65"
40503       ],
40504       [
40505         "Sint Maarten",
40506         "sx",
40507         "1721"
40508       ],
40509       [
40510         "Slovakia (Slovensko)",
40511         "sk",
40512         "421"
40513       ],
40514       [
40515         "Slovenia (Slovenija)",
40516         "si",
40517         "386"
40518       ],
40519       [
40520         "Solomon Islands",
40521         "sb",
40522         "677"
40523       ],
40524       [
40525         "Somalia (Soomaaliya)",
40526         "so",
40527         "252"
40528       ],
40529       [
40530         "South Africa",
40531         "za",
40532         "27"
40533       ],
40534       [
40535         "South Korea (대한민국)",
40536         "kr",
40537         "82"
40538       ],
40539       [
40540         "South Sudan (‫جنوب السودان‬‎)",
40541         "ss",
40542         "211"
40543       ],
40544       [
40545         "Spain (España)",
40546         "es",
40547         "34"
40548       ],
40549       [
40550         "Sri Lanka (ශ්‍රී ලංකාව)",
40551         "lk",
40552         "94"
40553       ],
40554       [
40555         "Sudan (‫السودان‬‎)",
40556         "sd",
40557         "249"
40558       ],
40559       [
40560         "Suriname",
40561         "sr",
40562         "597"
40563       ],
40564       [
40565         "Svalbard and Jan Mayen",
40566         "sj",
40567         "47",
40568         1
40569       ],
40570       [
40571         "Swaziland",
40572         "sz",
40573         "268"
40574       ],
40575       [
40576         "Sweden (Sverige)",
40577         "se",
40578         "46"
40579       ],
40580       [
40581         "Switzerland (Schweiz)",
40582         "ch",
40583         "41"
40584       ],
40585       [
40586         "Syria (‫سوريا‬‎)",
40587         "sy",
40588         "963"
40589       ],
40590       [
40591         "Taiwan (台灣)",
40592         "tw",
40593         "886"
40594       ],
40595       [
40596         "Tajikistan",
40597         "tj",
40598         "992"
40599       ],
40600       [
40601         "Tanzania",
40602         "tz",
40603         "255"
40604       ],
40605       [
40606         "Thailand (ไทย)",
40607         "th",
40608         "66"
40609       ],
40610       [
40611         "Timor-Leste",
40612         "tl",
40613         "670"
40614       ],
40615       [
40616         "Togo",
40617         "tg",
40618         "228"
40619       ],
40620       [
40621         "Tokelau",
40622         "tk",
40623         "690"
40624       ],
40625       [
40626         "Tonga",
40627         "to",
40628         "676"
40629       ],
40630       [
40631         "Trinidad and Tobago",
40632         "tt",
40633         "1868"
40634       ],
40635       [
40636         "Tunisia (‫تونس‬‎)",
40637         "tn",
40638         "216"
40639       ],
40640       [
40641         "Turkey (Türkiye)",
40642         "tr",
40643         "90"
40644       ],
40645       [
40646         "Turkmenistan",
40647         "tm",
40648         "993"
40649       ],
40650       [
40651         "Turks and Caicos Islands",
40652         "tc",
40653         "1649"
40654       ],
40655       [
40656         "Tuvalu",
40657         "tv",
40658         "688"
40659       ],
40660       [
40661         "U.S. Virgin Islands",
40662         "vi",
40663         "1340"
40664       ],
40665       [
40666         "Uganda",
40667         "ug",
40668         "256"
40669       ],
40670       [
40671         "Ukraine (Україна)",
40672         "ua",
40673         "380"
40674       ],
40675       [
40676         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40677         "ae",
40678         "971"
40679       ],
40680       [
40681         "United Kingdom",
40682         "gb",
40683         "44",
40684         0
40685       ],
40686       [
40687         "United States",
40688         "us",
40689         "1",
40690         0
40691       ],
40692       [
40693         "Uruguay",
40694         "uy",
40695         "598"
40696       ],
40697       [
40698         "Uzbekistan (Oʻzbekiston)",
40699         "uz",
40700         "998"
40701       ],
40702       [
40703         "Vanuatu",
40704         "vu",
40705         "678"
40706       ],
40707       [
40708         "Vatican City (Città del Vaticano)",
40709         "va",
40710         "39",
40711         1
40712       ],
40713       [
40714         "Venezuela",
40715         "ve",
40716         "58"
40717       ],
40718       [
40719         "Vietnam (Việt Nam)",
40720         "vn",
40721         "84"
40722       ],
40723       [
40724         "Wallis and Futuna (Wallis-et-Futuna)",
40725         "wf",
40726         "681"
40727       ],
40728       [
40729         "Western Sahara (‫الصحراء الغربية‬‎)",
40730         "eh",
40731         "212",
40732         1
40733       ],
40734       [
40735         "Yemen (‫اليمن‬‎)",
40736         "ye",
40737         "967"
40738       ],
40739       [
40740         "Zambia",
40741         "zm",
40742         "260"
40743       ],
40744       [
40745         "Zimbabwe",
40746         "zw",
40747         "263"
40748       ],
40749       [
40750         "Åland Islands",
40751         "ax",
40752         "358",
40753         1
40754       ]
40755   ];
40756   
40757   return d;
40758 }/**
40759 *    This script refer to:
40760 *    Title: International Telephone Input
40761 *    Author: Jack O'Connor
40762 *    Code version:  v12.1.12
40763 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40764 **/
40765
40766 /**
40767  * @class Roo.bootstrap.PhoneInput
40768  * @extends Roo.bootstrap.TriggerField
40769  * An input with International dial-code selection
40770  
40771  * @cfg {String} defaultDialCode default '+852'
40772  * @cfg {Array} preferedCountries default []
40773   
40774  * @constructor
40775  * Create a new PhoneInput.
40776  * @param {Object} config Configuration options
40777  */
40778
40779 Roo.bootstrap.PhoneInput = function(config) {
40780     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40781 };
40782
40783 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40784         
40785         listWidth: undefined,
40786         
40787         selectedClass: 'active',
40788         
40789         invalidClass : "has-warning",
40790         
40791         validClass: 'has-success',
40792         
40793         allowed: '0123456789',
40794         
40795         max_length: 15,
40796         
40797         /**
40798          * @cfg {String} defaultDialCode The default dial code when initializing the input
40799          */
40800         defaultDialCode: '+852',
40801         
40802         /**
40803          * @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
40804          */
40805         preferedCountries: false,
40806         
40807         getAutoCreate : function()
40808         {
40809             var data = Roo.bootstrap.PhoneInputData();
40810             var align = this.labelAlign || this.parentLabelAlign();
40811             var id = Roo.id();
40812             
40813             this.allCountries = [];
40814             this.dialCodeMapping = [];
40815             
40816             for (var i = 0; i < data.length; i++) {
40817               var c = data[i];
40818               this.allCountries[i] = {
40819                 name: c[0],
40820                 iso2: c[1],
40821                 dialCode: c[2],
40822                 priority: c[3] || 0,
40823                 areaCodes: c[4] || null
40824               };
40825               this.dialCodeMapping[c[2]] = {
40826                   name: c[0],
40827                   iso2: c[1],
40828                   priority: c[3] || 0,
40829                   areaCodes: c[4] || null
40830               };
40831             }
40832             
40833             var cfg = {
40834                 cls: 'form-group',
40835                 cn: []
40836             };
40837             
40838             var input =  {
40839                 tag: 'input',
40840                 id : id,
40841                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40842                 maxlength: this.max_length,
40843                 cls : 'form-control tel-input',
40844                 autocomplete: 'new-password'
40845             };
40846             
40847             var hiddenInput = {
40848                 tag: 'input',
40849                 type: 'hidden',
40850                 cls: 'hidden-tel-input'
40851             };
40852             
40853             if (this.name) {
40854                 hiddenInput.name = this.name;
40855             }
40856             
40857             if (this.disabled) {
40858                 input.disabled = true;
40859             }
40860             
40861             var flag_container = {
40862                 tag: 'div',
40863                 cls: 'flag-box',
40864                 cn: [
40865                     {
40866                         tag: 'div',
40867                         cls: 'flag'
40868                     },
40869                     {
40870                         tag: 'div',
40871                         cls: 'caret'
40872                     }
40873                 ]
40874             };
40875             
40876             var box = {
40877                 tag: 'div',
40878                 cls: this.hasFeedback ? 'has-feedback' : '',
40879                 cn: [
40880                     hiddenInput,
40881                     input,
40882                     {
40883                         tag: 'input',
40884                         cls: 'dial-code-holder',
40885                         disabled: true
40886                     }
40887                 ]
40888             };
40889             
40890             var container = {
40891                 cls: 'roo-select2-container input-group',
40892                 cn: [
40893                     flag_container,
40894                     box
40895                 ]
40896             };
40897             
40898             if (this.fieldLabel.length) {
40899                 var indicator = {
40900                     tag: 'i',
40901                     tooltip: 'This field is required'
40902                 };
40903                 
40904                 var label = {
40905                     tag: 'label',
40906                     'for':  id,
40907                     cls: 'control-label',
40908                     cn: []
40909                 };
40910                 
40911                 var label_text = {
40912                     tag: 'span',
40913                     html: this.fieldLabel
40914                 };
40915                 
40916                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40917                 label.cn = [
40918                     indicator,
40919                     label_text
40920                 ];
40921                 
40922                 if(this.indicatorpos == 'right') {
40923                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40924                     label.cn = [
40925                         label_text,
40926                         indicator
40927                     ];
40928                 }
40929                 
40930                 if(align == 'left') {
40931                     container = {
40932                         tag: 'div',
40933                         cn: [
40934                             container
40935                         ]
40936                     };
40937                     
40938                     if(this.labelWidth > 12){
40939                         label.style = "width: " + this.labelWidth + 'px';
40940                     }
40941                     if(this.labelWidth < 13 && this.labelmd == 0){
40942                         this.labelmd = this.labelWidth;
40943                     }
40944                     if(this.labellg > 0){
40945                         label.cls += ' col-lg-' + this.labellg;
40946                         input.cls += ' col-lg-' + (12 - this.labellg);
40947                     }
40948                     if(this.labelmd > 0){
40949                         label.cls += ' col-md-' + this.labelmd;
40950                         container.cls += ' col-md-' + (12 - this.labelmd);
40951                     }
40952                     if(this.labelsm > 0){
40953                         label.cls += ' col-sm-' + this.labelsm;
40954                         container.cls += ' col-sm-' + (12 - this.labelsm);
40955                     }
40956                     if(this.labelxs > 0){
40957                         label.cls += ' col-xs-' + this.labelxs;
40958                         container.cls += ' col-xs-' + (12 - this.labelxs);
40959                     }
40960                 }
40961             }
40962             
40963             cfg.cn = [
40964                 label,
40965                 container
40966             ];
40967             
40968             var settings = this;
40969             
40970             ['xs','sm','md','lg'].map(function(size){
40971                 if (settings[size]) {
40972                     cfg.cls += ' col-' + size + '-' + settings[size];
40973                 }
40974             });
40975             
40976             this.store = new Roo.data.Store({
40977                 proxy : new Roo.data.MemoryProxy({}),
40978                 reader : new Roo.data.JsonReader({
40979                     fields : [
40980                         {
40981                             'name' : 'name',
40982                             'type' : 'string'
40983                         },
40984                         {
40985                             'name' : 'iso2',
40986                             'type' : 'string'
40987                         },
40988                         {
40989                             'name' : 'dialCode',
40990                             'type' : 'string'
40991                         },
40992                         {
40993                             'name' : 'priority',
40994                             'type' : 'string'
40995                         },
40996                         {
40997                             'name' : 'areaCodes',
40998                             'type' : 'string'
40999                         }
41000                     ]
41001                 })
41002             });
41003             
41004             if(!this.preferedCountries) {
41005                 this.preferedCountries = [
41006                     'hk',
41007                     'gb',
41008                     'us'
41009                 ];
41010             }
41011             
41012             var p = this.preferedCountries.reverse();
41013             
41014             if(p) {
41015                 for (var i = 0; i < p.length; i++) {
41016                     for (var j = 0; j < this.allCountries.length; j++) {
41017                         if(this.allCountries[j].iso2 == p[i]) {
41018                             var t = this.allCountries[j];
41019                             this.allCountries.splice(j,1);
41020                             this.allCountries.unshift(t);
41021                         }
41022                     } 
41023                 }
41024             }
41025             
41026             this.store.proxy.data = {
41027                 success: true,
41028                 data: this.allCountries
41029             };
41030             
41031             return cfg;
41032         },
41033         
41034         initEvents : function()
41035         {
41036             this.createList();
41037             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
41038             
41039             this.indicator = this.indicatorEl();
41040             this.flag = this.flagEl();
41041             this.dialCodeHolder = this.dialCodeHolderEl();
41042             
41043             this.trigger = this.el.select('div.flag-box',true).first();
41044             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41045             
41046             var _this = this;
41047             
41048             (function(){
41049                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41050                 _this.list.setWidth(lw);
41051             }).defer(100);
41052             
41053             this.list.on('mouseover', this.onViewOver, this);
41054             this.list.on('mousemove', this.onViewMove, this);
41055             this.inputEl().on("keyup", this.onKeyUp, this);
41056             this.inputEl().on("keypress", this.onKeyPress, this);
41057             
41058             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
41059
41060             this.view = new Roo.View(this.list, this.tpl, {
41061                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41062             });
41063             
41064             this.view.on('click', this.onViewClick, this);
41065             this.setValue(this.defaultDialCode);
41066         },
41067         
41068         onTriggerClick : function(e)
41069         {
41070             Roo.log('trigger click');
41071             if(this.disabled){
41072                 return;
41073             }
41074             
41075             if(this.isExpanded()){
41076                 this.collapse();
41077                 this.hasFocus = false;
41078             }else {
41079                 this.store.load({});
41080                 this.hasFocus = true;
41081                 this.expand();
41082             }
41083         },
41084         
41085         isExpanded : function()
41086         {
41087             return this.list.isVisible();
41088         },
41089         
41090         collapse : function()
41091         {
41092             if(!this.isExpanded()){
41093                 return;
41094             }
41095             this.list.hide();
41096             Roo.get(document).un('mousedown', this.collapseIf, this);
41097             Roo.get(document).un('mousewheel', this.collapseIf, this);
41098             this.fireEvent('collapse', this);
41099             this.validate();
41100         },
41101         
41102         expand : function()
41103         {
41104             Roo.log('expand');
41105
41106             if(this.isExpanded() || !this.hasFocus){
41107                 return;
41108             }
41109             
41110             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
41111             this.list.setWidth(lw);
41112             
41113             this.list.show();
41114             this.restrictHeight();
41115             
41116             Roo.get(document).on('mousedown', this.collapseIf, this);
41117             Roo.get(document).on('mousewheel', this.collapseIf, this);
41118             
41119             this.fireEvent('expand', this);
41120         },
41121         
41122         restrictHeight : function()
41123         {
41124             this.list.alignTo(this.inputEl(), this.listAlign);
41125             this.list.alignTo(this.inputEl(), this.listAlign);
41126         },
41127         
41128         onViewOver : function(e, t)
41129         {
41130             if(this.inKeyMode){
41131                 return;
41132             }
41133             var item = this.view.findItemFromChild(t);
41134             
41135             if(item){
41136                 var index = this.view.indexOf(item);
41137                 this.select(index, false);
41138             }
41139         },
41140
41141         // private
41142         onViewClick : function(view, doFocus, el, e)
41143         {
41144             var index = this.view.getSelectedIndexes()[0];
41145             
41146             var r = this.store.getAt(index);
41147             
41148             if(r){
41149                 this.onSelect(r, index);
41150             }
41151             if(doFocus !== false && !this.blockFocus){
41152                 this.inputEl().focus();
41153             }
41154         },
41155         
41156         onViewMove : function(e, t)
41157         {
41158             this.inKeyMode = false;
41159         },
41160         
41161         select : function(index, scrollIntoView)
41162         {
41163             this.selectedIndex = index;
41164             this.view.select(index);
41165             if(scrollIntoView !== false){
41166                 var el = this.view.getNode(index);
41167                 if(el){
41168                     this.list.scrollChildIntoView(el, false);
41169                 }
41170             }
41171         },
41172         
41173         createList : function()
41174         {
41175             this.list = Roo.get(document.body).createChild({
41176                 tag: 'ul',
41177                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
41178                 style: 'display:none'
41179             });
41180             
41181             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
41182         },
41183         
41184         collapseIf : function(e)
41185         {
41186             var in_combo  = e.within(this.el);
41187             var in_list =  e.within(this.list);
41188             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
41189             
41190             if (in_combo || in_list || is_list) {
41191                 return;
41192             }
41193             this.collapse();
41194         },
41195         
41196         onSelect : function(record, index)
41197         {
41198             if(this.fireEvent('beforeselect', this, record, index) !== false){
41199                 
41200                 this.setFlagClass(record.data.iso2);
41201                 this.setDialCode(record.data.dialCode);
41202                 this.hasFocus = false;
41203                 this.collapse();
41204                 this.fireEvent('select', this, record, index);
41205             }
41206         },
41207         
41208         flagEl : function()
41209         {
41210             var flag = this.el.select('div.flag',true).first();
41211             if(!flag){
41212                 return false;
41213             }
41214             return flag;
41215         },
41216         
41217         dialCodeHolderEl : function()
41218         {
41219             var d = this.el.select('input.dial-code-holder',true).first();
41220             if(!d){
41221                 return false;
41222             }
41223             return d;
41224         },
41225         
41226         setDialCode : function(v)
41227         {
41228             this.dialCodeHolder.dom.value = '+'+v;
41229         },
41230         
41231         setFlagClass : function(n)
41232         {
41233             this.flag.dom.className = 'flag '+n;
41234         },
41235         
41236         getValue : function()
41237         {
41238             var v = this.inputEl().getValue();
41239             if(this.dialCodeHolder) {
41240                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
41241             }
41242             return v;
41243         },
41244         
41245         setValue : function(v)
41246         {
41247             var d = this.getDialCode(v);
41248             
41249             //invalid dial code
41250             if(v.length == 0 || !d || d.length == 0) {
41251                 if(this.rendered){
41252                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41253                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41254                 }
41255                 return;
41256             }
41257             
41258             //valid dial code
41259             this.setFlagClass(this.dialCodeMapping[d].iso2);
41260             this.setDialCode(d);
41261             this.inputEl().dom.value = v.replace('+'+d,'');
41262             this.hiddenEl().dom.value = this.getValue();
41263             
41264             this.validate();
41265         },
41266         
41267         getDialCode : function(v)
41268         {
41269             v = v ||  '';
41270             
41271             if (v.length == 0) {
41272                 return this.dialCodeHolder.dom.value;
41273             }
41274             
41275             var dialCode = "";
41276             if (v.charAt(0) != "+") {
41277                 return false;
41278             }
41279             var numericChars = "";
41280             for (var i = 1; i < v.length; i++) {
41281               var c = v.charAt(i);
41282               if (!isNaN(c)) {
41283                 numericChars += c;
41284                 if (this.dialCodeMapping[numericChars]) {
41285                   dialCode = v.substr(1, i);
41286                 }
41287                 if (numericChars.length == 4) {
41288                   break;
41289                 }
41290               }
41291             }
41292             return dialCode;
41293         },
41294         
41295         reset : function()
41296         {
41297             this.setValue(this.defaultDialCode);
41298             this.validate();
41299         },
41300         
41301         hiddenEl : function()
41302         {
41303             return this.el.select('input.hidden-tel-input',true).first();
41304         },
41305         
41306         // after setting val
41307         onKeyUp : function(e){
41308             this.setValue(this.getValue());
41309         },
41310         
41311         onKeyPress : function(e){
41312             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
41313                 e.stopEvent();
41314             }
41315         }
41316         
41317 });
41318 /**
41319  * @class Roo.bootstrap.MoneyField
41320  * @extends Roo.bootstrap.ComboBox
41321  * Bootstrap MoneyField class
41322  * 
41323  * @constructor
41324  * Create a new MoneyField.
41325  * @param {Object} config Configuration options
41326  */
41327
41328 Roo.bootstrap.MoneyField = function(config) {
41329     
41330     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
41331     
41332 };
41333
41334 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
41335     
41336     /**
41337      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41338      */
41339     allowDecimals : true,
41340     /**
41341      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41342      */
41343     decimalSeparator : ".",
41344     /**
41345      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41346      */
41347     decimalPrecision : 0,
41348     /**
41349      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41350      */
41351     allowNegative : true,
41352     /**
41353      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41354      */
41355     allowZero: true,
41356     /**
41357      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41358      */
41359     minValue : Number.NEGATIVE_INFINITY,
41360     /**
41361      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41362      */
41363     maxValue : Number.MAX_VALUE,
41364     /**
41365      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41366      */
41367     minText : "The minimum value for this field is {0}",
41368     /**
41369      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41370      */
41371     maxText : "The maximum value for this field is {0}",
41372     /**
41373      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41374      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41375      */
41376     nanText : "{0} is not a valid number",
41377     /**
41378      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
41379      */
41380     castInt : true,
41381     /**
41382      * @cfg {String} defaults currency of the MoneyField
41383      * value should be in lkey
41384      */
41385     defaultCurrency : false,
41386     /**
41387      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41388      */
41389     thousandsDelimiter : false,
41390     /**
41391      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41392      */
41393     max_length: false,
41394     
41395     inputlg : 9,
41396     inputmd : 9,
41397     inputsm : 9,
41398     inputxs : 6,
41399     
41400     store : false,
41401     
41402     getAutoCreate : function()
41403     {
41404         var align = this.labelAlign || this.parentLabelAlign();
41405         
41406         var id = Roo.id();
41407
41408         var cfg = {
41409             cls: 'form-group',
41410             cn: []
41411         };
41412
41413         var input =  {
41414             tag: 'input',
41415             id : id,
41416             cls : 'form-control roo-money-amount-input',
41417             autocomplete: 'new-password'
41418         };
41419         
41420         var hiddenInput = {
41421             tag: 'input',
41422             type: 'hidden',
41423             id: Roo.id(),
41424             cls: 'hidden-number-input'
41425         };
41426         
41427         if(this.max_length) {
41428             input.maxlength = this.max_length; 
41429         }
41430         
41431         if (this.name) {
41432             hiddenInput.name = this.name;
41433         }
41434
41435         if (this.disabled) {
41436             input.disabled = true;
41437         }
41438
41439         var clg = 12 - this.inputlg;
41440         var cmd = 12 - this.inputmd;
41441         var csm = 12 - this.inputsm;
41442         var cxs = 12 - this.inputxs;
41443         
41444         var container = {
41445             tag : 'div',
41446             cls : 'row roo-money-field',
41447             cn : [
41448                 {
41449                     tag : 'div',
41450                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
41451                     cn : [
41452                         {
41453                             tag : 'div',
41454                             cls: 'roo-select2-container input-group',
41455                             cn: [
41456                                 {
41457                                     tag : 'input',
41458                                     cls : 'form-control roo-money-currency-input',
41459                                     autocomplete: 'new-password',
41460                                     readOnly : 1,
41461                                     name : this.currencyName
41462                                 },
41463                                 {
41464                                     tag :'span',
41465                                     cls : 'input-group-addon',
41466                                     cn : [
41467                                         {
41468                                             tag: 'span',
41469                                             cls: 'caret'
41470                                         }
41471                                     ]
41472                                 }
41473                             ]
41474                         }
41475                     ]
41476                 },
41477                 {
41478                     tag : 'div',
41479                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41480                     cn : [
41481                         {
41482                             tag: 'div',
41483                             cls: this.hasFeedback ? 'has-feedback' : '',
41484                             cn: [
41485                                 input
41486                             ]
41487                         }
41488                     ]
41489                 }
41490             ]
41491             
41492         };
41493         
41494         if (this.fieldLabel.length) {
41495             var indicator = {
41496                 tag: 'i',
41497                 tooltip: 'This field is required'
41498             };
41499
41500             var label = {
41501                 tag: 'label',
41502                 'for':  id,
41503                 cls: 'control-label',
41504                 cn: []
41505             };
41506
41507             var label_text = {
41508                 tag: 'span',
41509                 html: this.fieldLabel
41510             };
41511
41512             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41513             label.cn = [
41514                 indicator,
41515                 label_text
41516             ];
41517
41518             if(this.indicatorpos == 'right') {
41519                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41520                 label.cn = [
41521                     label_text,
41522                     indicator
41523                 ];
41524             }
41525
41526             if(align == 'left') {
41527                 container = {
41528                     tag: 'div',
41529                     cn: [
41530                         container
41531                     ]
41532                 };
41533
41534                 if(this.labelWidth > 12){
41535                     label.style = "width: " + this.labelWidth + 'px';
41536                 }
41537                 if(this.labelWidth < 13 && this.labelmd == 0){
41538                     this.labelmd = this.labelWidth;
41539                 }
41540                 if(this.labellg > 0){
41541                     label.cls += ' col-lg-' + this.labellg;
41542                     input.cls += ' col-lg-' + (12 - this.labellg);
41543                 }
41544                 if(this.labelmd > 0){
41545                     label.cls += ' col-md-' + this.labelmd;
41546                     container.cls += ' col-md-' + (12 - this.labelmd);
41547                 }
41548                 if(this.labelsm > 0){
41549                     label.cls += ' col-sm-' + this.labelsm;
41550                     container.cls += ' col-sm-' + (12 - this.labelsm);
41551                 }
41552                 if(this.labelxs > 0){
41553                     label.cls += ' col-xs-' + this.labelxs;
41554                     container.cls += ' col-xs-' + (12 - this.labelxs);
41555                 }
41556             }
41557         }
41558
41559         cfg.cn = [
41560             label,
41561             container,
41562             hiddenInput
41563         ];
41564         
41565         var settings = this;
41566
41567         ['xs','sm','md','lg'].map(function(size){
41568             if (settings[size]) {
41569                 cfg.cls += ' col-' + size + '-' + settings[size];
41570             }
41571         });
41572         
41573         return cfg;
41574     },
41575     
41576     initEvents : function()
41577     {
41578         this.indicator = this.indicatorEl();
41579         
41580         this.initCurrencyEvent();
41581         
41582         this.initNumberEvent();
41583     },
41584     
41585     initCurrencyEvent : function()
41586     {
41587         if (!this.store) {
41588             throw "can not find store for combo";
41589         }
41590         
41591         this.store = Roo.factory(this.store, Roo.data);
41592         this.store.parent = this;
41593         
41594         this.createList();
41595         
41596         this.triggerEl = this.el.select('.input-group-addon', true).first();
41597         
41598         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41599         
41600         var _this = this;
41601         
41602         (function(){
41603             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41604             _this.list.setWidth(lw);
41605         }).defer(100);
41606         
41607         this.list.on('mouseover', this.onViewOver, this);
41608         this.list.on('mousemove', this.onViewMove, this);
41609         this.list.on('scroll', this.onViewScroll, this);
41610         
41611         if(!this.tpl){
41612             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41613         }
41614         
41615         this.view = new Roo.View(this.list, this.tpl, {
41616             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41617         });
41618         
41619         this.view.on('click', this.onViewClick, this);
41620         
41621         this.store.on('beforeload', this.onBeforeLoad, this);
41622         this.store.on('load', this.onLoad, this);
41623         this.store.on('loadexception', this.onLoadException, this);
41624         
41625         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41626             "up" : function(e){
41627                 this.inKeyMode = true;
41628                 this.selectPrev();
41629             },
41630
41631             "down" : function(e){
41632                 if(!this.isExpanded()){
41633                     this.onTriggerClick();
41634                 }else{
41635                     this.inKeyMode = true;
41636                     this.selectNext();
41637                 }
41638             },
41639
41640             "enter" : function(e){
41641                 this.collapse();
41642                 
41643                 if(this.fireEvent("specialkey", this, e)){
41644                     this.onViewClick(false);
41645                 }
41646                 
41647                 return true;
41648             },
41649
41650             "esc" : function(e){
41651                 this.collapse();
41652             },
41653
41654             "tab" : function(e){
41655                 this.collapse();
41656                 
41657                 if(this.fireEvent("specialkey", this, e)){
41658                     this.onViewClick(false);
41659                 }
41660                 
41661                 return true;
41662             },
41663
41664             scope : this,
41665
41666             doRelay : function(foo, bar, hname){
41667                 if(hname == 'down' || this.scope.isExpanded()){
41668                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41669                 }
41670                 return true;
41671             },
41672
41673             forceKeyDown: true
41674         });
41675         
41676         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41677         
41678     },
41679     
41680     initNumberEvent : function(e)
41681     {
41682         this.inputEl().on("keydown" , this.fireKey,  this);
41683         this.inputEl().on("focus", this.onFocus,  this);
41684         this.inputEl().on("blur", this.onBlur,  this);
41685         
41686         this.inputEl().relayEvent('keyup', this);
41687         
41688         if(this.indicator){
41689             this.indicator.addClass('invisible');
41690         }
41691  
41692         this.originalValue = this.getValue();
41693         
41694         if(this.validationEvent == 'keyup'){
41695             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41696             this.inputEl().on('keyup', this.filterValidation, this);
41697         }
41698         else if(this.validationEvent !== false){
41699             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41700         }
41701         
41702         if(this.selectOnFocus){
41703             this.on("focus", this.preFocus, this);
41704             
41705         }
41706         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41707             this.inputEl().on("keypress", this.filterKeys, this);
41708         } else {
41709             this.inputEl().relayEvent('keypress', this);
41710         }
41711         
41712         var allowed = "0123456789";
41713         
41714         if(this.allowDecimals){
41715             allowed += this.decimalSeparator;
41716         }
41717         
41718         if(this.allowNegative){
41719             allowed += "-";
41720         }
41721         
41722         if(this.thousandsDelimiter) {
41723             allowed += ",";
41724         }
41725         
41726         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41727         
41728         var keyPress = function(e){
41729             
41730             var k = e.getKey();
41731             
41732             var c = e.getCharCode();
41733             
41734             if(
41735                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41736                     allowed.indexOf(String.fromCharCode(c)) === -1
41737             ){
41738                 e.stopEvent();
41739                 return;
41740             }
41741             
41742             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41743                 return;
41744             }
41745             
41746             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41747                 e.stopEvent();
41748             }
41749         };
41750         
41751         this.inputEl().on("keypress", keyPress, this);
41752         
41753     },
41754     
41755     onTriggerClick : function(e)
41756     {   
41757         if(this.disabled){
41758             return;
41759         }
41760         
41761         this.page = 0;
41762         this.loadNext = false;
41763         
41764         if(this.isExpanded()){
41765             this.collapse();
41766             return;
41767         }
41768         
41769         this.hasFocus = true;
41770         
41771         if(this.triggerAction == 'all') {
41772             this.doQuery(this.allQuery, true);
41773             return;
41774         }
41775         
41776         this.doQuery(this.getRawValue());
41777     },
41778     
41779     getCurrency : function()
41780     {   
41781         var v = this.currencyEl().getValue();
41782         
41783         return v;
41784     },
41785     
41786     restrictHeight : function()
41787     {
41788         this.list.alignTo(this.currencyEl(), this.listAlign);
41789         this.list.alignTo(this.currencyEl(), this.listAlign);
41790     },
41791     
41792     onViewClick : function(view, doFocus, el, e)
41793     {
41794         var index = this.view.getSelectedIndexes()[0];
41795         
41796         var r = this.store.getAt(index);
41797         
41798         if(r){
41799             this.onSelect(r, index);
41800         }
41801     },
41802     
41803     onSelect : function(record, index){
41804         
41805         if(this.fireEvent('beforeselect', this, record, index) !== false){
41806         
41807             this.setFromCurrencyData(index > -1 ? record.data : false);
41808             
41809             this.collapse();
41810             
41811             this.fireEvent('select', this, record, index);
41812         }
41813     },
41814     
41815     setFromCurrencyData : function(o)
41816     {
41817         var currency = '';
41818         
41819         this.lastCurrency = o;
41820         
41821         if (this.currencyField) {
41822             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41823         } else {
41824             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41825         }
41826         
41827         this.lastSelectionText = currency;
41828         
41829         //setting default currency
41830         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41831             this.setCurrency(this.defaultCurrency);
41832             return;
41833         }
41834         
41835         this.setCurrency(currency);
41836     },
41837     
41838     setFromData : function(o)
41839     {
41840         var c = {};
41841         
41842         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41843         
41844         this.setFromCurrencyData(c);
41845         
41846         var value = '';
41847         
41848         if (this.name) {
41849             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41850         } else {
41851             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41852         }
41853         
41854         this.setValue(value);
41855         
41856     },
41857     
41858     setCurrency : function(v)
41859     {   
41860         this.currencyValue = v;
41861         
41862         if(this.rendered){
41863             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41864             this.validate();
41865         }
41866     },
41867     
41868     setValue : function(v)
41869     {
41870         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41871         
41872         this.value = v;
41873         
41874         if(this.rendered){
41875             
41876             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41877             
41878             this.inputEl().dom.value = (v == '') ? '' :
41879                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41880             
41881             if(!this.allowZero && v === '0') {
41882                 this.hiddenEl().dom.value = '';
41883                 this.inputEl().dom.value = '';
41884             }
41885             
41886             this.validate();
41887         }
41888     },
41889     
41890     getRawValue : function()
41891     {
41892         var v = this.inputEl().getValue();
41893         
41894         return v;
41895     },
41896     
41897     getValue : function()
41898     {
41899         return this.fixPrecision(this.parseValue(this.getRawValue()));
41900     },
41901     
41902     parseValue : function(value)
41903     {
41904         if(this.thousandsDelimiter) {
41905             value += "";
41906             r = new RegExp(",", "g");
41907             value = value.replace(r, "");
41908         }
41909         
41910         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41911         return isNaN(value) ? '' : value;
41912         
41913     },
41914     
41915     fixPrecision : function(value)
41916     {
41917         if(this.thousandsDelimiter) {
41918             value += "";
41919             r = new RegExp(",", "g");
41920             value = value.replace(r, "");
41921         }
41922         
41923         var nan = isNaN(value);
41924         
41925         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41926             return nan ? '' : value;
41927         }
41928         return parseFloat(value).toFixed(this.decimalPrecision);
41929     },
41930     
41931     decimalPrecisionFcn : function(v)
41932     {
41933         return Math.floor(v);
41934     },
41935     
41936     validateValue : function(value)
41937     {
41938         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41939             return false;
41940         }
41941         
41942         var num = this.parseValue(value);
41943         
41944         if(isNaN(num)){
41945             this.markInvalid(String.format(this.nanText, value));
41946             return false;
41947         }
41948         
41949         if(num < this.minValue){
41950             this.markInvalid(String.format(this.minText, this.minValue));
41951             return false;
41952         }
41953         
41954         if(num > this.maxValue){
41955             this.markInvalid(String.format(this.maxText, this.maxValue));
41956             return false;
41957         }
41958         
41959         return true;
41960     },
41961     
41962     validate : function()
41963     {
41964         if(this.disabled || this.allowBlank){
41965             this.markValid();
41966             return true;
41967         }
41968         
41969         var currency = this.getCurrency();
41970         
41971         if(this.validateValue(this.getRawValue()) && currency.length){
41972             this.markValid();
41973             return true;
41974         }
41975         
41976         this.markInvalid();
41977         return false;
41978     },
41979     
41980     getName: function()
41981     {
41982         return this.name;
41983     },
41984     
41985     beforeBlur : function()
41986     {
41987         if(!this.castInt){
41988             return;
41989         }
41990         
41991         var v = this.parseValue(this.getRawValue());
41992         
41993         if(v || v == 0){
41994             this.setValue(v);
41995         }
41996     },
41997     
41998     onBlur : function()
41999     {
42000         this.beforeBlur();
42001         
42002         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
42003             //this.el.removeClass(this.focusClass);
42004         }
42005         
42006         this.hasFocus = false;
42007         
42008         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
42009             this.validate();
42010         }
42011         
42012         var v = this.getValue();
42013         
42014         if(String(v) !== String(this.startValue)){
42015             this.fireEvent('change', this, v, this.startValue);
42016         }
42017         
42018         this.fireEvent("blur", this);
42019     },
42020     
42021     inputEl : function()
42022     {
42023         return this.el.select('.roo-money-amount-input', true).first();
42024     },
42025     
42026     currencyEl : function()
42027     {
42028         return this.el.select('.roo-money-currency-input', true).first();
42029     },
42030     
42031     hiddenEl : function()
42032     {
42033         return this.el.select('input.hidden-number-input',true).first();
42034     }
42035     
42036 });/**
42037  * @class Roo.bootstrap.BezierSignature
42038  * @extends Roo.bootstrap.Component
42039  * Bootstrap BezierSignature class
42040  * This script refer to:
42041  *    Title: Signature Pad
42042  *    Author: szimek
42043  *    Availability: https://github.com/szimek/signature_pad
42044  *
42045  * @constructor
42046  * Create a new BezierSignature
42047  * @param {Object} config The config object
42048  */
42049
42050 Roo.bootstrap.BezierSignature = function(config){
42051     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
42052     this.addEvents({
42053         "resize" : true
42054     });
42055 };
42056
42057 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
42058 {
42059      
42060     curve_data: [],
42061     
42062     is_empty: true,
42063     
42064     mouse_btn_down: true,
42065     
42066     /**
42067      * @cfg {int} canvas height
42068      */
42069     canvas_height: '200px',
42070     
42071     /**
42072      * @cfg {float|function} Radius of a single dot.
42073      */ 
42074     dot_size: false,
42075     
42076     /**
42077      * @cfg {float} Minimum width of a line. Defaults to 0.5.
42078      */
42079     min_width: 0.5,
42080     
42081     /**
42082      * @cfg {float} Maximum width of a line. Defaults to 2.5.
42083      */
42084     max_width: 2.5,
42085     
42086     /**
42087      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
42088      */
42089     throttle: 16,
42090     
42091     /**
42092      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
42093      */
42094     min_distance: 5,
42095     
42096     /**
42097      * @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.
42098      */
42099     bg_color: 'rgba(0, 0, 0, 0)',
42100     
42101     /**
42102      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
42103      */
42104     dot_color: 'black',
42105     
42106     /**
42107      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
42108      */ 
42109     velocity_filter_weight: 0.7,
42110     
42111     /**
42112      * @cfg {function} Callback when stroke begin. 
42113      */
42114     onBegin: false,
42115     
42116     /**
42117      * @cfg {function} Callback when stroke end.
42118      */
42119     onEnd: false,
42120     
42121     getAutoCreate : function()
42122     {
42123         var cls = 'roo-signature column';
42124         
42125         if(this.cls){
42126             cls += ' ' + this.cls;
42127         }
42128         
42129         var col_sizes = [
42130             'lg',
42131             'md',
42132             'sm',
42133             'xs'
42134         ];
42135         
42136         for(var i = 0; i < col_sizes.length; i++) {
42137             if(this[col_sizes[i]]) {
42138                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
42139             }
42140         }
42141         
42142         var cfg = {
42143             tag: 'div',
42144             cls: cls,
42145             cn: [
42146                 {
42147                     tag: 'div',
42148                     cls: 'roo-signature-body',
42149                     cn: [
42150                         {
42151                             tag: 'canvas',
42152                             cls: 'roo-signature-body-canvas',
42153                             height: this.canvas_height,
42154                             width: this.canvas_width
42155                         }
42156                     ]
42157                 },
42158                 {
42159                     tag: 'input',
42160                     type: 'file',
42161                     style: 'display: none'
42162                 }
42163             ]
42164         };
42165         
42166         return cfg;
42167     },
42168     
42169     initEvents: function() 
42170     {
42171         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
42172         
42173         var canvas = this.canvasEl();
42174         
42175         // mouse && touch event swapping...
42176         canvas.dom.style.touchAction = 'none';
42177         canvas.dom.style.msTouchAction = 'none';
42178         
42179         this.mouse_btn_down = false;
42180         canvas.on('mousedown', this._handleMouseDown, this);
42181         canvas.on('mousemove', this._handleMouseMove, this);
42182         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
42183         
42184         if (window.PointerEvent) {
42185             canvas.on('pointerdown', this._handleMouseDown, this);
42186             canvas.on('pointermove', this._handleMouseMove, this);
42187             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
42188         }
42189         
42190         if ('ontouchstart' in window) {
42191             canvas.on('touchstart', this._handleTouchStart, this);
42192             canvas.on('touchmove', this._handleTouchMove, this);
42193             canvas.on('touchend', this._handleTouchEnd, this);
42194         }
42195         
42196         Roo.EventManager.onWindowResize(this.resize, this, true);
42197         
42198         // file input event
42199         this.fileEl().on('change', this.uploadImage, this);
42200         
42201         this.clear();
42202         
42203         this.resize();
42204     },
42205     
42206     resize: function(){
42207         
42208         var canvas = this.canvasEl().dom;
42209         var ctx = this.canvasElCtx();
42210         var img_data = false;
42211         
42212         if(canvas.width > 0) {
42213             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
42214         }
42215         // setting canvas width will clean img data
42216         canvas.width = 0;
42217         
42218         var style = window.getComputedStyle ? 
42219             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
42220             
42221         var padding_left = parseInt(style.paddingLeft) || 0;
42222         var padding_right = parseInt(style.paddingRight) || 0;
42223         
42224         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
42225         
42226         if(img_data) {
42227             ctx.putImageData(img_data, 0, 0);
42228         }
42229     },
42230     
42231     _handleMouseDown: function(e)
42232     {
42233         if (e.browserEvent.which === 1) {
42234             this.mouse_btn_down = true;
42235             this.strokeBegin(e);
42236         }
42237     },
42238     
42239     _handleMouseMove: function (e)
42240     {
42241         if (this.mouse_btn_down) {
42242             this.strokeMoveUpdate(e);
42243         }
42244     },
42245     
42246     _handleMouseUp: function (e)
42247     {
42248         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
42249             this.mouse_btn_down = false;
42250             this.strokeEnd(e);
42251         }
42252     },
42253     
42254     _handleTouchStart: function (e) {
42255         
42256         e.preventDefault();
42257         if (e.browserEvent.targetTouches.length === 1) {
42258             // var touch = e.browserEvent.changedTouches[0];
42259             // this.strokeBegin(touch);
42260             
42261              this.strokeBegin(e); // assume e catching the correct xy...
42262         }
42263     },
42264     
42265     _handleTouchMove: function (e) {
42266         e.preventDefault();
42267         // var touch = event.targetTouches[0];
42268         // _this._strokeMoveUpdate(touch);
42269         this.strokeMoveUpdate(e);
42270     },
42271     
42272     _handleTouchEnd: function (e) {
42273         var wasCanvasTouched = e.target === this.canvasEl().dom;
42274         if (wasCanvasTouched) {
42275             e.preventDefault();
42276             // var touch = event.changedTouches[0];
42277             // _this._strokeEnd(touch);
42278             this.strokeEnd(e);
42279         }
42280     },
42281     
42282     reset: function () {
42283         this._lastPoints = [];
42284         this._lastVelocity = 0;
42285         this._lastWidth = (this.min_width + this.max_width) / 2;
42286         this.canvasElCtx().fillStyle = this.dot_color;
42287     },
42288     
42289     strokeMoveUpdate: function(e)
42290     {
42291         this.strokeUpdate(e);
42292         
42293         if (this.throttle) {
42294             this.throttleStroke(this.strokeUpdate, this.throttle);
42295         }
42296         else {
42297             this.strokeUpdate(e);
42298         }
42299     },
42300     
42301     strokeBegin: function(e)
42302     {
42303         var newPointGroup = {
42304             color: this.dot_color,
42305             points: []
42306         };
42307         
42308         if (typeof this.onBegin === 'function') {
42309             this.onBegin(e);
42310         }
42311         
42312         this.curve_data.push(newPointGroup);
42313         this.reset();
42314         this.strokeUpdate(e);
42315     },
42316     
42317     strokeUpdate: function(e)
42318     {
42319         var rect = this.canvasEl().dom.getBoundingClientRect();
42320         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
42321         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
42322         var lastPoints = lastPointGroup.points;
42323         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
42324         var isLastPointTooClose = lastPoint
42325             ? point.distanceTo(lastPoint) <= this.min_distance
42326             : false;
42327         var color = lastPointGroup.color;
42328         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
42329             var curve = this.addPoint(point);
42330             if (!lastPoint) {
42331                 this.drawDot({color: color, point: point});
42332             }
42333             else if (curve) {
42334                 this.drawCurve({color: color, curve: curve});
42335             }
42336             lastPoints.push({
42337                 time: point.time,
42338                 x: point.x,
42339                 y: point.y
42340             });
42341         }
42342     },
42343     
42344     strokeEnd: function(e)
42345     {
42346         this.strokeUpdate(e);
42347         if (typeof this.onEnd === 'function') {
42348             this.onEnd(e);
42349         }
42350     },
42351     
42352     addPoint:  function (point) {
42353         var _lastPoints = this._lastPoints;
42354         _lastPoints.push(point);
42355         if (_lastPoints.length > 2) {
42356             if (_lastPoints.length === 3) {
42357                 _lastPoints.unshift(_lastPoints[0]);
42358             }
42359             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
42360             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
42361             _lastPoints.shift();
42362             return curve;
42363         }
42364         return null;
42365     },
42366     
42367     calculateCurveWidths: function (startPoint, endPoint) {
42368         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
42369             (1 - this.velocity_filter_weight) * this._lastVelocity;
42370
42371         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
42372         var widths = {
42373             end: newWidth,
42374             start: this._lastWidth
42375         };
42376         
42377         this._lastVelocity = velocity;
42378         this._lastWidth = newWidth;
42379         return widths;
42380     },
42381     
42382     drawDot: function (_a) {
42383         var color = _a.color, point = _a.point;
42384         var ctx = this.canvasElCtx();
42385         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
42386         ctx.beginPath();
42387         this.drawCurveSegment(point.x, point.y, width);
42388         ctx.closePath();
42389         ctx.fillStyle = color;
42390         ctx.fill();
42391     },
42392     
42393     drawCurve: function (_a) {
42394         var color = _a.color, curve = _a.curve;
42395         var ctx = this.canvasElCtx();
42396         var widthDelta = curve.endWidth - curve.startWidth;
42397         var drawSteps = Math.floor(curve.length()) * 2;
42398         ctx.beginPath();
42399         ctx.fillStyle = color;
42400         for (var i = 0; i < drawSteps; i += 1) {
42401         var t = i / drawSteps;
42402         var tt = t * t;
42403         var ttt = tt * t;
42404         var u = 1 - t;
42405         var uu = u * u;
42406         var uuu = uu * u;
42407         var x = uuu * curve.startPoint.x;
42408         x += 3 * uu * t * curve.control1.x;
42409         x += 3 * u * tt * curve.control2.x;
42410         x += ttt * curve.endPoint.x;
42411         var y = uuu * curve.startPoint.y;
42412         y += 3 * uu * t * curve.control1.y;
42413         y += 3 * u * tt * curve.control2.y;
42414         y += ttt * curve.endPoint.y;
42415         var width = curve.startWidth + ttt * widthDelta;
42416         this.drawCurveSegment(x, y, width);
42417         }
42418         ctx.closePath();
42419         ctx.fill();
42420     },
42421     
42422     drawCurveSegment: function (x, y, width) {
42423         var ctx = this.canvasElCtx();
42424         ctx.moveTo(x, y);
42425         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
42426         this.is_empty = false;
42427     },
42428     
42429     clear: function()
42430     {
42431         var ctx = this.canvasElCtx();
42432         var canvas = this.canvasEl().dom;
42433         ctx.fillStyle = this.bg_color;
42434         ctx.clearRect(0, 0, canvas.width, canvas.height);
42435         ctx.fillRect(0, 0, canvas.width, canvas.height);
42436         this.curve_data = [];
42437         this.reset();
42438         this.is_empty = true;
42439     },
42440     
42441     fileEl: function()
42442     {
42443         return  this.el.select('input',true).first();
42444     },
42445     
42446     canvasEl: function()
42447     {
42448         return this.el.select('canvas',true).first();
42449     },
42450     
42451     canvasElCtx: function()
42452     {
42453         return this.el.select('canvas',true).first().dom.getContext('2d');
42454     },
42455     
42456     getImage: function(type)
42457     {
42458         if(this.is_empty) {
42459             return false;
42460         }
42461         
42462         // encryption ?
42463         return this.canvasEl().dom.toDataURL('image/'+type, 1);
42464     },
42465     
42466     drawFromImage: function(img_src)
42467     {
42468         var img = new Image();
42469         
42470         img.onload = function(){
42471             this.canvasElCtx().drawImage(img, 0, 0);
42472         }.bind(this);
42473         
42474         img.src = img_src;
42475         
42476         this.is_empty = false;
42477     },
42478     
42479     selectImage: function()
42480     {
42481         this.fileEl().dom.click();
42482     },
42483     
42484     uploadImage: function(e)
42485     {
42486         var reader = new FileReader();
42487         
42488         reader.onload = function(e){
42489             var img = new Image();
42490             img.onload = function(){
42491                 this.reset();
42492                 this.canvasElCtx().drawImage(img, 0, 0);
42493             }.bind(this);
42494             img.src = e.target.result;
42495         }.bind(this);
42496         
42497         reader.readAsDataURL(e.target.files[0]);
42498     },
42499     
42500     // Bezier Point Constructor
42501     Point: (function () {
42502         function Point(x, y, time) {
42503             this.x = x;
42504             this.y = y;
42505             this.time = time || Date.now();
42506         }
42507         Point.prototype.distanceTo = function (start) {
42508             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42509         };
42510         Point.prototype.equals = function (other) {
42511             return this.x === other.x && this.y === other.y && this.time === other.time;
42512         };
42513         Point.prototype.velocityFrom = function (start) {
42514             return this.time !== start.time
42515             ? this.distanceTo(start) / (this.time - start.time)
42516             : 0;
42517         };
42518         return Point;
42519     }()),
42520     
42521     
42522     // Bezier Constructor
42523     Bezier: (function () {
42524         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42525             this.startPoint = startPoint;
42526             this.control2 = control2;
42527             this.control1 = control1;
42528             this.endPoint = endPoint;
42529             this.startWidth = startWidth;
42530             this.endWidth = endWidth;
42531         }
42532         Bezier.fromPoints = function (points, widths, scope) {
42533             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42534             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42535             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42536         };
42537         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42538             var dx1 = s1.x - s2.x;
42539             var dy1 = s1.y - s2.y;
42540             var dx2 = s2.x - s3.x;
42541             var dy2 = s2.y - s3.y;
42542             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42543             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42544             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42545             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42546             var dxm = m1.x - m2.x;
42547             var dym = m1.y - m2.y;
42548             var k = l2 / (l1 + l2);
42549             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42550             var tx = s2.x - cm.x;
42551             var ty = s2.y - cm.y;
42552             return {
42553                 c1: new scope.Point(m1.x + tx, m1.y + ty),
42554                 c2: new scope.Point(m2.x + tx, m2.y + ty)
42555             };
42556         };
42557         Bezier.prototype.length = function () {
42558             var steps = 10;
42559             var length = 0;
42560             var px;
42561             var py;
42562             for (var i = 0; i <= steps; i += 1) {
42563                 var t = i / steps;
42564                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42565                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42566                 if (i > 0) {
42567                     var xdiff = cx - px;
42568                     var ydiff = cy - py;
42569                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42570                 }
42571                 px = cx;
42572                 py = cy;
42573             }
42574             return length;
42575         };
42576         Bezier.prototype.point = function (t, start, c1, c2, end) {
42577             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42578             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42579             + (3.0 * c2 * (1.0 - t) * t * t)
42580             + (end * t * t * t);
42581         };
42582         return Bezier;
42583     }()),
42584     
42585     throttleStroke: function(fn, wait) {
42586       if (wait === void 0) { wait = 250; }
42587       var previous = 0;
42588       var timeout = null;
42589       var result;
42590       var storedContext;
42591       var storedArgs;
42592       var later = function () {
42593           previous = Date.now();
42594           timeout = null;
42595           result = fn.apply(storedContext, storedArgs);
42596           if (!timeout) {
42597               storedContext = null;
42598               storedArgs = [];
42599           }
42600       };
42601       return function wrapper() {
42602           var args = [];
42603           for (var _i = 0; _i < arguments.length; _i++) {
42604               args[_i] = arguments[_i];
42605           }
42606           var now = Date.now();
42607           var remaining = wait - (now - previous);
42608           storedContext = this;
42609           storedArgs = args;
42610           if (remaining <= 0 || remaining > wait) {
42611               if (timeout) {
42612                   clearTimeout(timeout);
42613                   timeout = null;
42614               }
42615               previous = now;
42616               result = fn.apply(storedContext, storedArgs);
42617               if (!timeout) {
42618                   storedContext = null;
42619                   storedArgs = [];
42620               }
42621           }
42622           else if (!timeout) {
42623               timeout = window.setTimeout(later, remaining);
42624           }
42625           return result;
42626       };
42627   }
42628   
42629 });
42630
42631  
42632
42633