roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets[0], function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })();/*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass(['hidden','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  * Body
442  *
443  */
444
445 /**
446  * @class Roo.bootstrap.Body
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Body class
449  *
450  * @constructor
451  * Create a new body
452  * @param {Object} config The config object
453  */
454
455 Roo.bootstrap.Body = function(config){
456
457     config = config || {};
458
459     Roo.bootstrap.Body.superclass.constructor.call(this, config);
460     this.el = Roo.get(config.el ? config.el : document.body );
461     if (this.cls && this.cls.length) {
462         Roo.get(document.body).addClass(this.cls);
463     }
464 };
465
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
467
468     is_body : true,// just to make sure it's constructed?
469
470         autoCreate : {
471         cls: 'container'
472     },
473     onRender : function(ct, position)
474     {
475        /* Roo.log("Roo.bootstrap.Body - onRender");
476         if (this.cls && this.cls.length) {
477             Roo.get(document.body).addClass(this.cls);
478         }
479         // style??? xttr???
480         */
481     }
482
483
484
485
486 });
487 /*
488  * - LGPL
489  *
490  * button group
491  * 
492  */
493
494
495 /**
496  * @class Roo.bootstrap.ButtonGroup
497  * @extends Roo.bootstrap.Component
498  * Bootstrap ButtonGroup class
499  * @cfg {String} size lg | sm | xs (default empty normal)
500  * @cfg {String} align vertical | justified  (default none)
501  * @cfg {String} direction up | down (default down)
502  * @cfg {Boolean} toolbar false | true
503  * @cfg {Boolean} btn true | false
504  * 
505  * 
506  * @constructor
507  * Create a new Input
508  * @param {Object} config The config object
509  */
510
511 Roo.bootstrap.ButtonGroup = function(config){
512     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
513 };
514
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
516     
517     size: '',
518     align: '',
519     direction: '',
520     toolbar: false,
521     btn: true,
522
523     getAutoCreate : function(){
524         var cfg = {
525             cls: 'btn-group',
526             html : null
527         };
528         
529         cfg.html = this.html || cfg.html;
530         
531         if (this.toolbar) {
532             cfg = {
533                 cls: 'btn-toolbar',
534                 html: null
535             };
536             
537             return cfg;
538         }
539         
540         if (['vertical','justified'].indexOf(this.align)!==-1) {
541             cfg.cls = 'btn-group-' + this.align;
542             
543             if (this.align == 'justified') {
544                 console.log(this.items);
545             }
546         }
547         
548         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549             cfg.cls += ' btn-group-' + this.size;
550         }
551         
552         if (this.direction == 'up') {
553             cfg.cls += ' dropup' ;
554         }
555         
556         return cfg;
557     },
558     /**
559      * Add a button to the group (similar to NavItem API.)
560      */
561     addItem : function(cfg)
562     {
563         var cn = new Roo.bootstrap.Button(cfg);
564         //this.register(cn);
565         cn.parentId = this.id;
566         cn.onRender(this.el, null);
567         return cn;
568     }
569    
570 });
571
572  /*
573  * - LGPL
574  *
575  * button
576  * 
577  */
578
579 /**
580  * @class Roo.bootstrap.Button
581  * @extends Roo.bootstrap.Component
582  * Bootstrap Button class
583  * @cfg {String} html The button content
584  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587  * @cfg {String} size ( lg | sm | xs)
588  * @cfg {String} tag ( a | input | submit)
589  * @cfg {String} href empty or href
590  * @cfg {Boolean} disabled default false;
591  * @cfg {Boolean} isClose default false;
592  * @cfg {String} glyphicon depricated - use fa
593  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
594  * @cfg {String} badge text for badge
595  * @cfg {String} theme (default|glow)  
596  * @cfg {Boolean} inverse dark themed version
597  * @cfg {Boolean} toggle is it a slidy toggle button
598  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
599  * @cfg {String} ontext text for on slidy toggle state
600  * @cfg {String} offtext text for off slidy toggle state
601  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
602  * @cfg {Boolean} removeClass remove the standard class..
603  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
604  * 
605  * @constructor
606  * Create a new button
607  * @param {Object} config The config object
608  */
609
610
611 Roo.bootstrap.Button = function(config){
612     Roo.bootstrap.Button.superclass.constructor.call(this, config);
613     this.weightClass = ["btn-default btn-outline-secondary", 
614                        "btn-primary", 
615                        "btn-success", 
616                        "btn-info", 
617                        "btn-warning",
618                        "btn-danger",
619                        "btn-link"
620                       ],  
621     this.addEvents({
622         // raw events
623         /**
624          * @event click
625          * When a butotn is pressed
626          * @param {Roo.bootstrap.Button} btn
627          * @param {Roo.EventObject} e
628          */
629         "click" : true,
630          /**
631          * @event toggle
632          * After the button has been toggles
633          * @param {Roo.bootstrap.Button} btn
634          * @param {Roo.EventObject} e
635          * @param {boolean} pressed (also available as button.pressed)
636          */
637         "toggle" : true
638     });
639 };
640
641 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
642     html: false,
643     active: false,
644     weight: '',
645     badge_weight: '',
646     outline : false,
647     size: '',
648     tag: 'button',
649     href: '',
650     disabled: false,
651     isClose: false,
652     glyphicon: '',
653     fa: '',
654     badge: '',
655     theme: 'default',
656     inverse: false,
657     
658     toggle: false,
659     ontext: 'ON',
660     offtext: 'OFF',
661     defaulton: true,
662     preventDefault: true,
663     removeClass: false,
664     name: false,
665     target: false,
666      
667     pressed : null,
668      
669     
670     getAutoCreate : function(){
671         
672         var cfg = {
673             tag : 'button',
674             cls : 'roo-button',
675             html: ''
676         };
677         
678         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
679             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
680             this.tag = 'button';
681         } else {
682             cfg.tag = this.tag;
683         }
684         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
685         
686         if (this.toggle == true) {
687             cfg={
688                 tag: 'div',
689                 cls: 'slider-frame roo-button',
690                 cn: [
691                     {
692                         tag: 'span',
693                         'data-on-text':'ON',
694                         'data-off-text':'OFF',
695                         cls: 'slider-button',
696                         html: this.offtext
697                     }
698                 ]
699             };
700             
701             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
702                 cfg.cls += ' '+this.weight;
703             }
704             
705             return cfg;
706         }
707         
708         if (this.isClose) {
709             cfg.cls += ' close';
710             
711             cfg["aria-hidden"] = true;
712             
713             cfg.html = "&times;";
714             
715             return cfg;
716         }
717         
718          
719         if (this.theme==='default') {
720             cfg.cls = 'btn roo-button';
721             
722             //if (this.parentType != 'Navbar') {
723             this.weight = this.weight.length ?  this.weight : 'default';
724             //}
725             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
726                 
727                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
728                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
729                 cfg.cls += ' btn-' + outline + weight;
730                 if (this.weight == 'default') {
731                     // BC
732                     cfg.cls += ' btn-' + this.weight;
733                 }
734             }
735         } else if (this.theme==='glow') {
736             
737             cfg.tag = 'a';
738             cfg.cls = 'btn-glow roo-button';
739             
740             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
741                 
742                 cfg.cls += ' ' + this.weight;
743             }
744         }
745    
746         
747         if (this.inverse) {
748             this.cls += ' inverse';
749         }
750         
751         
752         if (this.active || this.pressed === true) {
753             cfg.cls += ' active';
754         }
755         
756         if (this.disabled) {
757             cfg.disabled = 'disabled';
758         }
759         
760         if (this.items) {
761             Roo.log('changing to ul' );
762             cfg.tag = 'ul';
763             this.glyphicon = 'caret';
764             if (Roo.bootstrap.version == 4) {
765                 this.fa = 'caret-down';
766             }
767             
768         }
769         
770         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
771          
772         //gsRoo.log(this.parentType);
773         if (this.parentType === 'Navbar' && !this.parent().bar) {
774             Roo.log('changing to li?');
775             
776             cfg.tag = 'li';
777             
778             cfg.cls = '';
779             cfg.cn =  [{
780                 tag : 'a',
781                 cls : 'roo-button',
782                 html : this.html,
783                 href : this.href || '#'
784             }];
785             if (this.menu) {
786                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
787                 cfg.cls += ' dropdown';
788             }   
789             
790             delete cfg.html;
791             
792         }
793         
794        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
795         
796         if (this.glyphicon) {
797             cfg.html = ' ' + cfg.html;
798             
799             cfg.cn = [
800                 {
801                     tag: 'span',
802                     cls: 'glyphicon glyphicon-' + this.glyphicon
803                 }
804             ];
805         }
806         if (this.fa) {
807             cfg.html = ' ' + cfg.html;
808             
809             cfg.cn = [
810                 {
811                     tag: 'i',
812                     cls: 'fa fas fa-' + this.fa
813                 }
814             ];
815         }
816         
817         if (this.badge) {
818             cfg.html += ' ';
819             
820             cfg.tag = 'a';
821             
822 //            cfg.cls='btn roo-button';
823             
824             cfg.href=this.href;
825             
826             var value = cfg.html;
827             
828             if(this.glyphicon){
829                 value = {
830                     tag: 'span',
831                     cls: 'glyphicon glyphicon-' + this.glyphicon,
832                     html: this.html
833                 };
834             }
835             if(this.fa){
836                 value = {
837                     tag: 'i',
838                     cls: 'fa fas fa-' + this.fa,
839                     html: this.html
840                 };
841             }
842             
843             var bw = this.badge_weight.length ? this.badge_weight :
844                 (this.weight.length ? this.weight : 'secondary');
845             bw = bw == 'default' ? 'secondary' : bw;
846             
847             cfg.cn = [
848                 value,
849                 {
850                     tag: 'span',
851                     cls: 'badge badge-' + bw,
852                     html: this.badge
853                 }
854             ];
855             
856             cfg.html='';
857         }
858         
859         if (this.menu) {
860             cfg.cls += ' dropdown';
861             cfg.html = typeof(cfg.html) != 'undefined' ?
862                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
863         }
864         
865         if (cfg.tag !== 'a' && this.href !== '') {
866             throw "Tag must be a to set href.";
867         } else if (this.href.length > 0) {
868             cfg.href = this.href;
869         }
870         
871         if(this.removeClass){
872             cfg.cls = '';
873         }
874         
875         if(this.target){
876             cfg.target = this.target;
877         }
878         
879         return cfg;
880     },
881     initEvents: function() {
882        // Roo.log('init events?');
883 //        Roo.log(this.el.dom);
884         // add the menu...
885         
886         if (typeof (this.menu) != 'undefined') {
887             this.menu.parentType = this.xtype;
888             this.menu.triggerEl = this.el;
889             this.addxtype(Roo.apply({}, this.menu));
890         }
891
892
893        if (this.el.hasClass('roo-button')) {
894             this.el.on('click', this.onClick, this);
895        } else {
896             this.el.select('.roo-button').on('click', this.onClick, this);
897        }
898        
899        if(this.removeClass){
900            this.el.on('click', this.onClick, this);
901        }
902        
903        this.el.enableDisplayMode();
904         
905     },
906     onClick : function(e)
907     {
908         if (this.disabled) {
909             return;
910         }
911         
912         Roo.log('button on click ');
913         if(this.preventDefault){
914             e.preventDefault();
915         }
916         
917         if (this.pressed === true || this.pressed === false) {
918             this.toggleActive(e);
919         }
920         
921         
922         this.fireEvent('click', this, e);
923     },
924     
925     /**
926      * Enables this button
927      */
928     enable : function()
929     {
930         this.disabled = false;
931         this.el.removeClass('disabled');
932     },
933     
934     /**
935      * Disable this button
936      */
937     disable : function()
938     {
939         this.disabled = true;
940         this.el.addClass('disabled');
941     },
942      /**
943      * sets the active state on/off, 
944      * @param {Boolean} state (optional) Force a particular state
945      */
946     setActive : function(v) {
947         
948         this.el[v ? 'addClass' : 'removeClass']('active');
949         this.pressed = v;
950     },
951      /**
952      * toggles the current active state 
953      */
954     toggleActive : function(e)
955     {
956         this.setActive(!this.pressed);
957         this.fireEvent('toggle', this, e, !this.pressed);
958     },
959      /**
960      * get the current active state
961      * @return {boolean} true if it's active
962      */
963     isActive : function()
964     {
965         return this.el.hasClass('active');
966     },
967     /**
968      * set the text of the first selected button
969      */
970     setText : function(str)
971     {
972         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
973     },
974     /**
975      * get the text of the first selected button
976      */
977     getText : function()
978     {
979         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
980     },
981     
982     setWeight : function(str)
983     {
984         this.el.removeClass(this.weightClass);
985         this.weight = str;
986         var outline = this.outline ? 'outline-' : '';
987         if (str == 'default') {
988             this.el.addClass('btn-default btn-outline-secondary');        
989             return;
990         }
991         this.el.addClass('btn-' + outline + str);        
992     }
993     
994     
995 });
996
997  /*
998  * - LGPL
999  *
1000  * column
1001  * 
1002  */
1003
1004 /**
1005  * @class Roo.bootstrap.Column
1006  * @extends Roo.bootstrap.Component
1007  * Bootstrap Column class
1008  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1009  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1010  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1011  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1012  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1013  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1014  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1015  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1016  *
1017  * 
1018  * @cfg {Boolean} hidden (true|false) hide the element
1019  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1020  * @cfg {String} fa (ban|check|...) font awesome icon
1021  * @cfg {Number} fasize (1|2|....) font awsome size
1022
1023  * @cfg {String} icon (info-sign|check|...) glyphicon name
1024
1025  * @cfg {String} html content of column.
1026  * 
1027  * @constructor
1028  * Create a new Column
1029  * @param {Object} config The config object
1030  */
1031
1032 Roo.bootstrap.Column = function(config){
1033     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1034 };
1035
1036 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1037     
1038     xs: false,
1039     sm: false,
1040     md: false,
1041     lg: false,
1042     xsoff: false,
1043     smoff: false,
1044     mdoff: false,
1045     lgoff: false,
1046     html: '',
1047     offset: 0,
1048     alert: false,
1049     fa: false,
1050     icon : false,
1051     hidden : false,
1052     fasize : 1,
1053     
1054     getAutoCreate : function(){
1055         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1056         
1057         cfg = {
1058             tag: 'div',
1059             cls: 'column'
1060         };
1061         
1062         var settings=this;
1063         ['xs','sm','md','lg'].map(function(size){
1064             //Roo.log( size + ':' + settings[size]);
1065             
1066             if (settings[size+'off'] !== false) {
1067                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1068             }
1069             
1070             if (settings[size] === false) {
1071                 return;
1072             }
1073             
1074             if (!settings[size]) { // 0 = hidden
1075                 cfg.cls += ' hidden-' + size;
1076                 return;
1077             }
1078             cfg.cls += ' col-' + size + '-' + settings[size];
1079             
1080         });
1081         
1082         if (this.hidden) {
1083             cfg.cls += ' hidden';
1084         }
1085         
1086         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1087             cfg.cls +=' alert alert-' + this.alert;
1088         }
1089         
1090         
1091         if (this.html.length) {
1092             cfg.html = this.html;
1093         }
1094         if (this.fa) {
1095             var fasize = '';
1096             if (this.fasize > 1) {
1097                 fasize = ' fa-' + this.fasize + 'x';
1098             }
1099             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1100             
1101             
1102         }
1103         if (this.icon) {
1104             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1105         }
1106         
1107         return cfg;
1108     }
1109    
1110 });
1111
1112  
1113
1114  /*
1115  * - LGPL
1116  *
1117  * page container.
1118  * 
1119  */
1120
1121
1122 /**
1123  * @class Roo.bootstrap.Container
1124  * @extends Roo.bootstrap.Component
1125  * Bootstrap Container class
1126  * @cfg {Boolean} jumbotron is it a jumbotron element
1127  * @cfg {String} html content of element
1128  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1129  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1130  * @cfg {String} header content of header (for panel)
1131  * @cfg {String} footer content of footer (for panel)
1132  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1133  * @cfg {String} tag (header|aside|section) type of HTML tag.
1134  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1135  * @cfg {String} fa font awesome icon
1136  * @cfg {String} icon (info-sign|check|...) glyphicon name
1137  * @cfg {Boolean} hidden (true|false) hide the element
1138  * @cfg {Boolean} expandable (true|false) default false
1139  * @cfg {Boolean} expanded (true|false) default true
1140  * @cfg {String} rheader contet on the right of header
1141  * @cfg {Boolean} clickable (true|false) default false
1142
1143  *     
1144  * @constructor
1145  * Create a new Container
1146  * @param {Object} config The config object
1147  */
1148
1149 Roo.bootstrap.Container = function(config){
1150     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1151     
1152     this.addEvents({
1153         // raw events
1154          /**
1155          * @event expand
1156          * After the panel has been expand
1157          * 
1158          * @param {Roo.bootstrap.Container} this
1159          */
1160         "expand" : true,
1161         /**
1162          * @event collapse
1163          * After the panel has been collapsed
1164          * 
1165          * @param {Roo.bootstrap.Container} this
1166          */
1167         "collapse" : true,
1168         /**
1169          * @event click
1170          * When a element is chick
1171          * @param {Roo.bootstrap.Container} this
1172          * @param {Roo.EventObject} e
1173          */
1174         "click" : true
1175     });
1176 };
1177
1178 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1179     
1180     jumbotron : false,
1181     well: '',
1182     panel : '',
1183     header: '',
1184     footer : '',
1185     sticky: '',
1186     tag : false,
1187     alert : false,
1188     fa: false,
1189     icon : false,
1190     expandable : false,
1191     rheader : '',
1192     expanded : true,
1193     clickable: false,
1194   
1195      
1196     getChildContainer : function() {
1197         
1198         if(!this.el){
1199             return false;
1200         }
1201         
1202         if (this.panel.length) {
1203             return this.el.select('.panel-body',true).first();
1204         }
1205         
1206         return this.el;
1207     },
1208     
1209     
1210     getAutoCreate : function(){
1211         
1212         var cfg = {
1213             tag : this.tag || 'div',
1214             html : '',
1215             cls : ''
1216         };
1217         if (this.jumbotron) {
1218             cfg.cls = 'jumbotron';
1219         }
1220         
1221         
1222         
1223         // - this is applied by the parent..
1224         //if (this.cls) {
1225         //    cfg.cls = this.cls + '';
1226         //}
1227         
1228         if (this.sticky.length) {
1229             
1230             var bd = Roo.get(document.body);
1231             if (!bd.hasClass('bootstrap-sticky')) {
1232                 bd.addClass('bootstrap-sticky');
1233                 Roo.select('html',true).setStyle('height', '100%');
1234             }
1235              
1236             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1237         }
1238         
1239         
1240         if (this.well.length) {
1241             switch (this.well) {
1242                 case 'lg':
1243                 case 'sm':
1244                     cfg.cls +=' well well-' +this.well;
1245                     break;
1246                 default:
1247                     cfg.cls +=' well';
1248                     break;
1249             }
1250         }
1251         
1252         if (this.hidden) {
1253             cfg.cls += ' hidden';
1254         }
1255         
1256         
1257         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1258             cfg.cls +=' alert alert-' + this.alert;
1259         }
1260         
1261         var body = cfg;
1262         
1263         if (this.panel.length) {
1264             cfg.cls += ' panel panel-' + this.panel;
1265             cfg.cn = [];
1266             if (this.header.length) {
1267                 
1268                 var h = [];
1269                 
1270                 if(this.expandable){
1271                     
1272                     cfg.cls = cfg.cls + ' expandable';
1273                     
1274                     h.push({
1275                         tag: 'i',
1276                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1277                     });
1278                     
1279                 }
1280                 
1281                 h.push(
1282                     {
1283                         tag: 'span',
1284                         cls : 'panel-title',
1285                         html : (this.expandable ? '&nbsp;' : '') + this.header
1286                     },
1287                     {
1288                         tag: 'span',
1289                         cls: 'panel-header-right',
1290                         html: this.rheader
1291                     }
1292                 );
1293                 
1294                 cfg.cn.push({
1295                     cls : 'panel-heading',
1296                     style : this.expandable ? 'cursor: pointer' : '',
1297                     cn : h
1298                 });
1299                 
1300             }
1301             
1302             body = false;
1303             cfg.cn.push({
1304                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1305                 html : this.html
1306             });
1307             
1308             
1309             if (this.footer.length) {
1310                 cfg.cn.push({
1311                     cls : 'panel-footer',
1312                     html : this.footer
1313                     
1314                 });
1315             }
1316             
1317         }
1318         
1319         if (body) {
1320             body.html = this.html || cfg.html;
1321             // prefix with the icons..
1322             if (this.fa) {
1323                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1324             }
1325             if (this.icon) {
1326                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1327             }
1328             
1329             
1330         }
1331         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1332             cfg.cls =  'container';
1333         }
1334         
1335         return cfg;
1336     },
1337     
1338     initEvents: function() 
1339     {
1340         if(this.expandable){
1341             var headerEl = this.headerEl();
1342         
1343             if(headerEl){
1344                 headerEl.on('click', this.onToggleClick, this);
1345             }
1346         }
1347         
1348         if(this.clickable){
1349             this.el.on('click', this.onClick, this);
1350         }
1351         
1352     },
1353     
1354     onToggleClick : function()
1355     {
1356         var headerEl = this.headerEl();
1357         
1358         if(!headerEl){
1359             return;
1360         }
1361         
1362         if(this.expanded){
1363             this.collapse();
1364             return;
1365         }
1366         
1367         this.expand();
1368     },
1369     
1370     expand : function()
1371     {
1372         if(this.fireEvent('expand', this)) {
1373             
1374             this.expanded = true;
1375             
1376             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1377             
1378             this.el.select('.panel-body',true).first().removeClass('hide');
1379             
1380             var toggleEl = this.toggleEl();
1381
1382             if(!toggleEl){
1383                 return;
1384             }
1385
1386             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1387         }
1388         
1389     },
1390     
1391     collapse : function()
1392     {
1393         if(this.fireEvent('collapse', this)) {
1394             
1395             this.expanded = false;
1396             
1397             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1398             this.el.select('.panel-body',true).first().addClass('hide');
1399         
1400             var toggleEl = this.toggleEl();
1401
1402             if(!toggleEl){
1403                 return;
1404             }
1405
1406             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1407         }
1408     },
1409     
1410     toggleEl : function()
1411     {
1412         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1413             return;
1414         }
1415         
1416         return this.el.select('.panel-heading .fa',true).first();
1417     },
1418     
1419     headerEl : function()
1420     {
1421         if(!this.el || !this.panel.length || !this.header.length){
1422             return;
1423         }
1424         
1425         return this.el.select('.panel-heading',true).first()
1426     },
1427     
1428     bodyEl : function()
1429     {
1430         if(!this.el || !this.panel.length){
1431             return;
1432         }
1433         
1434         return this.el.select('.panel-body',true).first()
1435     },
1436     
1437     titleEl : function()
1438     {
1439         if(!this.el || !this.panel.length || !this.header.length){
1440             return;
1441         }
1442         
1443         return this.el.select('.panel-title',true).first();
1444     },
1445     
1446     setTitle : function(v)
1447     {
1448         var titleEl = this.titleEl();
1449         
1450         if(!titleEl){
1451             return;
1452         }
1453         
1454         titleEl.dom.innerHTML = v;
1455     },
1456     
1457     getTitle : function()
1458     {
1459         
1460         var titleEl = this.titleEl();
1461         
1462         if(!titleEl){
1463             return '';
1464         }
1465         
1466         return titleEl.dom.innerHTML;
1467     },
1468     
1469     setRightTitle : function(v)
1470     {
1471         var t = this.el.select('.panel-header-right',true).first();
1472         
1473         if(!t){
1474             return;
1475         }
1476         
1477         t.dom.innerHTML = v;
1478     },
1479     
1480     onClick : function(e)
1481     {
1482         e.preventDefault();
1483         
1484         this.fireEvent('click', this, e);
1485     }
1486 });
1487
1488  /*
1489  * - LGPL
1490  *
1491  * image
1492  * 
1493  */
1494
1495
1496 /**
1497  * @class Roo.bootstrap.Img
1498  * @extends Roo.bootstrap.Component
1499  * Bootstrap Img class
1500  * @cfg {Boolean} imgResponsive false | true
1501  * @cfg {String} border rounded | circle | thumbnail
1502  * @cfg {String} src image source
1503  * @cfg {String} alt image alternative text
1504  * @cfg {String} href a tag href
1505  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1506  * @cfg {String} xsUrl xs image source
1507  * @cfg {String} smUrl sm image source
1508  * @cfg {String} mdUrl md image source
1509  * @cfg {String} lgUrl lg image source
1510  * 
1511  * @constructor
1512  * Create a new Input
1513  * @param {Object} config The config object
1514  */
1515
1516 Roo.bootstrap.Img = function(config){
1517     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1518     
1519     this.addEvents({
1520         // img events
1521         /**
1522          * @event click
1523          * The img click event for the img.
1524          * @param {Roo.EventObject} e
1525          */
1526         "click" : true
1527     });
1528 };
1529
1530 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1531     
1532     imgResponsive: true,
1533     border: '',
1534     src: 'about:blank',
1535     href: false,
1536     target: false,
1537     xsUrl: '',
1538     smUrl: '',
1539     mdUrl: '',
1540     lgUrl: '',
1541
1542     getAutoCreate : function()
1543     {   
1544         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1545             return this.createSingleImg();
1546         }
1547         
1548         var cfg = {
1549             tag: 'div',
1550             cls: 'roo-image-responsive-group',
1551             cn: []
1552         };
1553         var _this = this;
1554         
1555         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1556             
1557             if(!_this[size + 'Url']){
1558                 return;
1559             }
1560             
1561             var img = {
1562                 tag: 'img',
1563                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1564                 html: _this.html || cfg.html,
1565                 src: _this[size + 'Url']
1566             };
1567             
1568             img.cls += ' roo-image-responsive-' + size;
1569             
1570             var s = ['xs', 'sm', 'md', 'lg'];
1571             
1572             s.splice(s.indexOf(size), 1);
1573             
1574             Roo.each(s, function(ss){
1575                 img.cls += ' hidden-' + ss;
1576             });
1577             
1578             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1579                 cfg.cls += ' img-' + _this.border;
1580             }
1581             
1582             if(_this.alt){
1583                 cfg.alt = _this.alt;
1584             }
1585             
1586             if(_this.href){
1587                 var a = {
1588                     tag: 'a',
1589                     href: _this.href,
1590                     cn: [
1591                         img
1592                     ]
1593                 };
1594
1595                 if(this.target){
1596                     a.target = _this.target;
1597                 }
1598             }
1599             
1600             cfg.cn.push((_this.href) ? a : img);
1601             
1602         });
1603         
1604         return cfg;
1605     },
1606     
1607     createSingleImg : function()
1608     {
1609         var cfg = {
1610             tag: 'img',
1611             cls: (this.imgResponsive) ? 'img-responsive' : '',
1612             html : null,
1613             src : 'about:blank'  // just incase src get's set to undefined?!?
1614         };
1615         
1616         cfg.html = this.html || cfg.html;
1617         
1618         cfg.src = this.src || cfg.src;
1619         
1620         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1621             cfg.cls += ' img-' + this.border;
1622         }
1623         
1624         if(this.alt){
1625             cfg.alt = this.alt;
1626         }
1627         
1628         if(this.href){
1629             var a = {
1630                 tag: 'a',
1631                 href: this.href,
1632                 cn: [
1633                     cfg
1634                 ]
1635             };
1636             
1637             if(this.target){
1638                 a.target = this.target;
1639             }
1640             
1641         }
1642         
1643         return (this.href) ? a : cfg;
1644     },
1645     
1646     initEvents: function() 
1647     {
1648         if(!this.href){
1649             this.el.on('click', this.onClick, this);
1650         }
1651         
1652     },
1653     
1654     onClick : function(e)
1655     {
1656         Roo.log('img onclick');
1657         this.fireEvent('click', this, e);
1658     },
1659     /**
1660      * Sets the url of the image - used to update it
1661      * @param {String} url the url of the image
1662      */
1663     
1664     setSrc : function(url)
1665     {
1666         this.src =  url;
1667         
1668         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1669             this.el.dom.src =  url;
1670             return;
1671         }
1672         
1673         this.el.select('img', true).first().dom.src =  url;
1674     }
1675     
1676     
1677    
1678 });
1679
1680  /*
1681  * - LGPL
1682  *
1683  * image
1684  * 
1685  */
1686
1687
1688 /**
1689  * @class Roo.bootstrap.Link
1690  * @extends Roo.bootstrap.Component
1691  * Bootstrap Link Class
1692  * @cfg {String} alt image alternative text
1693  * @cfg {String} href a tag href
1694  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1695  * @cfg {String} html the content of the link.
1696  * @cfg {String} anchor name for the anchor link
1697  * @cfg {String} fa - favicon
1698
1699  * @cfg {Boolean} preventDefault (true | false) default false
1700
1701  * 
1702  * @constructor
1703  * Create a new Input
1704  * @param {Object} config The config object
1705  */
1706
1707 Roo.bootstrap.Link = function(config){
1708     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1709     
1710     this.addEvents({
1711         // img events
1712         /**
1713          * @event click
1714          * The img click event for the img.
1715          * @param {Roo.EventObject} e
1716          */
1717         "click" : true
1718     });
1719 };
1720
1721 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1722     
1723     href: false,
1724     target: false,
1725     preventDefault: false,
1726     anchor : false,
1727     alt : false,
1728     fa: false,
1729
1730
1731     getAutoCreate : function()
1732     {
1733         var html = this.html || '';
1734         
1735         if (this.fa !== false) {
1736             html = '<i class="fa fa-' + this.fa + '"></i>';
1737         }
1738         var cfg = {
1739             tag: 'a'
1740         };
1741         // anchor's do not require html/href...
1742         if (this.anchor === false) {
1743             cfg.html = html;
1744             cfg.href = this.href || '#';
1745         } else {
1746             cfg.name = this.anchor;
1747             if (this.html !== false || this.fa !== false) {
1748                 cfg.html = html;
1749             }
1750             if (this.href !== false) {
1751                 cfg.href = this.href;
1752             }
1753         }
1754         
1755         if(this.alt !== false){
1756             cfg.alt = this.alt;
1757         }
1758         
1759         
1760         if(this.target !== false) {
1761             cfg.target = this.target;
1762         }
1763         
1764         return cfg;
1765     },
1766     
1767     initEvents: function() {
1768         
1769         if(!this.href || this.preventDefault){
1770             this.el.on('click', this.onClick, this);
1771         }
1772     },
1773     
1774     onClick : function(e)
1775     {
1776         if(this.preventDefault){
1777             e.preventDefault();
1778         }
1779         //Roo.log('img onclick');
1780         this.fireEvent('click', this, e);
1781     }
1782    
1783 });
1784
1785  /*
1786  * - LGPL
1787  *
1788  * header
1789  * 
1790  */
1791
1792 /**
1793  * @class Roo.bootstrap.Header
1794  * @extends Roo.bootstrap.Component
1795  * Bootstrap Header class
1796  * @cfg {String} html content of header
1797  * @cfg {Number} level (1|2|3|4|5|6) default 1
1798  * 
1799  * @constructor
1800  * Create a new Header
1801  * @param {Object} config The config object
1802  */
1803
1804
1805 Roo.bootstrap.Header  = function(config){
1806     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1807 };
1808
1809 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1810     
1811     //href : false,
1812     html : false,
1813     level : 1,
1814     
1815     
1816     
1817     getAutoCreate : function(){
1818         
1819         
1820         
1821         var cfg = {
1822             tag: 'h' + (1 *this.level),
1823             html: this.html || ''
1824         } ;
1825         
1826         return cfg;
1827     }
1828    
1829 });
1830
1831  
1832
1833  /*
1834  * Based on:
1835  * Ext JS Library 1.1.1
1836  * Copyright(c) 2006-2007, Ext JS, LLC.
1837  *
1838  * Originally Released Under LGPL - original licence link has changed is not relivant.
1839  *
1840  * Fork - LGPL
1841  * <script type="text/javascript">
1842  */
1843  
1844 /**
1845  * @class Roo.bootstrap.MenuMgr
1846  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1847  * @singleton
1848  */
1849 Roo.bootstrap.MenuMgr = function(){
1850    var menus, active, groups = {}, attached = false, lastShow = new Date();
1851
1852    // private - called when first menu is created
1853    function init(){
1854        menus = {};
1855        active = new Roo.util.MixedCollection();
1856        Roo.get(document).addKeyListener(27, function(){
1857            if(active.length > 0){
1858                hideAll();
1859            }
1860        });
1861    }
1862
1863    // private
1864    function hideAll(){
1865        if(active && active.length > 0){
1866            var c = active.clone();
1867            c.each(function(m){
1868                m.hide();
1869            });
1870        }
1871    }
1872
1873    // private
1874    function onHide(m){
1875        active.remove(m);
1876        if(active.length < 1){
1877            Roo.get(document).un("mouseup", onMouseDown);
1878             
1879            attached = false;
1880        }
1881    }
1882
1883    // private
1884    function onShow(m){
1885        var last = active.last();
1886        lastShow = new Date();
1887        active.add(m);
1888        if(!attached){
1889           Roo.get(document).on("mouseup", onMouseDown);
1890            
1891            attached = true;
1892        }
1893        if(m.parentMenu){
1894           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1895           m.parentMenu.activeChild = m;
1896        }else if(last && last.isVisible()){
1897           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1898        }
1899    }
1900
1901    // private
1902    function onBeforeHide(m){
1903        if(m.activeChild){
1904            m.activeChild.hide();
1905        }
1906        if(m.autoHideTimer){
1907            clearTimeout(m.autoHideTimer);
1908            delete m.autoHideTimer;
1909        }
1910    }
1911
1912    // private
1913    function onBeforeShow(m){
1914        var pm = m.parentMenu;
1915        if(!pm && !m.allowOtherMenus){
1916            hideAll();
1917        }else if(pm && pm.activeChild && active != m){
1918            pm.activeChild.hide();
1919        }
1920    }
1921
1922    // private this should really trigger on mouseup..
1923    function onMouseDown(e){
1924         Roo.log("on Mouse Up");
1925         
1926         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1927             Roo.log("MenuManager hideAll");
1928             hideAll();
1929             e.stopEvent();
1930         }
1931         
1932         
1933    }
1934
1935    // private
1936    function onBeforeCheck(mi, state){
1937        if(state){
1938            var g = groups[mi.group];
1939            for(var i = 0, l = g.length; i < l; i++){
1940                if(g[i] != mi){
1941                    g[i].setChecked(false);
1942                }
1943            }
1944        }
1945    }
1946
1947    return {
1948
1949        /**
1950         * Hides all menus that are currently visible
1951         */
1952        hideAll : function(){
1953             hideAll();  
1954        },
1955
1956        // private
1957        register : function(menu){
1958            if(!menus){
1959                init();
1960            }
1961            menus[menu.id] = menu;
1962            menu.on("beforehide", onBeforeHide);
1963            menu.on("hide", onHide);
1964            menu.on("beforeshow", onBeforeShow);
1965            menu.on("show", onShow);
1966            var g = menu.group;
1967            if(g && menu.events["checkchange"]){
1968                if(!groups[g]){
1969                    groups[g] = [];
1970                }
1971                groups[g].push(menu);
1972                menu.on("checkchange", onCheck);
1973            }
1974        },
1975
1976         /**
1977          * Returns a {@link Roo.menu.Menu} object
1978          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1979          * be used to generate and return a new Menu instance.
1980          */
1981        get : function(menu){
1982            if(typeof menu == "string"){ // menu id
1983                return menus[menu];
1984            }else if(menu.events){  // menu instance
1985                return menu;
1986            }
1987            /*else if(typeof menu.length == 'number'){ // array of menu items?
1988                return new Roo.bootstrap.Menu({items:menu});
1989            }else{ // otherwise, must be a config
1990                return new Roo.bootstrap.Menu(menu);
1991            }
1992            */
1993            return false;
1994        },
1995
1996        // private
1997        unregister : function(menu){
1998            delete menus[menu.id];
1999            menu.un("beforehide", onBeforeHide);
2000            menu.un("hide", onHide);
2001            menu.un("beforeshow", onBeforeShow);
2002            menu.un("show", onShow);
2003            var g = menu.group;
2004            if(g && menu.events["checkchange"]){
2005                groups[g].remove(menu);
2006                menu.un("checkchange", onCheck);
2007            }
2008        },
2009
2010        // private
2011        registerCheckable : function(menuItem){
2012            var g = menuItem.group;
2013            if(g){
2014                if(!groups[g]){
2015                    groups[g] = [];
2016                }
2017                groups[g].push(menuItem);
2018                menuItem.on("beforecheckchange", onBeforeCheck);
2019            }
2020        },
2021
2022        // private
2023        unregisterCheckable : function(menuItem){
2024            var g = menuItem.group;
2025            if(g){
2026                groups[g].remove(menuItem);
2027                menuItem.un("beforecheckchange", onBeforeCheck);
2028            }
2029        }
2030    };
2031 }();/*
2032  * - LGPL
2033  *
2034  * menu
2035  * 
2036  */
2037
2038 /**
2039  * @class Roo.bootstrap.Menu
2040  * @extends Roo.bootstrap.Component
2041  * Bootstrap Menu class - container for MenuItems
2042  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2043  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2044  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2045  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2046  * 
2047  * @constructor
2048  * Create a new Menu
2049  * @param {Object} config The config object
2050  */
2051
2052
2053 Roo.bootstrap.Menu = function(config){
2054     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2055     if (this.registerMenu && this.type != 'treeview')  {
2056         Roo.bootstrap.MenuMgr.register(this);
2057     }
2058     
2059     
2060     this.addEvents({
2061         /**
2062          * @event beforeshow
2063          * Fires before this menu is displayed
2064          * @param {Roo.menu.Menu} this
2065          */
2066         beforeshow : true,
2067         /**
2068          * @event beforehide
2069          * Fires before this menu is hidden
2070          * @param {Roo.menu.Menu} this
2071          */
2072         beforehide : true,
2073         /**
2074          * @event show
2075          * Fires after this menu is displayed
2076          * @param {Roo.menu.Menu} this
2077          */
2078         show : true,
2079         /**
2080          * @event hide
2081          * Fires after this menu is hidden
2082          * @param {Roo.menu.Menu} this
2083          */
2084         hide : true,
2085         /**
2086          * @event click
2087          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2088          * @param {Roo.menu.Menu} this
2089          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2090          * @param {Roo.EventObject} e
2091          */
2092         click : true,
2093         /**
2094          * @event mouseover
2095          * Fires when the mouse is hovering over this menu
2096          * @param {Roo.menu.Menu} this
2097          * @param {Roo.EventObject} e
2098          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2099          */
2100         mouseover : true,
2101         /**
2102          * @event mouseout
2103          * Fires when the mouse exits this menu
2104          * @param {Roo.menu.Menu} this
2105          * @param {Roo.EventObject} e
2106          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2107          */
2108         mouseout : true,
2109         /**
2110          * @event itemclick
2111          * Fires when a menu item contained in this menu is clicked
2112          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2113          * @param {Roo.EventObject} e
2114          */
2115         itemclick: true
2116     });
2117     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2118 };
2119
2120 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2121     
2122    /// html : false,
2123     //align : '',
2124     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2125     type: false,
2126     /**
2127      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2128      */
2129     registerMenu : true,
2130     
2131     menuItems :false, // stores the menu items..
2132     
2133     hidden:true,
2134         
2135     parentMenu : false,
2136     
2137     stopEvent : true,
2138     
2139     isLink : false,
2140     
2141     getChildContainer : function() {
2142         return this.el;  
2143     },
2144     
2145     getAutoCreate : function(){
2146          
2147         //if (['right'].indexOf(this.align)!==-1) {
2148         //    cfg.cn[1].cls += ' pull-right'
2149         //}
2150         
2151         
2152         var cfg = {
2153             tag : 'ul',
2154             cls : 'dropdown-menu' ,
2155             style : 'z-index:1000'
2156             
2157         };
2158         
2159         if (this.type === 'submenu') {
2160             cfg.cls = 'submenu active';
2161         }
2162         if (this.type === 'treeview') {
2163             cfg.cls = 'treeview-menu';
2164         }
2165         
2166         return cfg;
2167     },
2168     initEvents : function() {
2169         
2170        // Roo.log("ADD event");
2171        // Roo.log(this.triggerEl.dom);
2172         
2173         this.triggerEl.on('click', this.onTriggerClick, this);
2174         
2175         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2176         
2177         
2178         if (this.triggerEl.hasClass('nav-item')) {
2179             // dropdown toggle on the 'a' in BS4?
2180             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2181         } else {
2182             this.triggerEl.addClass('dropdown-toggle');
2183         }
2184         if (Roo.isTouch) {
2185             this.el.on('touchstart'  , this.onTouch, this);
2186         }
2187         this.el.on('click' , this.onClick, this);
2188
2189         this.el.on("mouseover", this.onMouseOver, this);
2190         this.el.on("mouseout", this.onMouseOut, this);
2191         
2192     },
2193     
2194     findTargetItem : function(e)
2195     {
2196         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2197         if(!t){
2198             return false;
2199         }
2200         //Roo.log(t);         Roo.log(t.id);
2201         if(t && t.id){
2202             //Roo.log(this.menuitems);
2203             return this.menuitems.get(t.id);
2204             
2205             //return this.items.get(t.menuItemId);
2206         }
2207         
2208         return false;
2209     },
2210     
2211     onTouch : function(e) 
2212     {
2213         Roo.log("menu.onTouch");
2214         //e.stopEvent(); this make the user popdown broken
2215         this.onClick(e);
2216     },
2217     
2218     onClick : function(e)
2219     {
2220         Roo.log("menu.onClick");
2221         
2222         var t = this.findTargetItem(e);
2223         if(!t || t.isContainer){
2224             return;
2225         }
2226         Roo.log(e);
2227         /*
2228         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2229             if(t == this.activeItem && t.shouldDeactivate(e)){
2230                 this.activeItem.deactivate();
2231                 delete this.activeItem;
2232                 return;
2233             }
2234             if(t.canActivate){
2235                 this.setActiveItem(t, true);
2236             }
2237             return;
2238             
2239             
2240         }
2241         */
2242        
2243         Roo.log('pass click event');
2244         
2245         t.onClick(e);
2246         
2247         this.fireEvent("click", this, t, e);
2248         
2249         var _this = this;
2250         
2251         if(!t.href.length || t.href == '#'){
2252             (function() { _this.hide(); }).defer(100);
2253         }
2254         
2255     },
2256     
2257     onMouseOver : function(e){
2258         var t  = this.findTargetItem(e);
2259         //Roo.log(t);
2260         //if(t){
2261         //    if(t.canActivate && !t.disabled){
2262         //        this.setActiveItem(t, true);
2263         //    }
2264         //}
2265         
2266         this.fireEvent("mouseover", this, e, t);
2267     },
2268     isVisible : function(){
2269         return !this.hidden;
2270     },
2271      onMouseOut : function(e){
2272         var t  = this.findTargetItem(e);
2273         
2274         //if(t ){
2275         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2276         //        this.activeItem.deactivate();
2277         //        delete this.activeItem;
2278         //    }
2279         //}
2280         this.fireEvent("mouseout", this, e, t);
2281     },
2282     
2283     
2284     /**
2285      * Displays this menu relative to another element
2286      * @param {String/HTMLElement/Roo.Element} element The element to align to
2287      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2288      * the element (defaults to this.defaultAlign)
2289      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2290      */
2291     show : function(el, pos, parentMenu){
2292         this.parentMenu = parentMenu;
2293         if(!this.el){
2294             this.render();
2295         }
2296         this.fireEvent("beforeshow", this);
2297         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2298     },
2299      /**
2300      * Displays this menu at a specific xy position
2301      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2302      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2303      */
2304     showAt : function(xy, parentMenu, /* private: */_e){
2305         this.parentMenu = parentMenu;
2306         if(!this.el){
2307             this.render();
2308         }
2309         if(_e !== false){
2310             this.fireEvent("beforeshow", this);
2311             //xy = this.el.adjustForConstraints(xy);
2312         }
2313         
2314         //this.el.show();
2315         this.hideMenuItems();
2316         this.hidden = false;
2317         this.triggerEl.addClass('open');
2318         this.el.addClass('show');
2319         
2320         // reassign x when hitting right
2321         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2322             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2323         }
2324         
2325         // reassign y when hitting bottom
2326         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2327             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2328         }
2329         
2330         // but the list may align on trigger left or trigger top... should it be a properity?
2331         
2332         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2333             this.el.setXY(xy);
2334         }
2335         
2336         this.focus();
2337         this.fireEvent("show", this);
2338     },
2339     
2340     focus : function(){
2341         return;
2342         if(!this.hidden){
2343             this.doFocus.defer(50, this);
2344         }
2345     },
2346
2347     doFocus : function(){
2348         if(!this.hidden){
2349             this.focusEl.focus();
2350         }
2351     },
2352
2353     /**
2354      * Hides this menu and optionally all parent menus
2355      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2356      */
2357     hide : function(deep)
2358     {
2359         
2360         this.hideMenuItems();
2361         if(this.el && this.isVisible()){
2362             this.fireEvent("beforehide", this);
2363             if(this.activeItem){
2364                 this.activeItem.deactivate();
2365                 this.activeItem = null;
2366             }
2367             this.triggerEl.removeClass('open');;
2368             this.el.removeClass('show');
2369             this.hidden = true;
2370             this.fireEvent("hide", this);
2371         }
2372         if(deep === true && this.parentMenu){
2373             this.parentMenu.hide(true);
2374         }
2375     },
2376     
2377     onTriggerClick : function(e)
2378     {
2379         Roo.log('trigger click');
2380         
2381         var target = e.getTarget();
2382         
2383         Roo.log(target.nodeName.toLowerCase());
2384         
2385         if(target.nodeName.toLowerCase() === 'i'){
2386             e.preventDefault();
2387         }
2388         
2389     },
2390     
2391     onTriggerPress  : function(e)
2392     {
2393         Roo.log('trigger press');
2394         //Roo.log(e.getTarget());
2395        // Roo.log(this.triggerEl.dom);
2396        
2397         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2398         var pel = Roo.get(e.getTarget());
2399         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2400             Roo.log('is treeview or dropdown?');
2401             return;
2402         }
2403         
2404         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2405             return;
2406         }
2407         
2408         if (this.isVisible()) {
2409             Roo.log('hide');
2410             this.hide();
2411         } else {
2412             Roo.log('show');
2413             this.show(this.triggerEl, '?', false);
2414         }
2415         
2416         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2417             e.stopEvent();
2418         }
2419         
2420     },
2421        
2422     
2423     hideMenuItems : function()
2424     {
2425         Roo.log("hide Menu Items");
2426         if (!this.el) { 
2427             return;
2428         }
2429         //$(backdrop).remove()
2430         this.el.select('.open',true).each(function(aa) {
2431             
2432             aa.removeClass('open');
2433           //var parent = getParent($(this))
2434           //var relatedTarget = { relatedTarget: this }
2435           
2436            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2437           //if (e.isDefaultPrevented()) return
2438            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2439         });
2440     },
2441     addxtypeChild : function (tree, cntr) {
2442         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2443           
2444         this.menuitems.add(comp);
2445         return comp;
2446
2447     },
2448     getEl : function()
2449     {
2450         Roo.log(this.el);
2451         return this.el;
2452     },
2453     
2454     clear : function()
2455     {
2456         this.getEl().dom.innerHTML = '';
2457         this.menuitems.clear();
2458     }
2459 });
2460
2461  
2462  /*
2463  * - LGPL
2464  *
2465  * menu item
2466  * 
2467  */
2468
2469
2470 /**
2471  * @class Roo.bootstrap.MenuItem
2472  * @extends Roo.bootstrap.Component
2473  * Bootstrap MenuItem class
2474  * @cfg {String} html the menu label
2475  * @cfg {String} href the link
2476  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2477  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2478  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2479  * @cfg {String} fa favicon to show on left of menu item.
2480  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2481  * 
2482  * 
2483  * @constructor
2484  * Create a new MenuItem
2485  * @param {Object} config The config object
2486  */
2487
2488
2489 Roo.bootstrap.MenuItem = function(config){
2490     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2491     this.addEvents({
2492         // raw events
2493         /**
2494          * @event click
2495          * The raw click event for the entire grid.
2496          * @param {Roo.bootstrap.MenuItem} this
2497          * @param {Roo.EventObject} e
2498          */
2499         "click" : true
2500     });
2501 };
2502
2503 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2504     
2505     href : false,
2506     html : false,
2507     preventDefault: false,
2508     isContainer : false,
2509     active : false,
2510     fa: false,
2511     
2512     getAutoCreate : function(){
2513         
2514         if(this.isContainer){
2515             return {
2516                 tag: 'li',
2517                 cls: 'dropdown-menu-item '
2518             };
2519         }
2520         var ctag = {
2521             tag: 'span',
2522             html: 'Link'
2523         };
2524         
2525         var anc = {
2526             tag : 'a',
2527             cls : 'dropdown-item',
2528             href : '#',
2529             cn : [  ]
2530         };
2531         
2532         if (this.fa !== false) {
2533             anc.cn.push({
2534                 tag : 'i',
2535                 cls : 'fa fa-' + this.fa
2536             });
2537         }
2538         
2539         anc.cn.push(ctag);
2540         
2541         
2542         var cfg= {
2543             tag: 'li',
2544             cls: 'dropdown-menu-item',
2545             cn: [ anc ]
2546         };
2547         if (this.parent().type == 'treeview') {
2548             cfg.cls = 'treeview-menu';
2549         }
2550         if (this.active) {
2551             cfg.cls += ' active';
2552         }
2553         
2554         
2555         
2556         anc.href = this.href || cfg.cn[0].href ;
2557         ctag.html = this.html || cfg.cn[0].html ;
2558         return cfg;
2559     },
2560     
2561     initEvents: function()
2562     {
2563         if (this.parent().type == 'treeview') {
2564             this.el.select('a').on('click', this.onClick, this);
2565         }
2566         
2567         if (this.menu) {
2568             this.menu.parentType = this.xtype;
2569             this.menu.triggerEl = this.el;
2570             this.menu = this.addxtype(Roo.apply({}, this.menu));
2571         }
2572         
2573     },
2574     onClick : function(e)
2575     {
2576         Roo.log('item on click ');
2577         
2578         if(this.preventDefault){
2579             e.preventDefault();
2580         }
2581         //this.parent().hideMenuItems();
2582         
2583         this.fireEvent('click', this, e);
2584     },
2585     getEl : function()
2586     {
2587         return this.el;
2588     } 
2589 });
2590
2591  
2592
2593  /*
2594  * - LGPL
2595  *
2596  * menu separator
2597  * 
2598  */
2599
2600
2601 /**
2602  * @class Roo.bootstrap.MenuSeparator
2603  * @extends Roo.bootstrap.Component
2604  * Bootstrap MenuSeparator class
2605  * 
2606  * @constructor
2607  * Create a new MenuItem
2608  * @param {Object} config The config object
2609  */
2610
2611
2612 Roo.bootstrap.MenuSeparator = function(config){
2613     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2614 };
2615
2616 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2617     
2618     getAutoCreate : function(){
2619         var cfg = {
2620             cls: 'divider',
2621             tag : 'li'
2622         };
2623         
2624         return cfg;
2625     }
2626    
2627 });
2628
2629  
2630
2631  
2632 /*
2633 * Licence: LGPL
2634 */
2635
2636 /**
2637  * @class Roo.bootstrap.Modal
2638  * @extends Roo.bootstrap.Component
2639  * Bootstrap Modal class
2640  * @cfg {String} title Title of dialog
2641  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2642  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2643  * @cfg {Boolean} specificTitle default false
2644  * @cfg {Array} buttons Array of buttons or standard button set..
2645  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2646  * @cfg {Boolean} animate default true
2647  * @cfg {Boolean} allow_close default true
2648  * @cfg {Boolean} fitwindow default false
2649  * @cfg {String} size (sm|lg) default empty
2650  * @cfg {Number} max_width set the max width of modal
2651  *
2652  *
2653  * @constructor
2654  * Create a new Modal Dialog
2655  * @param {Object} config The config object
2656  */
2657
2658 Roo.bootstrap.Modal = function(config){
2659     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2660     this.addEvents({
2661         // raw events
2662         /**
2663          * @event btnclick
2664          * The raw btnclick event for the button
2665          * @param {Roo.EventObject} e
2666          */
2667         "btnclick" : true,
2668         /**
2669          * @event resize
2670          * Fire when dialog resize
2671          * @param {Roo.bootstrap.Modal} this
2672          * @param {Roo.EventObject} e
2673          */
2674         "resize" : true
2675     });
2676     this.buttons = this.buttons || [];
2677
2678     if (this.tmpl) {
2679         this.tmpl = Roo.factory(this.tmpl);
2680     }
2681
2682 };
2683
2684 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2685
2686     title : 'test dialog',
2687
2688     buttons : false,
2689
2690     // set on load...
2691
2692     html: false,
2693
2694     tmp: false,
2695
2696     specificTitle: false,
2697
2698     buttonPosition: 'right',
2699
2700     allow_close : true,
2701
2702     animate : true,
2703
2704     fitwindow: false,
2705     
2706      // private
2707     dialogEl: false,
2708     bodyEl:  false,
2709     footerEl:  false,
2710     titleEl:  false,
2711     closeEl:  false,
2712
2713     size: '',
2714     
2715     max_width: 0,
2716     
2717     max_height: 0,
2718     
2719     fit_content: false,
2720
2721     onRender : function(ct, position)
2722     {
2723         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2724
2725         if(!this.el){
2726             var cfg = Roo.apply({},  this.getAutoCreate());
2727             cfg.id = Roo.id();
2728             //if(!cfg.name){
2729             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2730             //}
2731             //if (!cfg.name.length) {
2732             //    delete cfg.name;
2733            // }
2734             if (this.cls) {
2735                 cfg.cls += ' ' + this.cls;
2736             }
2737             if (this.style) {
2738                 cfg.style = this.style;
2739             }
2740             this.el = Roo.get(document.body).createChild(cfg, position);
2741         }
2742         //var type = this.el.dom.type;
2743
2744
2745         if(this.tabIndex !== undefined){
2746             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2747         }
2748
2749         this.dialogEl = this.el.select('.modal-dialog',true).first();
2750         this.bodyEl = this.el.select('.modal-body',true).first();
2751         this.closeEl = this.el.select('.modal-header .close', true).first();
2752         this.headerEl = this.el.select('.modal-header',true).first();
2753         this.titleEl = this.el.select('.modal-title',true).first();
2754         this.footerEl = this.el.select('.modal-footer',true).first();
2755
2756         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2757         
2758         //this.el.addClass("x-dlg-modal");
2759
2760         if (this.buttons.length) {
2761             Roo.each(this.buttons, function(bb) {
2762                 var b = Roo.apply({}, bb);
2763                 b.xns = b.xns || Roo.bootstrap;
2764                 b.xtype = b.xtype || 'Button';
2765                 if (typeof(b.listeners) == 'undefined') {
2766                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2767                 }
2768
2769                 var btn = Roo.factory(b);
2770
2771                 btn.render(this.getButtonContainer());
2772
2773             },this);
2774         }
2775         // render the children.
2776         var nitems = [];
2777
2778         if(typeof(this.items) != 'undefined'){
2779             var items = this.items;
2780             delete this.items;
2781
2782             for(var i =0;i < items.length;i++) {
2783                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2784             }
2785         }
2786
2787         this.items = nitems;
2788
2789         // where are these used - they used to be body/close/footer
2790
2791
2792         this.initEvents();
2793         //this.el.addClass([this.fieldClass, this.cls]);
2794
2795     },
2796
2797     getAutoCreate : function()
2798     {
2799         var bdy = {
2800                 cls : 'modal-body',
2801                 html : this.html || ''
2802         };
2803
2804         var title = {
2805             tag: 'h4',
2806             cls : 'modal-title',
2807             html : this.title
2808         };
2809
2810         if(this.specificTitle){
2811             title = this.title;
2812
2813         };
2814
2815         var header = [];
2816         if (this.allow_close && Roo.bootstrap.version == 3) {
2817             header.push({
2818                 tag: 'button',
2819                 cls : 'close',
2820                 html : '&times'
2821             });
2822         }
2823
2824         header.push(title);
2825
2826         if (this.allow_close && Roo.bootstrap.version == 4) {
2827             header.push({
2828                 tag: 'button',
2829                 cls : 'close',
2830                 html : '&times'
2831             });
2832         }
2833         
2834         var size = '';
2835
2836         if(this.size.length){
2837             size = 'modal-' + this.size;
2838         }
2839         
2840         var footer = Roo.bootstrap.version == 3 ?
2841             {
2842                 cls : 'modal-footer',
2843                 cn : [
2844                     {
2845                         tag: 'div',
2846                         cls: 'btn-' + this.buttonPosition
2847                     }
2848                 ]
2849
2850             } :
2851             {  // BS4 uses mr-auto on left buttons....
2852                 cls : 'modal-footer'
2853             };
2854
2855             
2856
2857         
2858         
2859         var modal = {
2860             cls: "modal",
2861              cn : [
2862                 {
2863                     cls: "modal-dialog " + size,
2864                     cn : [
2865                         {
2866                             cls : "modal-content",
2867                             cn : [
2868                                 {
2869                                     cls : 'modal-header',
2870                                     cn : header
2871                                 },
2872                                 bdy,
2873                                 footer
2874                             ]
2875
2876                         }
2877                     ]
2878
2879                 }
2880             ]
2881         };
2882
2883         if(this.animate){
2884             modal.cls += ' fade';
2885         }
2886
2887         return modal;
2888
2889     },
2890     getChildContainer : function() {
2891
2892          return this.bodyEl;
2893
2894     },
2895     getButtonContainer : function() {
2896         
2897          return Roo.bootstrap.version == 4 ?
2898             this.el.select('.modal-footer',true).first()
2899             : this.el.select('.modal-footer div',true).first();
2900
2901     },
2902     initEvents : function()
2903     {
2904         if (this.allow_close) {
2905             this.closeEl.on('click', this.hide, this);
2906         }
2907         Roo.EventManager.onWindowResize(this.resize, this, true);
2908
2909
2910     },
2911
2912     resize : function()
2913     {
2914         this.maskEl.setSize(
2915             Roo.lib.Dom.getViewWidth(true),
2916             Roo.lib.Dom.getViewHeight(true)
2917         );
2918         
2919         if (this.fitwindow) {
2920             this.setSize(
2921                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2922                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2923             );
2924             return;
2925         }
2926         
2927         if(this.max_width !== 0) {
2928             
2929             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2930             
2931             if(this.height) {
2932                 this.setSize(w, this.height);
2933                 return;
2934             }
2935             
2936             if(this.max_height) {
2937                 this.setSize(w,Math.min(
2938                     this.max_height,
2939                     Roo.lib.Dom.getViewportHeight(true) - 60
2940                 ));
2941                 
2942                 return;
2943             }
2944             
2945             if(!this.fit_content) {
2946                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2947                 return;
2948             }
2949             
2950             this.setSize(w, Math.min(
2951                 60 +
2952                 this.headerEl.getHeight() + 
2953                 this.footerEl.getHeight() + 
2954                 this.getChildHeight(this.bodyEl.dom.childNodes),
2955                 Roo.lib.Dom.getViewportHeight(true) - 60)
2956             );
2957         }
2958         
2959     },
2960
2961     setSize : function(w,h)
2962     {
2963         if (!w && !h) {
2964             return;
2965         }
2966         
2967         this.resizeTo(w,h);
2968     },
2969
2970     show : function() {
2971
2972         if (!this.rendered) {
2973             this.render();
2974         }
2975
2976         //this.el.setStyle('display', 'block');
2977         this.el.removeClass('hideing');
2978         this.el.dom.style.display='block';
2979         
2980         Roo.get(document.body).addClass('modal-open');
2981  
2982         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2983             var _this = this;
2984             (function(){
2985                 this.el.addClass('show');
2986                 this.el.addClass('in');
2987             }).defer(50, this);
2988         }else{
2989             this.el.addClass('show');
2990             this.el.addClass('in');
2991         }
2992
2993         // not sure how we can show data in here..
2994         //if (this.tmpl) {
2995         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2996         //}
2997
2998         Roo.get(document.body).addClass("x-body-masked");
2999         
3000         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3001         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3002         this.maskEl.dom.style.display = 'block';
3003         this.maskEl.addClass('show');
3004         
3005         
3006         this.resize();
3007         
3008         this.fireEvent('show', this);
3009
3010         // set zindex here - otherwise it appears to be ignored...
3011         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3012
3013         (function () {
3014             this.items.forEach( function(e) {
3015                 e.layout ? e.layout() : false;
3016
3017             });
3018         }).defer(100,this);
3019
3020     },
3021     hide : function()
3022     {
3023         if(this.fireEvent("beforehide", this) !== false){
3024             
3025             this.maskEl.removeClass('show');
3026             
3027             this.maskEl.dom.style.display = '';
3028             Roo.get(document.body).removeClass("x-body-masked");
3029             this.el.removeClass('in');
3030             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3031
3032             if(this.animate){ // why
3033                 this.el.addClass('hideing');
3034                 this.el.removeClass('show');
3035                 (function(){
3036                     if (!this.el.hasClass('hideing')) {
3037                         return; // it's been shown again...
3038                     }
3039                     
3040                     this.el.dom.style.display='';
3041
3042                     Roo.get(document.body).removeClass('modal-open');
3043                     this.el.removeClass('hideing');
3044                 }).defer(150,this);
3045                 
3046             }else{
3047                 this.el.removeClass('show');
3048                 this.el.dom.style.display='';
3049                 Roo.get(document.body).removeClass('modal-open');
3050
3051             }
3052             this.fireEvent('hide', this);
3053         }
3054     },
3055     isVisible : function()
3056     {
3057         
3058         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3059         
3060     },
3061
3062     addButton : function(str, cb)
3063     {
3064
3065
3066         var b = Roo.apply({}, { html : str } );
3067         b.xns = b.xns || Roo.bootstrap;
3068         b.xtype = b.xtype || 'Button';
3069         if (typeof(b.listeners) == 'undefined') {
3070             b.listeners = { click : cb.createDelegate(this)  };
3071         }
3072
3073         var btn = Roo.factory(b);
3074
3075         btn.render(this.getButtonContainer());
3076
3077         return btn;
3078
3079     },
3080
3081     setDefaultButton : function(btn)
3082     {
3083         //this.el.select('.modal-footer').()
3084     },
3085     diff : false,
3086
3087     resizeTo: function(w,h)
3088     {
3089         // skip.. ?? why??
3090
3091         this.dialogEl.setWidth(w);
3092         if (this.diff === false) {
3093             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3094         }
3095
3096         this.bodyEl.setHeight(h - this.diff);
3097
3098         this.fireEvent('resize', this);
3099
3100     },
3101     setContentSize  : function(w, h)
3102     {
3103
3104     },
3105     onButtonClick: function(btn,e)
3106     {
3107         //Roo.log([a,b,c]);
3108         this.fireEvent('btnclick', btn.name, e);
3109     },
3110      /**
3111      * Set the title of the Dialog
3112      * @param {String} str new Title
3113      */
3114     setTitle: function(str) {
3115         this.titleEl.dom.innerHTML = str;
3116     },
3117     /**
3118      * Set the body of the Dialog
3119      * @param {String} str new Title
3120      */
3121     setBody: function(str) {
3122         this.bodyEl.dom.innerHTML = str;
3123     },
3124     /**
3125      * Set the body of the Dialog using the template
3126      * @param {Obj} data - apply this data to the template and replace the body contents.
3127      */
3128     applyBody: function(obj)
3129     {
3130         if (!this.tmpl) {
3131             Roo.log("Error - using apply Body without a template");
3132             //code
3133         }
3134         this.tmpl.overwrite(this.bodyEl, obj);
3135     },
3136     
3137     getChildHeight : function(child_nodes)
3138     {
3139         if(
3140             !child_nodes ||
3141             child_nodes.length == 0
3142         ) {
3143             return;
3144         }
3145         
3146         var child_height = 0;
3147         
3148         for(var i = 0; i < child_nodes.length; i++) {
3149             
3150             /*
3151             * for modal with tabs...
3152             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3153                 
3154                 var layout_childs = child_nodes[i].childNodes;
3155                 
3156                 for(var j = 0; j < layout_childs.length; j++) {
3157                     
3158                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3159                         
3160                         var layout_body_childs = layout_childs[j].childNodes;
3161                         
3162                         for(var k = 0; k < layout_body_childs.length; k++) {
3163                             
3164                             if(layout_body_childs[k].classList.contains('navbar')) {
3165                                 child_height += layout_body_childs[k].offsetHeight;
3166                                 continue;
3167                             }
3168                             
3169                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3170                                 
3171                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3172                                 
3173                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3174                                     
3175                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3176                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3177                                         continue;
3178                                     }
3179                                     
3180                                 }
3181                                 
3182                             }
3183                             
3184                         }
3185                     }
3186                 }
3187                 continue;
3188             }
3189             */
3190             
3191             child_height += child_nodes[i].offsetHeight;
3192             // Roo.log(child_nodes[i].offsetHeight);
3193         }
3194         
3195         return child_height;
3196     }
3197
3198 });
3199
3200
3201 Roo.apply(Roo.bootstrap.Modal,  {
3202     /**
3203          * Button config that displays a single OK button
3204          * @type Object
3205          */
3206         OK :  [{
3207             name : 'ok',
3208             weight : 'primary',
3209             html : 'OK'
3210         }],
3211         /**
3212          * Button config that displays Yes and No buttons
3213          * @type Object
3214          */
3215         YESNO : [
3216             {
3217                 name  : 'no',
3218                 html : 'No'
3219             },
3220             {
3221                 name  :'yes',
3222                 weight : 'primary',
3223                 html : 'Yes'
3224             }
3225         ],
3226
3227         /**
3228          * Button config that displays OK and Cancel buttons
3229          * @type Object
3230          */
3231         OKCANCEL : [
3232             {
3233                name : 'cancel',
3234                 html : 'Cancel'
3235             },
3236             {
3237                 name : 'ok',
3238                 weight : 'primary',
3239                 html : 'OK'
3240             }
3241         ],
3242         /**
3243          * Button config that displays Yes, No and Cancel buttons
3244          * @type Object
3245          */
3246         YESNOCANCEL : [
3247             {
3248                 name : 'yes',
3249                 weight : 'primary',
3250                 html : 'Yes'
3251             },
3252             {
3253                 name : 'no',
3254                 html : 'No'
3255             },
3256             {
3257                 name : 'cancel',
3258                 html : 'Cancel'
3259             }
3260         ],
3261         
3262         zIndex : 10001
3263 });
3264 /*
3265  * - LGPL
3266  *
3267  * messagebox - can be used as a replace
3268  * 
3269  */
3270 /**
3271  * @class Roo.MessageBox
3272  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3273  * Example usage:
3274  *<pre><code>
3275 // Basic alert:
3276 Roo.Msg.alert('Status', 'Changes saved successfully.');
3277
3278 // Prompt for user data:
3279 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3280     if (btn == 'ok'){
3281         // process text value...
3282     }
3283 });
3284
3285 // Show a dialog using config options:
3286 Roo.Msg.show({
3287    title:'Save Changes?',
3288    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3289    buttons: Roo.Msg.YESNOCANCEL,
3290    fn: processResult,
3291    animEl: 'elId'
3292 });
3293 </code></pre>
3294  * @singleton
3295  */
3296 Roo.bootstrap.MessageBox = function(){
3297     var dlg, opt, mask, waitTimer;
3298     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3299     var buttons, activeTextEl, bwidth;
3300
3301     
3302     // private
3303     var handleButton = function(button){
3304         dlg.hide();
3305         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3306     };
3307
3308     // private
3309     var handleHide = function(){
3310         if(opt && opt.cls){
3311             dlg.el.removeClass(opt.cls);
3312         }
3313         //if(waitTimer){
3314         //    Roo.TaskMgr.stop(waitTimer);
3315         //    waitTimer = null;
3316         //}
3317     };
3318
3319     // private
3320     var updateButtons = function(b){
3321         var width = 0;
3322         if(!b){
3323             buttons["ok"].hide();
3324             buttons["cancel"].hide();
3325             buttons["yes"].hide();
3326             buttons["no"].hide();
3327             dlg.footerEl.hide();
3328             
3329             return width;
3330         }
3331         dlg.footerEl.show();
3332         for(var k in buttons){
3333             if(typeof buttons[k] != "function"){
3334                 if(b[k]){
3335                     buttons[k].show();
3336                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3337                     width += buttons[k].el.getWidth()+15;
3338                 }else{
3339                     buttons[k].hide();
3340                 }
3341             }
3342         }
3343         return width;
3344     };
3345
3346     // private
3347     var handleEsc = function(d, k, e){
3348         if(opt && opt.closable !== false){
3349             dlg.hide();
3350         }
3351         if(e){
3352             e.stopEvent();
3353         }
3354     };
3355
3356     return {
3357         /**
3358          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3359          * @return {Roo.BasicDialog} The BasicDialog element
3360          */
3361         getDialog : function(){
3362            if(!dlg){
3363                 dlg = new Roo.bootstrap.Modal( {
3364                     //draggable: true,
3365                     //resizable:false,
3366                     //constraintoviewport:false,
3367                     //fixedcenter:true,
3368                     //collapsible : false,
3369                     //shim:true,
3370                     //modal: true,
3371                 //    width: 'auto',
3372                   //  height:100,
3373                     //buttonAlign:"center",
3374                     closeClick : function(){
3375                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3376                             handleButton("no");
3377                         }else{
3378                             handleButton("cancel");
3379                         }
3380                     }
3381                 });
3382                 dlg.render();
3383                 dlg.on("hide", handleHide);
3384                 mask = dlg.mask;
3385                 //dlg.addKeyListener(27, handleEsc);
3386                 buttons = {};
3387                 this.buttons = buttons;
3388                 var bt = this.buttonText;
3389                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3390                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3391                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3392                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3393                 //Roo.log(buttons);
3394                 bodyEl = dlg.bodyEl.createChild({
3395
3396                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3397                         '<textarea class="roo-mb-textarea"></textarea>' +
3398                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3399                 });
3400                 msgEl = bodyEl.dom.firstChild;
3401                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3402                 textboxEl.enableDisplayMode();
3403                 textboxEl.addKeyListener([10,13], function(){
3404                     if(dlg.isVisible() && opt && opt.buttons){
3405                         if(opt.buttons.ok){
3406                             handleButton("ok");
3407                         }else if(opt.buttons.yes){
3408                             handleButton("yes");
3409                         }
3410                     }
3411                 });
3412                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3413                 textareaEl.enableDisplayMode();
3414                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3415                 progressEl.enableDisplayMode();
3416                 
3417                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3418                 var pf = progressEl.dom.firstChild;
3419                 if (pf) {
3420                     pp = Roo.get(pf.firstChild);
3421                     pp.setHeight(pf.offsetHeight);
3422                 }
3423                 
3424             }
3425             return dlg;
3426         },
3427
3428         /**
3429          * Updates the message box body text
3430          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3431          * the XHTML-compliant non-breaking space character '&amp;#160;')
3432          * @return {Roo.MessageBox} This message box
3433          */
3434         updateText : function(text)
3435         {
3436             if(!dlg.isVisible() && !opt.width){
3437                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3438                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3439             }
3440             msgEl.innerHTML = text || '&#160;';
3441       
3442             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3443             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3444             var w = Math.max(
3445                     Math.min(opt.width || cw , this.maxWidth), 
3446                     Math.max(opt.minWidth || this.minWidth, bwidth)
3447             );
3448             if(opt.prompt){
3449                 activeTextEl.setWidth(w);
3450             }
3451             if(dlg.isVisible()){
3452                 dlg.fixedcenter = false;
3453             }
3454             // to big, make it scroll. = But as usual stupid IE does not support
3455             // !important..
3456             
3457             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3458                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3459                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3460             } else {
3461                 bodyEl.dom.style.height = '';
3462                 bodyEl.dom.style.overflowY = '';
3463             }
3464             if (cw > w) {
3465                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3466             } else {
3467                 bodyEl.dom.style.overflowX = '';
3468             }
3469             
3470             dlg.setContentSize(w, bodyEl.getHeight());
3471             if(dlg.isVisible()){
3472                 dlg.fixedcenter = true;
3473             }
3474             return this;
3475         },
3476
3477         /**
3478          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3479          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3480          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3481          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3482          * @return {Roo.MessageBox} This message box
3483          */
3484         updateProgress : function(value, text){
3485             if(text){
3486                 this.updateText(text);
3487             }
3488             
3489             if (pp) { // weird bug on my firefox - for some reason this is not defined
3490                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3491                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3492             }
3493             return this;
3494         },        
3495
3496         /**
3497          * Returns true if the message box is currently displayed
3498          * @return {Boolean} True if the message box is visible, else false
3499          */
3500         isVisible : function(){
3501             return dlg && dlg.isVisible();  
3502         },
3503
3504         /**
3505          * Hides the message box if it is displayed
3506          */
3507         hide : function(){
3508             if(this.isVisible()){
3509                 dlg.hide();
3510             }  
3511         },
3512
3513         /**
3514          * Displays a new message box, or reinitializes an existing message box, based on the config options
3515          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3516          * The following config object properties are supported:
3517          * <pre>
3518 Property    Type             Description
3519 ----------  ---------------  ------------------------------------------------------------------------------------
3520 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3521                                    closes (defaults to undefined)
3522 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3523                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3524 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3525                                    progress and wait dialogs will ignore this property and always hide the
3526                                    close button as they can only be closed programmatically.
3527 cls               String           A custom CSS class to apply to the message box element
3528 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3529                                    displayed (defaults to 75)
3530 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3531                                    function will be btn (the name of the button that was clicked, if applicable,
3532                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3533                                    Progress and wait dialogs will ignore this option since they do not respond to
3534                                    user actions and can only be closed programmatically, so any required function
3535                                    should be called by the same code after it closes the dialog.
3536 icon              String           A CSS class that provides a background image to be used as an icon for
3537                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3538 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3539 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3540 modal             Boolean          False to allow user interaction with the page while the message box is
3541                                    displayed (defaults to true)
3542 msg               String           A string that will replace the existing message box body text (defaults
3543                                    to the XHTML-compliant non-breaking space character '&#160;')
3544 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3545 progress          Boolean          True to display a progress bar (defaults to false)
3546 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3547 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3548 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3549 title             String           The title text
3550 value             String           The string value to set into the active textbox element if displayed
3551 wait              Boolean          True to display a progress bar (defaults to false)
3552 width             Number           The width of the dialog in pixels
3553 </pre>
3554          *
3555          * Example usage:
3556          * <pre><code>
3557 Roo.Msg.show({
3558    title: 'Address',
3559    msg: 'Please enter your address:',
3560    width: 300,
3561    buttons: Roo.MessageBox.OKCANCEL,
3562    multiline: true,
3563    fn: saveAddress,
3564    animEl: 'addAddressBtn'
3565 });
3566 </code></pre>
3567          * @param {Object} config Configuration options
3568          * @return {Roo.MessageBox} This message box
3569          */
3570         show : function(options)
3571         {
3572             
3573             // this causes nightmares if you show one dialog after another
3574             // especially on callbacks..
3575              
3576             if(this.isVisible()){
3577                 
3578                 this.hide();
3579                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3580                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3581                 Roo.log("New Dialog Message:" +  options.msg )
3582                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3583                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3584                 
3585             }
3586             var d = this.getDialog();
3587             opt = options;
3588             d.setTitle(opt.title || "&#160;");
3589             d.closeEl.setDisplayed(opt.closable !== false);
3590             activeTextEl = textboxEl;
3591             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3592             if(opt.prompt){
3593                 if(opt.multiline){
3594                     textboxEl.hide();
3595                     textareaEl.show();
3596                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3597                         opt.multiline : this.defaultTextHeight);
3598                     activeTextEl = textareaEl;
3599                 }else{
3600                     textboxEl.show();
3601                     textareaEl.hide();
3602                 }
3603             }else{
3604                 textboxEl.hide();
3605                 textareaEl.hide();
3606             }
3607             progressEl.setDisplayed(opt.progress === true);
3608             if (opt.progress) {
3609                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3610             }
3611             this.updateProgress(0);
3612             activeTextEl.dom.value = opt.value || "";
3613             if(opt.prompt){
3614                 dlg.setDefaultButton(activeTextEl);
3615             }else{
3616                 var bs = opt.buttons;
3617                 var db = null;
3618                 if(bs && bs.ok){
3619                     db = buttons["ok"];
3620                 }else if(bs && bs.yes){
3621                     db = buttons["yes"];
3622                 }
3623                 dlg.setDefaultButton(db);
3624             }
3625             bwidth = updateButtons(opt.buttons);
3626             this.updateText(opt.msg);
3627             if(opt.cls){
3628                 d.el.addClass(opt.cls);
3629             }
3630             d.proxyDrag = opt.proxyDrag === true;
3631             d.modal = opt.modal !== false;
3632             d.mask = opt.modal !== false ? mask : false;
3633             if(!d.isVisible()){
3634                 // force it to the end of the z-index stack so it gets a cursor in FF
3635                 document.body.appendChild(dlg.el.dom);
3636                 d.animateTarget = null;
3637                 d.show(options.animEl);
3638             }
3639             return this;
3640         },
3641
3642         /**
3643          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3644          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3645          * and closing the message box when the process is complete.
3646          * @param {String} title The title bar text
3647          * @param {String} msg The message box body text
3648          * @return {Roo.MessageBox} This message box
3649          */
3650         progress : function(title, msg){
3651             this.show({
3652                 title : title,
3653                 msg : msg,
3654                 buttons: false,
3655                 progress:true,
3656                 closable:false,
3657                 minWidth: this.minProgressWidth,
3658                 modal : true
3659             });
3660             return this;
3661         },
3662
3663         /**
3664          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3665          * If a callback function is passed it will be called after the user clicks the button, and the
3666          * id of the button that was clicked will be passed as the only parameter to the callback
3667          * (could also be the top-right close button).
3668          * @param {String} title The title bar text
3669          * @param {String} msg The message box body text
3670          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3671          * @param {Object} scope (optional) The scope of the callback function
3672          * @return {Roo.MessageBox} This message box
3673          */
3674         alert : function(title, msg, fn, scope)
3675         {
3676             this.show({
3677                 title : title,
3678                 msg : msg,
3679                 buttons: this.OK,
3680                 fn: fn,
3681                 closable : false,
3682                 scope : scope,
3683                 modal : true
3684             });
3685             return this;
3686         },
3687
3688         /**
3689          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3690          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3691          * You are responsible for closing the message box when the process is complete.
3692          * @param {String} msg The message box body text
3693          * @param {String} title (optional) The title bar text
3694          * @return {Roo.MessageBox} This message box
3695          */
3696         wait : function(msg, title){
3697             this.show({
3698                 title : title,
3699                 msg : msg,
3700                 buttons: false,
3701                 closable:false,
3702                 progress:true,
3703                 modal:true,
3704                 width:300,
3705                 wait:true
3706             });
3707             waitTimer = Roo.TaskMgr.start({
3708                 run: function(i){
3709                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3710                 },
3711                 interval: 1000
3712             });
3713             return this;
3714         },
3715
3716         /**
3717          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3718          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3719          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3720          * @param {String} title The title bar text
3721          * @param {String} msg The message box body text
3722          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3723          * @param {Object} scope (optional) The scope of the callback function
3724          * @return {Roo.MessageBox} This message box
3725          */
3726         confirm : function(title, msg, fn, scope){
3727             this.show({
3728                 title : title,
3729                 msg : msg,
3730                 buttons: this.YESNO,
3731                 fn: fn,
3732                 scope : scope,
3733                 modal : true
3734             });
3735             return this;
3736         },
3737
3738         /**
3739          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3740          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3741          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3742          * (could also be the top-right close button) and the text that was entered will be passed as the two
3743          * parameters to the callback.
3744          * @param {String} title The title bar text
3745          * @param {String} msg The message box body text
3746          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3747          * @param {Object} scope (optional) The scope of the callback function
3748          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3749          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3750          * @return {Roo.MessageBox} This message box
3751          */
3752         prompt : function(title, msg, fn, scope, multiline){
3753             this.show({
3754                 title : title,
3755                 msg : msg,
3756                 buttons: this.OKCANCEL,
3757                 fn: fn,
3758                 minWidth:250,
3759                 scope : scope,
3760                 prompt:true,
3761                 multiline: multiline,
3762                 modal : true
3763             });
3764             return this;
3765         },
3766
3767         /**
3768          * Button config that displays a single OK button
3769          * @type Object
3770          */
3771         OK : {ok:true},
3772         /**
3773          * Button config that displays Yes and No buttons
3774          * @type Object
3775          */
3776         YESNO : {yes:true, no:true},
3777         /**
3778          * Button config that displays OK and Cancel buttons
3779          * @type Object
3780          */
3781         OKCANCEL : {ok:true, cancel:true},
3782         /**
3783          * Button config that displays Yes, No and Cancel buttons
3784          * @type Object
3785          */
3786         YESNOCANCEL : {yes:true, no:true, cancel:true},
3787
3788         /**
3789          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3790          * @type Number
3791          */
3792         defaultTextHeight : 75,
3793         /**
3794          * The maximum width in pixels of the message box (defaults to 600)
3795          * @type Number
3796          */
3797         maxWidth : 600,
3798         /**
3799          * The minimum width in pixels of the message box (defaults to 100)
3800          * @type Number
3801          */
3802         minWidth : 100,
3803         /**
3804          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3805          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3806          * @type Number
3807          */
3808         minProgressWidth : 250,
3809         /**
3810          * An object containing the default button text strings that can be overriden for localized language support.
3811          * Supported properties are: ok, cancel, yes and no.
3812          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3813          * @type Object
3814          */
3815         buttonText : {
3816             ok : "OK",
3817             cancel : "Cancel",
3818             yes : "Yes",
3819             no : "No"
3820         }
3821     };
3822 }();
3823
3824 /**
3825  * Shorthand for {@link Roo.MessageBox}
3826  */
3827 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3828 Roo.Msg = Roo.Msg || Roo.MessageBox;
3829 /*
3830  * - LGPL
3831  *
3832  * navbar
3833  * 
3834  */
3835
3836 /**
3837  * @class Roo.bootstrap.Navbar
3838  * @extends Roo.bootstrap.Component
3839  * Bootstrap Navbar class
3840
3841  * @constructor
3842  * Create a new Navbar
3843  * @param {Object} config The config object
3844  */
3845
3846
3847 Roo.bootstrap.Navbar = function(config){
3848     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3849     this.addEvents({
3850         // raw events
3851         /**
3852          * @event beforetoggle
3853          * Fire before toggle the menu
3854          * @param {Roo.EventObject} e
3855          */
3856         "beforetoggle" : true
3857     });
3858 };
3859
3860 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3861     
3862     
3863    
3864     // private
3865     navItems : false,
3866     loadMask : false,
3867     
3868     
3869     getAutoCreate : function(){
3870         
3871         
3872         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3873         
3874     },
3875     
3876     initEvents :function ()
3877     {
3878         //Roo.log(this.el.select('.navbar-toggle',true));
3879         this.el.select('.navbar-toggle',true).on('click', function() {
3880             if(this.fireEvent('beforetoggle', this) !== false){
3881                 var ce = this.el.select('.navbar-collapse',true).first();
3882                 ce.toggleClass('in'); // old...
3883                 if (ce.hasClass('collapse')) {
3884                     // show it...
3885                     ce.removeClass('collapse');
3886                     ce.addClass('show');
3887                     var h = ce.getHeight();
3888                     Roo.log(h);
3889                     ce.removeClass('show');
3890                     // at this point we should be able to see it..
3891                     ce.addClass('collapsing');
3892                     
3893                     ce.setHeight(0); // resize it ...
3894                     ce.on('transitionend', function() {
3895                         Roo.log('done transition');
3896                         ce.removeClass('collapsing');
3897                         ce.addClass('show');
3898                         ce.removeClass('collapse');
3899
3900                         ce.dom.style.height = '';
3901                     }, this, { single: true} );
3902                     ce.setHeight(h);
3903                     
3904                 } else {
3905                     ce.setHeight(ce.getHeight());
3906                     ce.removeClass('show');
3907                     ce.addClass('collapsing');
3908                     
3909                     ce.on('transitionend', function() {
3910                         ce.dom.style.height = '';
3911                         ce.removeClass('collapsing');
3912                         ce.addClass('collapse');
3913                     }, this, { single: true} );
3914                     ce.setHeight(0);
3915                 }
3916             }
3917             
3918         }, this);
3919         
3920         var mark = {
3921             tag: "div",
3922             cls:"x-dlg-mask"
3923         };
3924         
3925         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3926         
3927         var size = this.el.getSize();
3928         this.maskEl.setSize(size.width, size.height);
3929         this.maskEl.enableDisplayMode("block");
3930         this.maskEl.hide();
3931         
3932         if(this.loadMask){
3933             this.maskEl.show();
3934         }
3935     },
3936     
3937     
3938     getChildContainer : function()
3939     {
3940         if (this.el.select('.collapse').getCount()) {
3941             return this.el.select('.collapse',true).first();
3942         }
3943         
3944         return this.el;
3945     },
3946     
3947     mask : function()
3948     {
3949         this.maskEl.show();
3950     },
3951     
3952     unmask : function()
3953     {
3954         this.maskEl.hide();
3955     } 
3956     
3957     
3958     
3959     
3960 });
3961
3962
3963
3964  
3965
3966  /*
3967  * - LGPL
3968  *
3969  * navbar
3970  * 
3971  */
3972
3973 /**
3974  * @class Roo.bootstrap.NavSimplebar
3975  * @extends Roo.bootstrap.Navbar
3976  * Bootstrap Sidebar class
3977  *
3978  * @cfg {Boolean} inverse is inverted color
3979  * 
3980  * @cfg {String} type (nav | pills | tabs)
3981  * @cfg {Boolean} arrangement stacked | justified
3982  * @cfg {String} align (left | right) alignment
3983  * 
3984  * @cfg {Boolean} main (true|false) main nav bar? default false
3985  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3986  * 
3987  * @cfg {String} tag (header|footer|nav|div) default is nav 
3988
3989  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3990  * 
3991  * 
3992  * @constructor
3993  * Create a new Sidebar
3994  * @param {Object} config The config object
3995  */
3996
3997
3998 Roo.bootstrap.NavSimplebar = function(config){
3999     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4000 };
4001
4002 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4003     
4004     inverse: false,
4005     
4006     type: false,
4007     arrangement: '',
4008     align : false,
4009     
4010     weight : 'light',
4011     
4012     main : false,
4013     
4014     
4015     tag : false,
4016     
4017     
4018     getAutoCreate : function(){
4019         
4020         
4021         var cfg = {
4022             tag : this.tag || 'div',
4023             cls : 'navbar navbar-expand-lg'
4024         };
4025         if (['light','white'].indexOf(this.weight) > -1) {
4026             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4027         }
4028         cfg.cls += ' bg-' + this.weight;
4029         
4030         if (this.inverse) {
4031             cfg.cls += ' navbar-inverse';
4032             
4033         }
4034         
4035         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4036         
4037         if (Roo.bootstrap.version == 4) {
4038             return cfg;
4039         }
4040         
4041         cfg.cn = [
4042             {
4043                 cls: 'nav',
4044                 tag : 'ul'
4045             }
4046         ];
4047         
4048          
4049         this.type = this.type || 'nav';
4050         if (['tabs','pills'].indexOf(this.type)!==-1) {
4051             cfg.cn[0].cls += ' nav-' + this.type
4052         
4053         
4054         } else {
4055             if (this.type!=='nav') {
4056                 Roo.log('nav type must be nav/tabs/pills')
4057             }
4058             cfg.cn[0].cls += ' navbar-nav'
4059         }
4060         
4061         
4062         
4063         
4064         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4065             cfg.cn[0].cls += ' nav-' + this.arrangement;
4066         }
4067         
4068         
4069         if (this.align === 'right') {
4070             cfg.cn[0].cls += ' navbar-right';
4071         }
4072         
4073         
4074         
4075         
4076         return cfg;
4077     
4078         
4079     }
4080     
4081     
4082     
4083 });
4084
4085
4086
4087  
4088
4089  
4090        /*
4091  * - LGPL
4092  *
4093  * navbar
4094  * navbar-fixed-top
4095  * navbar-expand-md  fixed-top 
4096  */
4097
4098 /**
4099  * @class Roo.bootstrap.NavHeaderbar
4100  * @extends Roo.bootstrap.NavSimplebar
4101  * Bootstrap Sidebar class
4102  *
4103  * @cfg {String} brand what is brand
4104  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4105  * @cfg {String} brand_href href of the brand
4106  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4107  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4108  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4109  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4110  * 
4111  * @constructor
4112  * Create a new Sidebar
4113  * @param {Object} config The config object
4114  */
4115
4116
4117 Roo.bootstrap.NavHeaderbar = function(config){
4118     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4119       
4120 };
4121
4122 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4123     
4124     position: '',
4125     brand: '',
4126     brand_href: false,
4127     srButton : true,
4128     autohide : false,
4129     desktopCenter : false,
4130    
4131     
4132     getAutoCreate : function(){
4133         
4134         var   cfg = {
4135             tag: this.nav || 'nav',
4136             cls: 'navbar navbar-expand-md',
4137             role: 'navigation',
4138             cn: []
4139         };
4140         
4141         var cn = cfg.cn;
4142         if (this.desktopCenter) {
4143             cn.push({cls : 'container', cn : []});
4144             cn = cn[0].cn;
4145         }
4146         
4147         if(this.srButton){
4148             var btn = {
4149                 tag: 'button',
4150                 type: 'button',
4151                 cls: 'navbar-toggle navbar-toggler',
4152                 'data-toggle': 'collapse',
4153                 cn: [
4154                     {
4155                         tag: 'span',
4156                         cls: 'sr-only',
4157                         html: 'Toggle navigation'
4158                     },
4159                     {
4160                         tag: 'span',
4161                         cls: 'icon-bar navbar-toggler-icon'
4162                     },
4163                     {
4164                         tag: 'span',
4165                         cls: 'icon-bar'
4166                     },
4167                     {
4168                         tag: 'span',
4169                         cls: 'icon-bar'
4170                     }
4171                 ]
4172             };
4173             
4174             cn.push( Roo.bootstrap.version == 4 ? btn : {
4175                 tag: 'div',
4176                 cls: 'navbar-header',
4177                 cn: [
4178                     btn
4179                 ]
4180             });
4181         }
4182         
4183         cn.push({
4184             tag: 'div',
4185             cls: 'collapse navbar-collapse',
4186             cn : []
4187         });
4188         
4189         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4190         
4191         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4192             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4193             
4194             // tag can override this..
4195             
4196             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4197         }
4198         
4199         if (this.brand !== '') {
4200             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4201             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4202                 tag: 'a',
4203                 href: this.brand_href ? this.brand_href : '#',
4204                 cls: 'navbar-brand',
4205                 cn: [
4206                 this.brand
4207                 ]
4208             });
4209         }
4210         
4211         if(this.main){
4212             cfg.cls += ' main-nav';
4213         }
4214         
4215         
4216         return cfg;
4217
4218         
4219     },
4220     getHeaderChildContainer : function()
4221     {
4222         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4223             return this.el.select('.navbar-header',true).first();
4224         }
4225         
4226         return this.getChildContainer();
4227     },
4228     
4229     
4230     initEvents : function()
4231     {
4232         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4233         
4234         if (this.autohide) {
4235             
4236             var prevScroll = 0;
4237             var ft = this.el;
4238             
4239             Roo.get(document).on('scroll',function(e) {
4240                 var ns = Roo.get(document).getScroll().top;
4241                 var os = prevScroll;
4242                 prevScroll = ns;
4243                 
4244                 if(ns > os){
4245                     ft.removeClass('slideDown');
4246                     ft.addClass('slideUp');
4247                     return;
4248                 }
4249                 ft.removeClass('slideUp');
4250                 ft.addClass('slideDown');
4251                  
4252               
4253           },this);
4254         }
4255     }    
4256     
4257 });
4258
4259
4260
4261  
4262
4263  /*
4264  * - LGPL
4265  *
4266  * navbar
4267  * 
4268  */
4269
4270 /**
4271  * @class Roo.bootstrap.NavSidebar
4272  * @extends Roo.bootstrap.Navbar
4273  * Bootstrap Sidebar class
4274  * 
4275  * @constructor
4276  * Create a new Sidebar
4277  * @param {Object} config The config object
4278  */
4279
4280
4281 Roo.bootstrap.NavSidebar = function(config){
4282     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4283 };
4284
4285 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4286     
4287     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4288     
4289     getAutoCreate : function(){
4290         
4291         
4292         return  {
4293             tag: 'div',
4294             cls: 'sidebar sidebar-nav'
4295         };
4296     
4297         
4298     }
4299     
4300     
4301     
4302 });
4303
4304
4305
4306  
4307
4308  /*
4309  * - LGPL
4310  *
4311  * nav group
4312  * 
4313  */
4314
4315 /**
4316  * @class Roo.bootstrap.NavGroup
4317  * @extends Roo.bootstrap.Component
4318  * Bootstrap NavGroup class
4319  * @cfg {String} align (left|right)
4320  * @cfg {Boolean} inverse
4321  * @cfg {String} type (nav|pills|tab) default nav
4322  * @cfg {String} navId - reference Id for navbar.
4323
4324  * 
4325  * @constructor
4326  * Create a new nav group
4327  * @param {Object} config The config object
4328  */
4329
4330 Roo.bootstrap.NavGroup = function(config){
4331     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4332     this.navItems = [];
4333    
4334     Roo.bootstrap.NavGroup.register(this);
4335      this.addEvents({
4336         /**
4337              * @event changed
4338              * Fires when the active item changes
4339              * @param {Roo.bootstrap.NavGroup} this
4340              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4341              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4342          */
4343         'changed': true
4344      });
4345     
4346 };
4347
4348 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4349     
4350     align: '',
4351     inverse: false,
4352     form: false,
4353     type: 'nav',
4354     navId : '',
4355     // private
4356     
4357     navItems : false, 
4358     
4359     getAutoCreate : function()
4360     {
4361         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4362         
4363         cfg = {
4364             tag : 'ul',
4365             cls: 'nav' 
4366         };
4367         if (Roo.bootstrap.version == 4) {
4368             if (this.type == 'pills') {
4369                 cfg.cls = ' nav-pills';
4370             }
4371         } else {
4372             if (['tabs','pills'].indexOf(this.type)!==-1) {
4373                 cfg.cls += ' nav-' + this.type
4374             } else {
4375                 if (this.type !== 'nav') {
4376                     Roo.log('nav type must be nav/tabs/pills')
4377                 }
4378                 cfg.cls += ' navbar-nav'
4379             }
4380         }
4381         
4382         if (this.parent() && this.parent().sidebar) {
4383             cfg = {
4384                 tag: 'ul',
4385                 cls: 'dashboard-menu sidebar-menu'
4386             };
4387             
4388             return cfg;
4389         }
4390         
4391         if (this.form === true) {
4392             cfg = {
4393                 tag: 'form',
4394                 cls: 'navbar-form form-inline'
4395             };
4396             
4397             if (this.align === 'right') {
4398                 cfg.cls += ' navbar-right ml-md-auto';
4399             } else {
4400                 cfg.cls += ' navbar-left';
4401             }
4402         }
4403         
4404         if (this.align === 'right') {
4405             cfg.cls += ' navbar-right ml-md-auto';
4406         } else {
4407             cfg.cls += ' mr-auto';
4408         }
4409         
4410         if (this.inverse) {
4411             cfg.cls += ' navbar-inverse';
4412             
4413         }
4414         
4415         
4416         return cfg;
4417     },
4418     /**
4419     * sets the active Navigation item
4420     * @param {Roo.bootstrap.NavItem} the new current navitem
4421     */
4422     setActiveItem : function(item)
4423     {
4424         var prev = false;
4425         Roo.each(this.navItems, function(v){
4426             if (v == item) {
4427                 return ;
4428             }
4429             if (v.isActive()) {
4430                 v.setActive(false, true);
4431                 prev = v;
4432                 
4433             }
4434             
4435         });
4436
4437         item.setActive(true, true);
4438         this.fireEvent('changed', this, item, prev);
4439         
4440         
4441     },
4442     /**
4443     * gets the active Navigation item
4444     * @return {Roo.bootstrap.NavItem} the current navitem
4445     */
4446     getActive : function()
4447     {
4448         
4449         var prev = false;
4450         Roo.each(this.navItems, function(v){
4451             
4452             if (v.isActive()) {
4453                 prev = v;
4454                 
4455             }
4456             
4457         });
4458         return prev;
4459     },
4460     
4461     indexOfNav : function()
4462     {
4463         
4464         var prev = false;
4465         Roo.each(this.navItems, function(v,i){
4466             
4467             if (v.isActive()) {
4468                 prev = i;
4469                 
4470             }
4471             
4472         });
4473         return prev;
4474     },
4475     /**
4476     * adds a Navigation item
4477     * @param {Roo.bootstrap.NavItem} the navitem to add
4478     */
4479     addItem : function(cfg)
4480     {
4481         if (this.form && Roo.bootstrap.version == 4) {
4482             cfg.tag = 'div';
4483         }
4484         var cn = new Roo.bootstrap.NavItem(cfg);
4485         this.register(cn);
4486         cn.parentId = this.id;
4487         cn.onRender(this.el, null);
4488         return cn;
4489     },
4490     /**
4491     * register a Navigation item
4492     * @param {Roo.bootstrap.NavItem} the navitem to add
4493     */
4494     register : function(item)
4495     {
4496         this.navItems.push( item);
4497         item.navId = this.navId;
4498     
4499     },
4500     
4501     /**
4502     * clear all the Navigation item
4503     */
4504    
4505     clearAll : function()
4506     {
4507         this.navItems = [];
4508         this.el.dom.innerHTML = '';
4509     },
4510     
4511     getNavItem: function(tabId)
4512     {
4513         var ret = false;
4514         Roo.each(this.navItems, function(e) {
4515             if (e.tabId == tabId) {
4516                ret =  e;
4517                return false;
4518             }
4519             return true;
4520             
4521         });
4522         return ret;
4523     },
4524     
4525     setActiveNext : function()
4526     {
4527         var i = this.indexOfNav(this.getActive());
4528         if (i > this.navItems.length) {
4529             return;
4530         }
4531         this.setActiveItem(this.navItems[i+1]);
4532     },
4533     setActivePrev : function()
4534     {
4535         var i = this.indexOfNav(this.getActive());
4536         if (i  < 1) {
4537             return;
4538         }
4539         this.setActiveItem(this.navItems[i-1]);
4540     },
4541     clearWasActive : function(except) {
4542         Roo.each(this.navItems, function(e) {
4543             if (e.tabId != except.tabId && e.was_active) {
4544                e.was_active = false;
4545                return false;
4546             }
4547             return true;
4548             
4549         });
4550     },
4551     getWasActive : function ()
4552     {
4553         var r = false;
4554         Roo.each(this.navItems, function(e) {
4555             if (e.was_active) {
4556                r = e;
4557                return false;
4558             }
4559             return true;
4560             
4561         });
4562         return r;
4563     }
4564     
4565     
4566 });
4567
4568  
4569 Roo.apply(Roo.bootstrap.NavGroup, {
4570     
4571     groups: {},
4572      /**
4573     * register a Navigation Group
4574     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4575     */
4576     register : function(navgrp)
4577     {
4578         this.groups[navgrp.navId] = navgrp;
4579         
4580     },
4581     /**
4582     * fetch a Navigation Group based on the navigation ID
4583     * @param {string} the navgroup to add
4584     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4585     */
4586     get: function(navId) {
4587         if (typeof(this.groups[navId]) == 'undefined') {
4588             return false;
4589             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4590         }
4591         return this.groups[navId] ;
4592     }
4593     
4594     
4595     
4596 });
4597
4598  /*
4599  * - LGPL
4600  *
4601  * row
4602  * 
4603  */
4604
4605 /**
4606  * @class Roo.bootstrap.NavItem
4607  * @extends Roo.bootstrap.Component
4608  * Bootstrap Navbar.NavItem class
4609  * @cfg {String} href  link to
4610  * @cfg {String} html content of button
4611  * @cfg {String} badge text inside badge
4612  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4613  * @cfg {String} glyphicon DEPRICATED - use fa
4614  * @cfg {String} icon DEPRICATED - use fa
4615  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4616  * @cfg {Boolean} active Is item active
4617  * @cfg {Boolean} disabled Is item disabled
4618  
4619  * @cfg {Boolean} preventDefault (true | false) default false
4620  * @cfg {String} tabId the tab that this item activates.
4621  * @cfg {String} tagtype (a|span) render as a href or span?
4622  * @cfg {Boolean} animateRef (true|false) link to element default false  
4623   
4624  * @constructor
4625  * Create a new Navbar Item
4626  * @param {Object} config The config object
4627  */
4628 Roo.bootstrap.NavItem = function(config){
4629     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4630     this.addEvents({
4631         // raw events
4632         /**
4633          * @event click
4634          * The raw click event for the entire grid.
4635          * @param {Roo.EventObject} e
4636          */
4637         "click" : true,
4638          /**
4639             * @event changed
4640             * Fires when the active item active state changes
4641             * @param {Roo.bootstrap.NavItem} this
4642             * @param {boolean} state the new state
4643              
4644          */
4645         'changed': true,
4646         /**
4647             * @event scrollto
4648             * Fires when scroll to element
4649             * @param {Roo.bootstrap.NavItem} this
4650             * @param {Object} options
4651             * @param {Roo.EventObject} e
4652              
4653          */
4654         'scrollto': true
4655     });
4656    
4657 };
4658
4659 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4660     
4661     href: false,
4662     html: '',
4663     badge: '',
4664     icon: false,
4665     fa : false,
4666     glyphicon: false,
4667     active: false,
4668     preventDefault : false,
4669     tabId : false,
4670     tagtype : 'a',
4671     tag: 'li',
4672     disabled : false,
4673     animateRef : false,
4674     was_active : false,
4675     
4676     getAutoCreate : function(){
4677          
4678         var cfg = {
4679             tag: this.tag,
4680             cls: 'nav-item'
4681             
4682         };
4683         
4684         if (this.active) {
4685             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4686         }
4687         if (this.disabled) {
4688             cfg.cls += ' disabled';
4689         }
4690         
4691         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4692             cfg.cn = [
4693                 {
4694                     tag: this.tagtype,
4695                     href : this.href || "#",
4696                     html: this.html || ''
4697                 }
4698             ];
4699             if (this.tagtype == 'a') {
4700                 cfg.cn[0].cls = 'nav-link';
4701             }
4702             if (this.icon) {
4703                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4704             }
4705             if (this.fa) {
4706                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4707             }
4708             if(this.glyphicon) {
4709                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4710             }
4711             
4712             if (this.menu) {
4713                 
4714                 cfg.cn[0].html += " <span class='caret'></span>";
4715              
4716             }
4717             
4718             if (this.badge !== '') {
4719                  
4720                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4721             }
4722         }
4723         
4724         
4725         
4726         return cfg;
4727     },
4728     onRender : function(ct, position)
4729     {
4730        // Roo.log("Call onRender: " + this.xtype);
4731         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4732             this.tag = 'div';
4733         }
4734         
4735         return Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4736     },
4737       
4738     
4739     initEvents: function() 
4740     {
4741         if (typeof (this.menu) != 'undefined') {
4742             this.menu.parentType = this.xtype;
4743             this.menu.triggerEl = this.el;
4744             this.menu = this.addxtype(Roo.apply({}, this.menu));
4745         }
4746         
4747         this.el.select('a',true).on('click', this.onClick, this);
4748         
4749         if(this.tagtype == 'span'){
4750             this.el.select('span',true).on('click', this.onClick, this);
4751         }
4752        
4753         // at this point parent should be available..
4754         this.parent().register(this);
4755     },
4756     
4757     onClick : function(e)
4758     {
4759         if (e.getTarget('.dropdown-menu-item')) {
4760             // did you click on a menu itemm.... - then don't trigger onclick..
4761             return;
4762         }
4763         
4764         if(
4765                 this.preventDefault || 
4766                 this.href == '#' 
4767         ){
4768             Roo.log("NavItem - prevent Default?");
4769             e.preventDefault();
4770         }
4771         
4772         if (this.disabled) {
4773             return;
4774         }
4775         
4776         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4777         if (tg && tg.transition) {
4778             Roo.log("waiting for the transitionend");
4779             return;
4780         }
4781         
4782         
4783         
4784         //Roo.log("fire event clicked");
4785         if(this.fireEvent('click', this, e) === false){
4786             return;
4787         };
4788         
4789         if(this.tagtype == 'span'){
4790             return;
4791         }
4792         
4793         //Roo.log(this.href);
4794         var ael = this.el.select('a',true).first();
4795         //Roo.log(ael);
4796         
4797         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4798             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4799             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4800                 return; // ignore... - it's a 'hash' to another page.
4801             }
4802             Roo.log("NavItem - prevent Default?");
4803             e.preventDefault();
4804             this.scrollToElement(e);
4805         }
4806         
4807         
4808         var p =  this.parent();
4809    
4810         if (['tabs','pills'].indexOf(p.type)!==-1) {
4811             if (typeof(p.setActiveItem) !== 'undefined') {
4812                 p.setActiveItem(this);
4813             }
4814         }
4815         
4816         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4817         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4818             // remove the collapsed menu expand...
4819             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4820         }
4821     },
4822     
4823     isActive: function () {
4824         return this.active
4825     },
4826     setActive : function(state, fire, is_was_active)
4827     {
4828         if (this.active && !state && this.navId) {
4829             this.was_active = true;
4830             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4831             if (nv) {
4832                 nv.clearWasActive(this);
4833             }
4834             
4835         }
4836         this.active = state;
4837         
4838         if (!state ) {
4839             this.el.removeClass('active');
4840         } else if (!this.el.hasClass('active')) {
4841             this.el.addClass('active');
4842         }
4843         if (fire) {
4844             this.fireEvent('changed', this, state);
4845         }
4846         
4847         // show a panel if it's registered and related..
4848         
4849         if (!this.navId || !this.tabId || !state || is_was_active) {
4850             return;
4851         }
4852         
4853         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4854         if (!tg) {
4855             return;
4856         }
4857         var pan = tg.getPanelByName(this.tabId);
4858         if (!pan) {
4859             return;
4860         }
4861         // if we can not flip to new panel - go back to old nav highlight..
4862         if (false == tg.showPanel(pan)) {
4863             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4864             if (nv) {
4865                 var onav = nv.getWasActive();
4866                 if (onav) {
4867                     onav.setActive(true, false, true);
4868                 }
4869             }
4870             
4871         }
4872         
4873         
4874         
4875     },
4876      // this should not be here...
4877     setDisabled : function(state)
4878     {
4879         this.disabled = state;
4880         if (!state ) {
4881             this.el.removeClass('disabled');
4882         } else if (!this.el.hasClass('disabled')) {
4883             this.el.addClass('disabled');
4884         }
4885         
4886     },
4887     
4888     /**
4889      * Fetch the element to display the tooltip on.
4890      * @return {Roo.Element} defaults to this.el
4891      */
4892     tooltipEl : function()
4893     {
4894         return this.el.select('' + this.tagtype + '', true).first();
4895     },
4896     
4897     scrollToElement : function(e)
4898     {
4899         var c = document.body;
4900         
4901         /*
4902          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4903          */
4904         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4905             c = document.documentElement;
4906         }
4907         
4908         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4909         
4910         if(!target){
4911             return;
4912         }
4913
4914         var o = target.calcOffsetsTo(c);
4915         
4916         var options = {
4917             target : target,
4918             value : o[1]
4919         };
4920         
4921         this.fireEvent('scrollto', this, options, e);
4922         
4923         Roo.get(c).scrollTo('top', options.value, true);
4924         
4925         return;
4926     }
4927 });
4928  
4929
4930  /*
4931  * - LGPL
4932  *
4933  * sidebar item
4934  *
4935  *  li
4936  *    <span> icon </span>
4937  *    <span> text </span>
4938  *    <span>badge </span>
4939  */
4940
4941 /**
4942  * @class Roo.bootstrap.NavSidebarItem
4943  * @extends Roo.bootstrap.NavItem
4944  * Bootstrap Navbar.NavSidebarItem class
4945  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4946  * {Boolean} open is the menu open
4947  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4948  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4949  * {String} buttonSize (sm|md|lg)the extra classes for the button
4950  * {Boolean} showArrow show arrow next to the text (default true)
4951  * @constructor
4952  * Create a new Navbar Button
4953  * @param {Object} config The config object
4954  */
4955 Roo.bootstrap.NavSidebarItem = function(config){
4956     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4957     this.addEvents({
4958         // raw events
4959         /**
4960          * @event click
4961          * The raw click event for the entire grid.
4962          * @param {Roo.EventObject} e
4963          */
4964         "click" : true,
4965          /**
4966             * @event changed
4967             * Fires when the active item active state changes
4968             * @param {Roo.bootstrap.NavSidebarItem} this
4969             * @param {boolean} state the new state
4970              
4971          */
4972         'changed': true
4973     });
4974    
4975 };
4976
4977 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4978     
4979     badgeWeight : 'default',
4980     
4981     open: false,
4982     
4983     buttonView : false,
4984     
4985     buttonWeight : 'default',
4986     
4987     buttonSize : 'md',
4988     
4989     showArrow : true,
4990     
4991     getAutoCreate : function(){
4992         
4993         
4994         var a = {
4995                 tag: 'a',
4996                 href : this.href || '#',
4997                 cls: '',
4998                 html : '',
4999                 cn : []
5000         };
5001         
5002         if(this.buttonView){
5003             a = {
5004                 tag: 'button',
5005                 href : this.href || '#',
5006                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5007                 html : this.html,
5008                 cn : []
5009             };
5010         }
5011         
5012         var cfg = {
5013             tag: 'li',
5014             cls: '',
5015             cn: [ a ]
5016         };
5017         
5018         if (this.active) {
5019             cfg.cls += ' active';
5020         }
5021         
5022         if (this.disabled) {
5023             cfg.cls += ' disabled';
5024         }
5025         if (this.open) {
5026             cfg.cls += ' open x-open';
5027         }
5028         // left icon..
5029         if (this.glyphicon || this.icon) {
5030             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5031             a.cn.push({ tag : 'i', cls : c }) ;
5032         }
5033         
5034         if(!this.buttonView){
5035             var span = {
5036                 tag: 'span',
5037                 html : this.html || ''
5038             };
5039
5040             a.cn.push(span);
5041             
5042         }
5043         
5044         if (this.badge !== '') {
5045             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5046         }
5047         
5048         if (this.menu) {
5049             
5050             if(this.showArrow){
5051                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5052             }
5053             
5054             a.cls += ' dropdown-toggle treeview' ;
5055         }
5056         
5057         return cfg;
5058     },
5059     
5060     initEvents : function()
5061     { 
5062         if (typeof (this.menu) != 'undefined') {
5063             this.menu.parentType = this.xtype;
5064             this.menu.triggerEl = this.el;
5065             this.menu = this.addxtype(Roo.apply({}, this.menu));
5066         }
5067         
5068         this.el.on('click', this.onClick, this);
5069         
5070         if(this.badge !== ''){
5071             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5072         }
5073         
5074     },
5075     
5076     onClick : function(e)
5077     {
5078         if(this.disabled){
5079             e.preventDefault();
5080             return;
5081         }
5082         
5083         if(this.preventDefault){
5084             e.preventDefault();
5085         }
5086         
5087         this.fireEvent('click', this);
5088     },
5089     
5090     disable : function()
5091     {
5092         this.setDisabled(true);
5093     },
5094     
5095     enable : function()
5096     {
5097         this.setDisabled(false);
5098     },
5099     
5100     setDisabled : function(state)
5101     {
5102         if(this.disabled == state){
5103             return;
5104         }
5105         
5106         this.disabled = state;
5107         
5108         if (state) {
5109             this.el.addClass('disabled');
5110             return;
5111         }
5112         
5113         this.el.removeClass('disabled');
5114         
5115         return;
5116     },
5117     
5118     setActive : function(state)
5119     {
5120         if(this.active == state){
5121             return;
5122         }
5123         
5124         this.active = state;
5125         
5126         if (state) {
5127             this.el.addClass('active');
5128             return;
5129         }
5130         
5131         this.el.removeClass('active');
5132         
5133         return;
5134     },
5135     
5136     isActive: function () 
5137     {
5138         return this.active;
5139     },
5140     
5141     setBadge : function(str)
5142     {
5143         if(!this.badgeEl){
5144             return;
5145         }
5146         
5147         this.badgeEl.dom.innerHTML = str;
5148     }
5149     
5150    
5151      
5152  
5153 });
5154  
5155
5156  /*
5157  * - LGPL
5158  *
5159  * row
5160  * 
5161  */
5162
5163 /**
5164  * @class Roo.bootstrap.Row
5165  * @extends Roo.bootstrap.Component
5166  * Bootstrap Row class (contains columns...)
5167  * 
5168  * @constructor
5169  * Create a new Row
5170  * @param {Object} config The config object
5171  */
5172
5173 Roo.bootstrap.Row = function(config){
5174     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5175 };
5176
5177 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5178     
5179     getAutoCreate : function(){
5180        return {
5181             cls: 'row clearfix'
5182        };
5183     }
5184     
5185     
5186 });
5187
5188  
5189
5190  /*
5191  * - LGPL
5192  *
5193  * element
5194  * 
5195  */
5196
5197 /**
5198  * @class Roo.bootstrap.Element
5199  * @extends Roo.bootstrap.Component
5200  * Bootstrap Element class
5201  * @cfg {String} html contents of the element
5202  * @cfg {String} tag tag of the element
5203  * @cfg {String} cls class of the element
5204  * @cfg {Boolean} preventDefault (true|false) default false
5205  * @cfg {Boolean} clickable (true|false) default false
5206  * 
5207  * @constructor
5208  * Create a new Element
5209  * @param {Object} config The config object
5210  */
5211
5212 Roo.bootstrap.Element = function(config){
5213     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5214     
5215     this.addEvents({
5216         // raw events
5217         /**
5218          * @event click
5219          * When a element is chick
5220          * @param {Roo.bootstrap.Element} this
5221          * @param {Roo.EventObject} e
5222          */
5223         "click" : true
5224     });
5225 };
5226
5227 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5228     
5229     tag: 'div',
5230     cls: '',
5231     html: '',
5232     preventDefault: false, 
5233     clickable: false,
5234     
5235     getAutoCreate : function(){
5236         
5237         var cfg = {
5238             tag: this.tag,
5239             // cls: this.cls, double assign in parent class Component.js :: onRender
5240             html: this.html
5241         };
5242         
5243         return cfg;
5244     },
5245     
5246     initEvents: function() 
5247     {
5248         Roo.bootstrap.Element.superclass.initEvents.call(this);
5249         
5250         if(this.clickable){
5251             this.el.on('click', this.onClick, this);
5252         }
5253         
5254     },
5255     
5256     onClick : function(e)
5257     {
5258         if(this.preventDefault){
5259             e.preventDefault();
5260         }
5261         
5262         this.fireEvent('click', this, e);
5263     },
5264     
5265     getValue : function()
5266     {
5267         return this.el.dom.innerHTML;
5268     },
5269     
5270     setValue : function(value)
5271     {
5272         this.el.dom.innerHTML = value;
5273     }
5274    
5275 });
5276
5277  
5278
5279  /*
5280  * - LGPL
5281  *
5282  * pagination
5283  * 
5284  */
5285
5286 /**
5287  * @class Roo.bootstrap.Pagination
5288  * @extends Roo.bootstrap.Component
5289  * Bootstrap Pagination class
5290  * @cfg {String} size xs | sm | md | lg
5291  * @cfg {Boolean} inverse false | true
5292  * 
5293  * @constructor
5294  * Create a new Pagination
5295  * @param {Object} config The config object
5296  */
5297
5298 Roo.bootstrap.Pagination = function(config){
5299     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5300 };
5301
5302 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5303     
5304     cls: false,
5305     size: false,
5306     inverse: false,
5307     
5308     getAutoCreate : function(){
5309         var cfg = {
5310             tag: 'ul',
5311                 cls: 'pagination'
5312         };
5313         if (this.inverse) {
5314             cfg.cls += ' inverse';
5315         }
5316         if (this.html) {
5317             cfg.html=this.html;
5318         }
5319         if (this.cls) {
5320             cfg.cls += " " + this.cls;
5321         }
5322         return cfg;
5323     }
5324    
5325 });
5326
5327  
5328
5329  /*
5330  * - LGPL
5331  *
5332  * Pagination item
5333  * 
5334  */
5335
5336
5337 /**
5338  * @class Roo.bootstrap.PaginationItem
5339  * @extends Roo.bootstrap.Component
5340  * Bootstrap PaginationItem class
5341  * @cfg {String} html text
5342  * @cfg {String} href the link
5343  * @cfg {Boolean} preventDefault (true | false) default true
5344  * @cfg {Boolean} active (true | false) default false
5345  * @cfg {Boolean} disabled default false
5346  * 
5347  * 
5348  * @constructor
5349  * Create a new PaginationItem
5350  * @param {Object} config The config object
5351  */
5352
5353
5354 Roo.bootstrap.PaginationItem = function(config){
5355     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5356     this.addEvents({
5357         // raw events
5358         /**
5359          * @event click
5360          * The raw click event for the entire grid.
5361          * @param {Roo.EventObject} e
5362          */
5363         "click" : true
5364     });
5365 };
5366
5367 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5368     
5369     href : false,
5370     html : false,
5371     preventDefault: true,
5372     active : false,
5373     cls : false,
5374     disabled: false,
5375     
5376     getAutoCreate : function(){
5377         var cfg= {
5378             tag: 'li',
5379             cn: [
5380                 {
5381                     tag : 'a',
5382                     href : this.href ? this.href : '#',
5383                     html : this.html ? this.html : ''
5384                 }
5385             ]
5386         };
5387         
5388         if(this.cls){
5389             cfg.cls = this.cls;
5390         }
5391         
5392         if(this.disabled){
5393             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5394         }
5395         
5396         if(this.active){
5397             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5398         }
5399         
5400         return cfg;
5401     },
5402     
5403     initEvents: function() {
5404         
5405         this.el.on('click', this.onClick, this);
5406         
5407     },
5408     onClick : function(e)
5409     {
5410         Roo.log('PaginationItem on click ');
5411         if(this.preventDefault){
5412             e.preventDefault();
5413         }
5414         
5415         if(this.disabled){
5416             return;
5417         }
5418         
5419         this.fireEvent('click', this, e);
5420     }
5421    
5422 });
5423
5424  
5425
5426  /*
5427  * - LGPL
5428  *
5429  * slider
5430  * 
5431  */
5432
5433
5434 /**
5435  * @class Roo.bootstrap.Slider
5436  * @extends Roo.bootstrap.Component
5437  * Bootstrap Slider class
5438  *    
5439  * @constructor
5440  * Create a new Slider
5441  * @param {Object} config The config object
5442  */
5443
5444 Roo.bootstrap.Slider = function(config){
5445     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5446 };
5447
5448 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5449     
5450     getAutoCreate : function(){
5451         
5452         var cfg = {
5453             tag: 'div',
5454             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5455             cn: [
5456                 {
5457                     tag: 'a',
5458                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5459                 }
5460             ]
5461         };
5462         
5463         return cfg;
5464     }
5465    
5466 });
5467
5468  /*
5469  * Based on:
5470  * Ext JS Library 1.1.1
5471  * Copyright(c) 2006-2007, Ext JS, LLC.
5472  *
5473  * Originally Released Under LGPL - original licence link has changed is not relivant.
5474  *
5475  * Fork - LGPL
5476  * <script type="text/javascript">
5477  */
5478  
5479
5480 /**
5481  * @class Roo.grid.ColumnModel
5482  * @extends Roo.util.Observable
5483  * This is the default implementation of a ColumnModel used by the Grid. It defines
5484  * the columns in the grid.
5485  * <br>Usage:<br>
5486  <pre><code>
5487  var colModel = new Roo.grid.ColumnModel([
5488         {header: "Ticker", width: 60, sortable: true, locked: true},
5489         {header: "Company Name", width: 150, sortable: true},
5490         {header: "Market Cap.", width: 100, sortable: true},
5491         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5492         {header: "Employees", width: 100, sortable: true, resizable: false}
5493  ]);
5494  </code></pre>
5495  * <p>
5496  
5497  * The config options listed for this class are options which may appear in each
5498  * individual column definition.
5499  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5500  * @constructor
5501  * @param {Object} config An Array of column config objects. See this class's
5502  * config objects for details.
5503 */
5504 Roo.grid.ColumnModel = function(config){
5505         /**
5506      * The config passed into the constructor
5507      */
5508     this.config = config;
5509     this.lookup = {};
5510
5511     // if no id, create one
5512     // if the column does not have a dataIndex mapping,
5513     // map it to the order it is in the config
5514     for(var i = 0, len = config.length; i < len; i++){
5515         var c = config[i];
5516         if(typeof c.dataIndex == "undefined"){
5517             c.dataIndex = i;
5518         }
5519         if(typeof c.renderer == "string"){
5520             c.renderer = Roo.util.Format[c.renderer];
5521         }
5522         if(typeof c.id == "undefined"){
5523             c.id = Roo.id();
5524         }
5525         if(c.editor && c.editor.xtype){
5526             c.editor  = Roo.factory(c.editor, Roo.grid);
5527         }
5528         if(c.editor && c.editor.isFormField){
5529             c.editor = new Roo.grid.GridEditor(c.editor);
5530         }
5531         this.lookup[c.id] = c;
5532     }
5533
5534     /**
5535      * The width of columns which have no width specified (defaults to 100)
5536      * @type Number
5537      */
5538     this.defaultWidth = 100;
5539
5540     /**
5541      * Default sortable of columns which have no sortable specified (defaults to false)
5542      * @type Boolean
5543      */
5544     this.defaultSortable = false;
5545
5546     this.addEvents({
5547         /**
5548              * @event widthchange
5549              * Fires when the width of a column changes.
5550              * @param {ColumnModel} this
5551              * @param {Number} columnIndex The column index
5552              * @param {Number} newWidth The new width
5553              */
5554             "widthchange": true,
5555         /**
5556              * @event headerchange
5557              * Fires when the text of a header changes.
5558              * @param {ColumnModel} this
5559              * @param {Number} columnIndex The column index
5560              * @param {Number} newText The new header text
5561              */
5562             "headerchange": true,
5563         /**
5564              * @event hiddenchange
5565              * Fires when a column is hidden or "unhidden".
5566              * @param {ColumnModel} this
5567              * @param {Number} columnIndex The column index
5568              * @param {Boolean} hidden true if hidden, false otherwise
5569              */
5570             "hiddenchange": true,
5571             /**
5572          * @event columnmoved
5573          * Fires when a column is moved.
5574          * @param {ColumnModel} this
5575          * @param {Number} oldIndex
5576          * @param {Number} newIndex
5577          */
5578         "columnmoved" : true,
5579         /**
5580          * @event columlockchange
5581          * Fires when a column's locked state is changed
5582          * @param {ColumnModel} this
5583          * @param {Number} colIndex
5584          * @param {Boolean} locked true if locked
5585          */
5586         "columnlockchange" : true
5587     });
5588     Roo.grid.ColumnModel.superclass.constructor.call(this);
5589 };
5590 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5591     /**
5592      * @cfg {String} header The header text to display in the Grid view.
5593      */
5594     /**
5595      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5596      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5597      * specified, the column's index is used as an index into the Record's data Array.
5598      */
5599     /**
5600      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5601      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5602      */
5603     /**
5604      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5605      * Defaults to the value of the {@link #defaultSortable} property.
5606      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5607      */
5608     /**
5609      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5610      */
5611     /**
5612      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5613      */
5614     /**
5615      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5616      */
5617     /**
5618      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5619      */
5620     /**
5621      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5622      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5623      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5624      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5625      */
5626        /**
5627      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5628      */
5629     /**
5630      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5631      */
5632     /**
5633      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5634      */
5635     /**
5636      * @cfg {String} cursor (Optional)
5637      */
5638     /**
5639      * @cfg {String} tooltip (Optional)
5640      */
5641     /**
5642      * @cfg {Number} xs (Optional)
5643      */
5644     /**
5645      * @cfg {Number} sm (Optional)
5646      */
5647     /**
5648      * @cfg {Number} md (Optional)
5649      */
5650     /**
5651      * @cfg {Number} lg (Optional)
5652      */
5653     /**
5654      * Returns the id of the column at the specified index.
5655      * @param {Number} index The column index
5656      * @return {String} the id
5657      */
5658     getColumnId : function(index){
5659         return this.config[index].id;
5660     },
5661
5662     /**
5663      * Returns the column for a specified id.
5664      * @param {String} id The column id
5665      * @return {Object} the column
5666      */
5667     getColumnById : function(id){
5668         return this.lookup[id];
5669     },
5670
5671     
5672     /**
5673      * Returns the column for a specified dataIndex.
5674      * @param {String} dataIndex The column dataIndex
5675      * @return {Object|Boolean} the column or false if not found
5676      */
5677     getColumnByDataIndex: function(dataIndex){
5678         var index = this.findColumnIndex(dataIndex);
5679         return index > -1 ? this.config[index] : false;
5680     },
5681     
5682     /**
5683      * Returns the index for a specified column id.
5684      * @param {String} id The column id
5685      * @return {Number} the index, or -1 if not found
5686      */
5687     getIndexById : function(id){
5688         for(var i = 0, len = this.config.length; i < len; i++){
5689             if(this.config[i].id == id){
5690                 return i;
5691             }
5692         }
5693         return -1;
5694     },
5695     
5696     /**
5697      * Returns the index for a specified column dataIndex.
5698      * @param {String} dataIndex The column dataIndex
5699      * @return {Number} the index, or -1 if not found
5700      */
5701     
5702     findColumnIndex : function(dataIndex){
5703         for(var i = 0, len = this.config.length; i < len; i++){
5704             if(this.config[i].dataIndex == dataIndex){
5705                 return i;
5706             }
5707         }
5708         return -1;
5709     },
5710     
5711     
5712     moveColumn : function(oldIndex, newIndex){
5713         var c = this.config[oldIndex];
5714         this.config.splice(oldIndex, 1);
5715         this.config.splice(newIndex, 0, c);
5716         this.dataMap = null;
5717         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5718     },
5719
5720     isLocked : function(colIndex){
5721         return this.config[colIndex].locked === true;
5722     },
5723
5724     setLocked : function(colIndex, value, suppressEvent){
5725         if(this.isLocked(colIndex) == value){
5726             return;
5727         }
5728         this.config[colIndex].locked = value;
5729         if(!suppressEvent){
5730             this.fireEvent("columnlockchange", this, colIndex, value);
5731         }
5732     },
5733
5734     getTotalLockedWidth : function(){
5735         var totalWidth = 0;
5736         for(var i = 0; i < this.config.length; i++){
5737             if(this.isLocked(i) && !this.isHidden(i)){
5738                 this.totalWidth += this.getColumnWidth(i);
5739             }
5740         }
5741         return totalWidth;
5742     },
5743
5744     getLockedCount : function(){
5745         for(var i = 0, len = this.config.length; i < len; i++){
5746             if(!this.isLocked(i)){
5747                 return i;
5748             }
5749         }
5750         
5751         return this.config.length;
5752     },
5753
5754     /**
5755      * Returns the number of columns.
5756      * @return {Number}
5757      */
5758     getColumnCount : function(visibleOnly){
5759         if(visibleOnly === true){
5760             var c = 0;
5761             for(var i = 0, len = this.config.length; i < len; i++){
5762                 if(!this.isHidden(i)){
5763                     c++;
5764                 }
5765             }
5766             return c;
5767         }
5768         return this.config.length;
5769     },
5770
5771     /**
5772      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5773      * @param {Function} fn
5774      * @param {Object} scope (optional)
5775      * @return {Array} result
5776      */
5777     getColumnsBy : function(fn, scope){
5778         var r = [];
5779         for(var i = 0, len = this.config.length; i < len; i++){
5780             var c = this.config[i];
5781             if(fn.call(scope||this, c, i) === true){
5782                 r[r.length] = c;
5783             }
5784         }
5785         return r;
5786     },
5787
5788     /**
5789      * Returns true if the specified column is sortable.
5790      * @param {Number} col The column index
5791      * @return {Boolean}
5792      */
5793     isSortable : function(col){
5794         if(typeof this.config[col].sortable == "undefined"){
5795             return this.defaultSortable;
5796         }
5797         return this.config[col].sortable;
5798     },
5799
5800     /**
5801      * Returns the rendering (formatting) function defined for the column.
5802      * @param {Number} col The column index.
5803      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5804      */
5805     getRenderer : function(col){
5806         if(!this.config[col].renderer){
5807             return Roo.grid.ColumnModel.defaultRenderer;
5808         }
5809         return this.config[col].renderer;
5810     },
5811
5812     /**
5813      * Sets the rendering (formatting) function for a column.
5814      * @param {Number} col The column index
5815      * @param {Function} fn The function to use to process the cell's raw data
5816      * to return HTML markup for the grid view. The render function is called with
5817      * the following parameters:<ul>
5818      * <li>Data value.</li>
5819      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5820      * <li>css A CSS style string to apply to the table cell.</li>
5821      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5822      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5823      * <li>Row index</li>
5824      * <li>Column index</li>
5825      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5826      */
5827     setRenderer : function(col, fn){
5828         this.config[col].renderer = fn;
5829     },
5830
5831     /**
5832      * Returns the width for the specified column.
5833      * @param {Number} col The column index
5834      * @return {Number}
5835      */
5836     getColumnWidth : function(col){
5837         return this.config[col].width * 1 || this.defaultWidth;
5838     },
5839
5840     /**
5841      * Sets the width for a column.
5842      * @param {Number} col The column index
5843      * @param {Number} width The new width
5844      */
5845     setColumnWidth : function(col, width, suppressEvent){
5846         this.config[col].width = width;
5847         this.totalWidth = null;
5848         if(!suppressEvent){
5849              this.fireEvent("widthchange", this, col, width);
5850         }
5851     },
5852
5853     /**
5854      * Returns the total width of all columns.
5855      * @param {Boolean} includeHidden True to include hidden column widths
5856      * @return {Number}
5857      */
5858     getTotalWidth : function(includeHidden){
5859         if(!this.totalWidth){
5860             this.totalWidth = 0;
5861             for(var i = 0, len = this.config.length; i < len; i++){
5862                 if(includeHidden || !this.isHidden(i)){
5863                     this.totalWidth += this.getColumnWidth(i);
5864                 }
5865             }
5866         }
5867         return this.totalWidth;
5868     },
5869
5870     /**
5871      * Returns the header for the specified column.
5872      * @param {Number} col The column index
5873      * @return {String}
5874      */
5875     getColumnHeader : function(col){
5876         return this.config[col].header;
5877     },
5878
5879     /**
5880      * Sets the header for a column.
5881      * @param {Number} col The column index
5882      * @param {String} header The new header
5883      */
5884     setColumnHeader : function(col, header){
5885         this.config[col].header = header;
5886         this.fireEvent("headerchange", this, col, header);
5887     },
5888
5889     /**
5890      * Returns the tooltip for the specified column.
5891      * @param {Number} col The column index
5892      * @return {String}
5893      */
5894     getColumnTooltip : function(col){
5895             return this.config[col].tooltip;
5896     },
5897     /**
5898      * Sets the tooltip for a column.
5899      * @param {Number} col The column index
5900      * @param {String} tooltip The new tooltip
5901      */
5902     setColumnTooltip : function(col, tooltip){
5903             this.config[col].tooltip = tooltip;
5904     },
5905
5906     /**
5907      * Returns the dataIndex for the specified column.
5908      * @param {Number} col The column index
5909      * @return {Number}
5910      */
5911     getDataIndex : function(col){
5912         return this.config[col].dataIndex;
5913     },
5914
5915     /**
5916      * Sets the dataIndex for a column.
5917      * @param {Number} col The column index
5918      * @param {Number} dataIndex The new dataIndex
5919      */
5920     setDataIndex : function(col, dataIndex){
5921         this.config[col].dataIndex = dataIndex;
5922     },
5923
5924     
5925     
5926     /**
5927      * Returns true if the cell is editable.
5928      * @param {Number} colIndex The column index
5929      * @param {Number} rowIndex The row index - this is nto actually used..?
5930      * @return {Boolean}
5931      */
5932     isCellEditable : function(colIndex, rowIndex){
5933         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5934     },
5935
5936     /**
5937      * Returns the editor defined for the cell/column.
5938      * return false or null to disable editing.
5939      * @param {Number} colIndex The column index
5940      * @param {Number} rowIndex The row index
5941      * @return {Object}
5942      */
5943     getCellEditor : function(colIndex, rowIndex){
5944         return this.config[colIndex].editor;
5945     },
5946
5947     /**
5948      * Sets if a column is editable.
5949      * @param {Number} col The column index
5950      * @param {Boolean} editable True if the column is editable
5951      */
5952     setEditable : function(col, editable){
5953         this.config[col].editable = editable;
5954     },
5955
5956
5957     /**
5958      * Returns true if the column is hidden.
5959      * @param {Number} colIndex The column index
5960      * @return {Boolean}
5961      */
5962     isHidden : function(colIndex){
5963         return this.config[colIndex].hidden;
5964     },
5965
5966
5967     /**
5968      * Returns true if the column width cannot be changed
5969      */
5970     isFixed : function(colIndex){
5971         return this.config[colIndex].fixed;
5972     },
5973
5974     /**
5975      * Returns true if the column can be resized
5976      * @return {Boolean}
5977      */
5978     isResizable : function(colIndex){
5979         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5980     },
5981     /**
5982      * Sets if a column is hidden.
5983      * @param {Number} colIndex The column index
5984      * @param {Boolean} hidden True if the column is hidden
5985      */
5986     setHidden : function(colIndex, hidden){
5987         this.config[colIndex].hidden = hidden;
5988         this.totalWidth = null;
5989         this.fireEvent("hiddenchange", this, colIndex, hidden);
5990     },
5991
5992     /**
5993      * Sets the editor for a column.
5994      * @param {Number} col The column index
5995      * @param {Object} editor The editor object
5996      */
5997     setEditor : function(col, editor){
5998         this.config[col].editor = editor;
5999     }
6000 });
6001
6002 Roo.grid.ColumnModel.defaultRenderer = function(value)
6003 {
6004     if(typeof value == "object") {
6005         return value;
6006     }
6007         if(typeof value == "string" && value.length < 1){
6008             return "&#160;";
6009         }
6010     
6011         return String.format("{0}", value);
6012 };
6013
6014 // Alias for backwards compatibility
6015 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6016 /*
6017  * Based on:
6018  * Ext JS Library 1.1.1
6019  * Copyright(c) 2006-2007, Ext JS, LLC.
6020  *
6021  * Originally Released Under LGPL - original licence link has changed is not relivant.
6022  *
6023  * Fork - LGPL
6024  * <script type="text/javascript">
6025  */
6026  
6027 /**
6028  * @class Roo.LoadMask
6029  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6030  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6031  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6032  * element's UpdateManager load indicator and will be destroyed after the initial load.
6033  * @constructor
6034  * Create a new LoadMask
6035  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6036  * @param {Object} config The config object
6037  */
6038 Roo.LoadMask = function(el, config){
6039     this.el = Roo.get(el);
6040     Roo.apply(this, config);
6041     if(this.store){
6042         this.store.on('beforeload', this.onBeforeLoad, this);
6043         this.store.on('load', this.onLoad, this);
6044         this.store.on('loadexception', this.onLoadException, this);
6045         this.removeMask = false;
6046     }else{
6047         var um = this.el.getUpdateManager();
6048         um.showLoadIndicator = false; // disable the default indicator
6049         um.on('beforeupdate', this.onBeforeLoad, this);
6050         um.on('update', this.onLoad, this);
6051         um.on('failure', this.onLoad, this);
6052         this.removeMask = true;
6053     }
6054 };
6055
6056 Roo.LoadMask.prototype = {
6057     /**
6058      * @cfg {Boolean} removeMask
6059      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6060      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6061      */
6062     /**
6063      * @cfg {String} msg
6064      * The text to display in a centered loading message box (defaults to 'Loading...')
6065      */
6066     msg : 'Loading...',
6067     /**
6068      * @cfg {String} msgCls
6069      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6070      */
6071     msgCls : 'x-mask-loading',
6072
6073     /**
6074      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6075      * @type Boolean
6076      */
6077     disabled: false,
6078
6079     /**
6080      * Disables the mask to prevent it from being displayed
6081      */
6082     disable : function(){
6083        this.disabled = true;
6084     },
6085
6086     /**
6087      * Enables the mask so that it can be displayed
6088      */
6089     enable : function(){
6090         this.disabled = false;
6091     },
6092     
6093     onLoadException : function()
6094     {
6095         Roo.log(arguments);
6096         
6097         if (typeof(arguments[3]) != 'undefined') {
6098             Roo.MessageBox.alert("Error loading",arguments[3]);
6099         } 
6100         /*
6101         try {
6102             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6103                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6104             }   
6105         } catch(e) {
6106             
6107         }
6108         */
6109     
6110         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6111     },
6112     // private
6113     onLoad : function()
6114     {
6115         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6116     },
6117
6118     // private
6119     onBeforeLoad : function(){
6120         if(!this.disabled){
6121             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6122         }
6123     },
6124
6125     // private
6126     destroy : function(){
6127         if(this.store){
6128             this.store.un('beforeload', this.onBeforeLoad, this);
6129             this.store.un('load', this.onLoad, this);
6130             this.store.un('loadexception', this.onLoadException, this);
6131         }else{
6132             var um = this.el.getUpdateManager();
6133             um.un('beforeupdate', this.onBeforeLoad, this);
6134             um.un('update', this.onLoad, this);
6135             um.un('failure', this.onLoad, this);
6136         }
6137     }
6138 };/*
6139  * - LGPL
6140  *
6141  * table
6142  * 
6143  */
6144
6145 /**
6146  * @class Roo.bootstrap.Table
6147  * @extends Roo.bootstrap.Component
6148  * Bootstrap Table class
6149  * @cfg {String} cls table class
6150  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6151  * @cfg {String} bgcolor Specifies the background color for a table
6152  * @cfg {Number} border Specifies whether the table cells should have borders or not
6153  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6154  * @cfg {Number} cellspacing Specifies the space between cells
6155  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6156  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6157  * @cfg {String} sortable Specifies that the table should be sortable
6158  * @cfg {String} summary Specifies a summary of the content of a table
6159  * @cfg {Number} width Specifies the width of a table
6160  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6161  * 
6162  * @cfg {boolean} striped Should the rows be alternative striped
6163  * @cfg {boolean} bordered Add borders to the table
6164  * @cfg {boolean} hover Add hover highlighting
6165  * @cfg {boolean} condensed Format condensed
6166  * @cfg {boolean} responsive Format condensed
6167  * @cfg {Boolean} loadMask (true|false) default false
6168  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6169  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6170  * @cfg {Boolean} rowSelection (true|false) default false
6171  * @cfg {Boolean} cellSelection (true|false) default false
6172  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6173  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6174  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6175  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6176  
6177  * 
6178  * @constructor
6179  * Create a new Table
6180  * @param {Object} config The config object
6181  */
6182
6183 Roo.bootstrap.Table = function(config){
6184     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6185     
6186   
6187     
6188     // BC...
6189     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6190     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6191     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6192     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6193     
6194     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6195     if (this.sm) {
6196         this.sm.grid = this;
6197         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6198         this.sm = this.selModel;
6199         this.sm.xmodule = this.xmodule || false;
6200     }
6201     
6202     if (this.cm && typeof(this.cm.config) == 'undefined') {
6203         this.colModel = new Roo.grid.ColumnModel(this.cm);
6204         this.cm = this.colModel;
6205         this.cm.xmodule = this.xmodule || false;
6206     }
6207     if (this.store) {
6208         this.store= Roo.factory(this.store, Roo.data);
6209         this.ds = this.store;
6210         this.ds.xmodule = this.xmodule || false;
6211          
6212     }
6213     if (this.footer && this.store) {
6214         this.footer.dataSource = this.ds;
6215         this.footer = Roo.factory(this.footer);
6216     }
6217     
6218     /** @private */
6219     this.addEvents({
6220         /**
6221          * @event cellclick
6222          * Fires when a cell is clicked
6223          * @param {Roo.bootstrap.Table} this
6224          * @param {Roo.Element} el
6225          * @param {Number} rowIndex
6226          * @param {Number} columnIndex
6227          * @param {Roo.EventObject} e
6228          */
6229         "cellclick" : true,
6230         /**
6231          * @event celldblclick
6232          * Fires when a cell is double clicked
6233          * @param {Roo.bootstrap.Table} this
6234          * @param {Roo.Element} el
6235          * @param {Number} rowIndex
6236          * @param {Number} columnIndex
6237          * @param {Roo.EventObject} e
6238          */
6239         "celldblclick" : true,
6240         /**
6241          * @event rowclick
6242          * Fires when a row is clicked
6243          * @param {Roo.bootstrap.Table} this
6244          * @param {Roo.Element} el
6245          * @param {Number} rowIndex
6246          * @param {Roo.EventObject} e
6247          */
6248         "rowclick" : true,
6249         /**
6250          * @event rowdblclick
6251          * Fires when a row is double clicked
6252          * @param {Roo.bootstrap.Table} this
6253          * @param {Roo.Element} el
6254          * @param {Number} rowIndex
6255          * @param {Roo.EventObject} e
6256          */
6257         "rowdblclick" : true,
6258         /**
6259          * @event mouseover
6260          * Fires when a mouseover occur
6261          * @param {Roo.bootstrap.Table} this
6262          * @param {Roo.Element} el
6263          * @param {Number} rowIndex
6264          * @param {Number} columnIndex
6265          * @param {Roo.EventObject} e
6266          */
6267         "mouseover" : true,
6268         /**
6269          * @event mouseout
6270          * Fires when a mouseout occur
6271          * @param {Roo.bootstrap.Table} this
6272          * @param {Roo.Element} el
6273          * @param {Number} rowIndex
6274          * @param {Number} columnIndex
6275          * @param {Roo.EventObject} e
6276          */
6277         "mouseout" : true,
6278         /**
6279          * @event rowclass
6280          * Fires when a row is rendered, so you can change add a style to it.
6281          * @param {Roo.bootstrap.Table} this
6282          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6283          */
6284         'rowclass' : true,
6285           /**
6286          * @event rowsrendered
6287          * Fires when all the  rows have been rendered
6288          * @param {Roo.bootstrap.Table} this
6289          */
6290         'rowsrendered' : true,
6291         /**
6292          * @event contextmenu
6293          * The raw contextmenu event for the entire grid.
6294          * @param {Roo.EventObject} e
6295          */
6296         "contextmenu" : true,
6297         /**
6298          * @event rowcontextmenu
6299          * Fires when a row is right clicked
6300          * @param {Roo.bootstrap.Table} this
6301          * @param {Number} rowIndex
6302          * @param {Roo.EventObject} e
6303          */
6304         "rowcontextmenu" : true,
6305         /**
6306          * @event cellcontextmenu
6307          * Fires when a cell is right clicked
6308          * @param {Roo.bootstrap.Table} this
6309          * @param {Number} rowIndex
6310          * @param {Number} cellIndex
6311          * @param {Roo.EventObject} e
6312          */
6313          "cellcontextmenu" : true,
6314          /**
6315          * @event headercontextmenu
6316          * Fires when a header is right clicked
6317          * @param {Roo.bootstrap.Table} this
6318          * @param {Number} columnIndex
6319          * @param {Roo.EventObject} e
6320          */
6321         "headercontextmenu" : true
6322     });
6323 };
6324
6325 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6326     
6327     cls: false,
6328     align: false,
6329     bgcolor: false,
6330     border: false,
6331     cellpadding: false,
6332     cellspacing: false,
6333     frame: false,
6334     rules: false,
6335     sortable: false,
6336     summary: false,
6337     width: false,
6338     striped : false,
6339     scrollBody : false,
6340     bordered: false,
6341     hover:  false,
6342     condensed : false,
6343     responsive : false,
6344     sm : false,
6345     cm : false,
6346     store : false,
6347     loadMask : false,
6348     footerShow : true,
6349     headerShow : true,
6350   
6351     rowSelection : false,
6352     cellSelection : false,
6353     layout : false,
6354     
6355     // Roo.Element - the tbody
6356     mainBody: false,
6357     // Roo.Element - thead element
6358     mainHead: false,
6359     
6360     container: false, // used by gridpanel...
6361     
6362     lazyLoad : false,
6363     
6364     CSS : Roo.util.CSS,
6365     
6366     auto_hide_footer : false,
6367     
6368     getAutoCreate : function()
6369     {
6370         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6371         
6372         cfg = {
6373             tag: 'table',
6374             cls : 'table',
6375             cn : []
6376         };
6377         if (this.scrollBody) {
6378             cfg.cls += ' table-body-fixed';
6379         }    
6380         if (this.striped) {
6381             cfg.cls += ' table-striped';
6382         }
6383         
6384         if (this.hover) {
6385             cfg.cls += ' table-hover';
6386         }
6387         if (this.bordered) {
6388             cfg.cls += ' table-bordered';
6389         }
6390         if (this.condensed) {
6391             cfg.cls += ' table-condensed';
6392         }
6393         if (this.responsive) {
6394             cfg.cls += ' table-responsive';
6395         }
6396         
6397         if (this.cls) {
6398             cfg.cls+=  ' ' +this.cls;
6399         }
6400         
6401         // this lot should be simplifed...
6402         var _t = this;
6403         var cp = [
6404             'align',
6405             'bgcolor',
6406             'border',
6407             'cellpadding',
6408             'cellspacing',
6409             'frame',
6410             'rules',
6411             'sortable',
6412             'summary',
6413             'width'
6414         ].forEach(function(k) {
6415             if (_t[k]) {
6416                 cfg[k] = _t[k];
6417             }
6418         });
6419         
6420         
6421         if (this.layout) {
6422             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6423         }
6424         
6425         if(this.store || this.cm){
6426             if(this.headerShow){
6427                 cfg.cn.push(this.renderHeader());
6428             }
6429             
6430             cfg.cn.push(this.renderBody());
6431             
6432             if(this.footerShow){
6433                 cfg.cn.push(this.renderFooter());
6434             }
6435             // where does this come from?
6436             //cfg.cls+=  ' TableGrid';
6437         }
6438         
6439         return { cn : [ cfg ] };
6440     },
6441     
6442     initEvents : function()
6443     {   
6444         if(!this.store || !this.cm){
6445             return;
6446         }
6447         if (this.selModel) {
6448             this.selModel.initEvents();
6449         }
6450         
6451         
6452         //Roo.log('initEvents with ds!!!!');
6453         
6454         this.mainBody = this.el.select('tbody', true).first();
6455         this.mainHead = this.el.select('thead', true).first();
6456         this.mainFoot = this.el.select('tfoot', true).first();
6457         
6458         
6459         
6460         var _this = this;
6461         
6462         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6463             e.on('click', _this.sort, _this);
6464         });
6465         
6466         this.mainBody.on("click", this.onClick, this);
6467         this.mainBody.on("dblclick", this.onDblClick, this);
6468         
6469         // why is this done????? = it breaks dialogs??
6470         //this.parent().el.setStyle('position', 'relative');
6471         
6472         
6473         if (this.footer) {
6474             this.footer.parentId = this.id;
6475             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6476             
6477             if(this.lazyLoad){
6478                 this.el.select('tfoot tr td').first().addClass('hide');
6479             }
6480         } 
6481         
6482         if(this.loadMask) {
6483             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6484         }
6485         
6486         this.store.on('load', this.onLoad, this);
6487         this.store.on('beforeload', this.onBeforeLoad, this);
6488         this.store.on('update', this.onUpdate, this);
6489         this.store.on('add', this.onAdd, this);
6490         this.store.on("clear", this.clear, this);
6491         
6492         this.el.on("contextmenu", this.onContextMenu, this);
6493         
6494         this.mainBody.on('scroll', this.onBodyScroll, this);
6495         
6496         this.cm.on("headerchange", this.onHeaderChange, this);
6497         
6498         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6499         
6500     },
6501     
6502     onContextMenu : function(e, t)
6503     {
6504         this.processEvent("contextmenu", e);
6505     },
6506     
6507     processEvent : function(name, e)
6508     {
6509         if (name != 'touchstart' ) {
6510             this.fireEvent(name, e);    
6511         }
6512         
6513         var t = e.getTarget();
6514         
6515         var cell = Roo.get(t);
6516         
6517         if(!cell){
6518             return;
6519         }
6520         
6521         if(cell.findParent('tfoot', false, true)){
6522             return;
6523         }
6524         
6525         if(cell.findParent('thead', false, true)){
6526             
6527             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6528                 cell = Roo.get(t).findParent('th', false, true);
6529                 if (!cell) {
6530                     Roo.log("failed to find th in thead?");
6531                     Roo.log(e.getTarget());
6532                     return;
6533                 }
6534             }
6535             
6536             var cellIndex = cell.dom.cellIndex;
6537             
6538             var ename = name == 'touchstart' ? 'click' : name;
6539             this.fireEvent("header" + ename, this, cellIndex, e);
6540             
6541             return;
6542         }
6543         
6544         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6545             cell = Roo.get(t).findParent('td', false, true);
6546             if (!cell) {
6547                 Roo.log("failed to find th in tbody?");
6548                 Roo.log(e.getTarget());
6549                 return;
6550             }
6551         }
6552         
6553         var row = cell.findParent('tr', false, true);
6554         var cellIndex = cell.dom.cellIndex;
6555         var rowIndex = row.dom.rowIndex - 1;
6556         
6557         if(row !== false){
6558             
6559             this.fireEvent("row" + name, this, rowIndex, e);
6560             
6561             if(cell !== false){
6562             
6563                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6564             }
6565         }
6566         
6567     },
6568     
6569     onMouseover : function(e, el)
6570     {
6571         var cell = Roo.get(el);
6572         
6573         if(!cell){
6574             return;
6575         }
6576         
6577         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6578             cell = cell.findParent('td', false, true);
6579         }
6580         
6581         var row = cell.findParent('tr', false, true);
6582         var cellIndex = cell.dom.cellIndex;
6583         var rowIndex = row.dom.rowIndex - 1; // start from 0
6584         
6585         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6586         
6587     },
6588     
6589     onMouseout : function(e, el)
6590     {
6591         var cell = Roo.get(el);
6592         
6593         if(!cell){
6594             return;
6595         }
6596         
6597         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6598             cell = cell.findParent('td', false, true);
6599         }
6600         
6601         var row = cell.findParent('tr', false, true);
6602         var cellIndex = cell.dom.cellIndex;
6603         var rowIndex = row.dom.rowIndex - 1; // start from 0
6604         
6605         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6606         
6607     },
6608     
6609     onClick : function(e, el)
6610     {
6611         var cell = Roo.get(el);
6612         
6613         if(!cell || (!this.cellSelection && !this.rowSelection)){
6614             return;
6615         }
6616         
6617         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6618             cell = cell.findParent('td', false, true);
6619         }
6620         
6621         if(!cell || typeof(cell) == 'undefined'){
6622             return;
6623         }
6624         
6625         var row = cell.findParent('tr', false, true);
6626         
6627         if(!row || typeof(row) == 'undefined'){
6628             return;
6629         }
6630         
6631         var cellIndex = cell.dom.cellIndex;
6632         var rowIndex = this.getRowIndex(row);
6633         
6634         // why??? - should these not be based on SelectionModel?
6635         if(this.cellSelection){
6636             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6637         }
6638         
6639         if(this.rowSelection){
6640             this.fireEvent('rowclick', this, row, rowIndex, e);
6641         }
6642         
6643         
6644     },
6645         
6646     onDblClick : function(e,el)
6647     {
6648         var cell = Roo.get(el);
6649         
6650         if(!cell || (!this.cellSelection && !this.rowSelection)){
6651             return;
6652         }
6653         
6654         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6655             cell = cell.findParent('td', false, true);
6656         }
6657         
6658         if(!cell || typeof(cell) == 'undefined'){
6659             return;
6660         }
6661         
6662         var row = cell.findParent('tr', false, true);
6663         
6664         if(!row || typeof(row) == 'undefined'){
6665             return;
6666         }
6667         
6668         var cellIndex = cell.dom.cellIndex;
6669         var rowIndex = this.getRowIndex(row);
6670         
6671         if(this.cellSelection){
6672             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6673         }
6674         
6675         if(this.rowSelection){
6676             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6677         }
6678     },
6679     
6680     sort : function(e,el)
6681     {
6682         var col = Roo.get(el);
6683         
6684         if(!col.hasClass('sortable')){
6685             return;
6686         }
6687         
6688         var sort = col.attr('sort');
6689         var dir = 'ASC';
6690         
6691         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6692             dir = 'DESC';
6693         }
6694         
6695         this.store.sortInfo = {field : sort, direction : dir};
6696         
6697         if (this.footer) {
6698             Roo.log("calling footer first");
6699             this.footer.onClick('first');
6700         } else {
6701         
6702             this.store.load({ params : { start : 0 } });
6703         }
6704     },
6705     
6706     renderHeader : function()
6707     {
6708         var header = {
6709             tag: 'thead',
6710             cn : []
6711         };
6712         
6713         var cm = this.cm;
6714         this.totalWidth = 0;
6715         
6716         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6717             
6718             var config = cm.config[i];
6719             
6720             var c = {
6721                 tag: 'th',
6722                 cls : 'x-hcol-' + i,
6723                 style : '',
6724                 html: cm.getColumnHeader(i)
6725             };
6726             
6727             var hh = '';
6728             
6729             if(typeof(config.sortable) != 'undefined' && config.sortable){
6730                 c.cls = 'sortable';
6731                 c.html = '<i class="glyphicon"></i>' + c.html;
6732             }
6733             
6734             if(typeof(config.lgHeader) != 'undefined'){
6735                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6736             }
6737             
6738             if(typeof(config.mdHeader) != 'undefined'){
6739                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6740             }
6741             
6742             if(typeof(config.smHeader) != 'undefined'){
6743                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6744             }
6745             
6746             if(typeof(config.xsHeader) != 'undefined'){
6747                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6748             }
6749             
6750             if(hh.length){
6751                 c.html = hh;
6752             }
6753             
6754             if(typeof(config.tooltip) != 'undefined'){
6755                 c.tooltip = config.tooltip;
6756             }
6757             
6758             if(typeof(config.colspan) != 'undefined'){
6759                 c.colspan = config.colspan;
6760             }
6761             
6762             if(typeof(config.hidden) != 'undefined' && config.hidden){
6763                 c.style += ' display:none;';
6764             }
6765             
6766             if(typeof(config.dataIndex) != 'undefined'){
6767                 c.sort = config.dataIndex;
6768             }
6769             
6770            
6771             
6772             if(typeof(config.align) != 'undefined' && config.align.length){
6773                 c.style += ' text-align:' + config.align + ';';
6774             }
6775             
6776             if(typeof(config.width) != 'undefined'){
6777                 c.style += ' width:' + config.width + 'px;';
6778                 this.totalWidth += config.width;
6779             } else {
6780                 this.totalWidth += 100; // assume minimum of 100 per column?
6781             }
6782             
6783             if(typeof(config.cls) != 'undefined'){
6784                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6785             }
6786             
6787             ['xs','sm','md','lg'].map(function(size){
6788                 
6789                 if(typeof(config[size]) == 'undefined'){
6790                     return;
6791                 }
6792                 
6793                 if (!config[size]) { // 0 = hidden
6794                     c.cls += ' hidden-' + size;
6795                     return;
6796                 }
6797                 
6798                 c.cls += ' col-' + size + '-' + config[size];
6799
6800             });
6801             
6802             header.cn.push(c)
6803         }
6804         
6805         return header;
6806     },
6807     
6808     renderBody : function()
6809     {
6810         var body = {
6811             tag: 'tbody',
6812             cn : [
6813                 {
6814                     tag: 'tr',
6815                     cn : [
6816                         {
6817                             tag : 'td',
6818                             colspan :  this.cm.getColumnCount()
6819                         }
6820                     ]
6821                 }
6822             ]
6823         };
6824         
6825         return body;
6826     },
6827     
6828     renderFooter : function()
6829     {
6830         var footer = {
6831             tag: 'tfoot',
6832             cn : [
6833                 {
6834                     tag: 'tr',
6835                     cn : [
6836                         {
6837                             tag : 'td',
6838                             colspan :  this.cm.getColumnCount()
6839                         }
6840                     ]
6841                 }
6842             ]
6843         };
6844         
6845         return footer;
6846     },
6847     
6848     
6849     
6850     onLoad : function()
6851     {
6852 //        Roo.log('ds onload');
6853         this.clear();
6854         
6855         var _this = this;
6856         var cm = this.cm;
6857         var ds = this.store;
6858         
6859         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6860             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6861             if (_this.store.sortInfo) {
6862                     
6863                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6864                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6865                 }
6866                 
6867                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6868                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6869                 }
6870             }
6871         });
6872         
6873         var tbody =  this.mainBody;
6874               
6875         if(ds.getCount() > 0){
6876             ds.data.each(function(d,rowIndex){
6877                 var row =  this.renderRow(cm, ds, rowIndex);
6878                 
6879                 tbody.createChild(row);
6880                 
6881                 var _this = this;
6882                 
6883                 if(row.cellObjects.length){
6884                     Roo.each(row.cellObjects, function(r){
6885                         _this.renderCellObject(r);
6886                     })
6887                 }
6888                 
6889             }, this);
6890         }
6891         
6892         var tfoot = this.el.select('tfoot', true).first();
6893         
6894         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6895             
6896             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6897             
6898             var total = this.ds.getTotalCount();
6899             
6900             if(this.footer.pageSize < total){
6901                 this.mainFoot.show();
6902             }
6903         }
6904         
6905         Roo.each(this.el.select('tbody td', true).elements, function(e){
6906             e.on('mouseover', _this.onMouseover, _this);
6907         });
6908         
6909         Roo.each(this.el.select('tbody td', true).elements, function(e){
6910             e.on('mouseout', _this.onMouseout, _this);
6911         });
6912         this.fireEvent('rowsrendered', this);
6913         
6914         this.autoSize();
6915     },
6916     
6917     
6918     onUpdate : function(ds,record)
6919     {
6920         this.refreshRow(record);
6921         this.autoSize();
6922     },
6923     
6924     onRemove : function(ds, record, index, isUpdate){
6925         if(isUpdate !== true){
6926             this.fireEvent("beforerowremoved", this, index, record);
6927         }
6928         var bt = this.mainBody.dom;
6929         
6930         var rows = this.el.select('tbody > tr', true).elements;
6931         
6932         if(typeof(rows[index]) != 'undefined'){
6933             bt.removeChild(rows[index].dom);
6934         }
6935         
6936 //        if(bt.rows[index]){
6937 //            bt.removeChild(bt.rows[index]);
6938 //        }
6939         
6940         if(isUpdate !== true){
6941             //this.stripeRows(index);
6942             //this.syncRowHeights(index, index);
6943             //this.layout();
6944             this.fireEvent("rowremoved", this, index, record);
6945         }
6946     },
6947     
6948     onAdd : function(ds, records, rowIndex)
6949     {
6950         //Roo.log('on Add called');
6951         // - note this does not handle multiple adding very well..
6952         var bt = this.mainBody.dom;
6953         for (var i =0 ; i < records.length;i++) {
6954             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6955             //Roo.log(records[i]);
6956             //Roo.log(this.store.getAt(rowIndex+i));
6957             this.insertRow(this.store, rowIndex + i, false);
6958             return;
6959         }
6960         
6961     },
6962     
6963     
6964     refreshRow : function(record){
6965         var ds = this.store, index;
6966         if(typeof record == 'number'){
6967             index = record;
6968             record = ds.getAt(index);
6969         }else{
6970             index = ds.indexOf(record);
6971         }
6972         this.insertRow(ds, index, true);
6973         this.autoSize();
6974         this.onRemove(ds, record, index+1, true);
6975         this.autoSize();
6976         //this.syncRowHeights(index, index);
6977         //this.layout();
6978         this.fireEvent("rowupdated", this, index, record);
6979     },
6980     
6981     insertRow : function(dm, rowIndex, isUpdate){
6982         
6983         if(!isUpdate){
6984             this.fireEvent("beforerowsinserted", this, rowIndex);
6985         }
6986             //var s = this.getScrollState();
6987         var row = this.renderRow(this.cm, this.store, rowIndex);
6988         // insert before rowIndex..
6989         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6990         
6991         var _this = this;
6992                 
6993         if(row.cellObjects.length){
6994             Roo.each(row.cellObjects, function(r){
6995                 _this.renderCellObject(r);
6996             })
6997         }
6998             
6999         if(!isUpdate){
7000             this.fireEvent("rowsinserted", this, rowIndex);
7001             //this.syncRowHeights(firstRow, lastRow);
7002             //this.stripeRows(firstRow);
7003             //this.layout();
7004         }
7005         
7006     },
7007     
7008     
7009     getRowDom : function(rowIndex)
7010     {
7011         var rows = this.el.select('tbody > tr', true).elements;
7012         
7013         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7014         
7015     },
7016     // returns the object tree for a tr..
7017   
7018     
7019     renderRow : function(cm, ds, rowIndex) 
7020     {
7021         var d = ds.getAt(rowIndex);
7022         
7023         var row = {
7024             tag : 'tr',
7025             cls : 'x-row-' + rowIndex,
7026             cn : []
7027         };
7028             
7029         var cellObjects = [];
7030         
7031         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7032             var config = cm.config[i];
7033             
7034             var renderer = cm.getRenderer(i);
7035             var value = '';
7036             var id = false;
7037             
7038             if(typeof(renderer) !== 'undefined'){
7039                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7040             }
7041             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7042             // and are rendered into the cells after the row is rendered - using the id for the element.
7043             
7044             if(typeof(value) === 'object'){
7045                 id = Roo.id();
7046                 cellObjects.push({
7047                     container : id,
7048                     cfg : value 
7049                 })
7050             }
7051             
7052             var rowcfg = {
7053                 record: d,
7054                 rowIndex : rowIndex,
7055                 colIndex : i,
7056                 rowClass : ''
7057             };
7058
7059             this.fireEvent('rowclass', this, rowcfg);
7060             
7061             var td = {
7062                 tag: 'td',
7063                 cls : rowcfg.rowClass + ' x-col-' + i,
7064                 style: '',
7065                 html: (typeof(value) === 'object') ? '' : value
7066             };
7067             
7068             if (id) {
7069                 td.id = id;
7070             }
7071             
7072             if(typeof(config.colspan) != 'undefined'){
7073                 td.colspan = config.colspan;
7074             }
7075             
7076             if(typeof(config.hidden) != 'undefined' && config.hidden){
7077                 td.style += ' display:none;';
7078             }
7079             
7080             if(typeof(config.align) != 'undefined' && config.align.length){
7081                 td.style += ' text-align:' + config.align + ';';
7082             }
7083             if(typeof(config.valign) != 'undefined' && config.valign.length){
7084                 td.style += ' vertical-align:' + config.valign + ';';
7085             }
7086             
7087             if(typeof(config.width) != 'undefined'){
7088                 td.style += ' width:' +  config.width + 'px;';
7089             }
7090             
7091             if(typeof(config.cursor) != 'undefined'){
7092                 td.style += ' cursor:' +  config.cursor + ';';
7093             }
7094             
7095             if(typeof(config.cls) != 'undefined'){
7096                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7097             }
7098             
7099             ['xs','sm','md','lg'].map(function(size){
7100                 
7101                 if(typeof(config[size]) == 'undefined'){
7102                     return;
7103                 }
7104                 
7105                 if (!config[size]) { // 0 = hidden
7106                     td.cls += ' hidden-' + size;
7107                     return;
7108                 }
7109                 
7110                 td.cls += ' col-' + size + '-' + config[size];
7111
7112             });
7113             
7114             row.cn.push(td);
7115            
7116         }
7117         
7118         row.cellObjects = cellObjects;
7119         
7120         return row;
7121           
7122     },
7123     
7124     
7125     
7126     onBeforeLoad : function()
7127     {
7128         
7129     },
7130      /**
7131      * Remove all rows
7132      */
7133     clear : function()
7134     {
7135         this.el.select('tbody', true).first().dom.innerHTML = '';
7136     },
7137     /**
7138      * Show or hide a row.
7139      * @param {Number} rowIndex to show or hide
7140      * @param {Boolean} state hide
7141      */
7142     setRowVisibility : function(rowIndex, state)
7143     {
7144         var bt = this.mainBody.dom;
7145         
7146         var rows = this.el.select('tbody > tr', true).elements;
7147         
7148         if(typeof(rows[rowIndex]) == 'undefined'){
7149             return;
7150         }
7151         rows[rowIndex].dom.style.display = state ? '' : 'none';
7152     },
7153     
7154     
7155     getSelectionModel : function(){
7156         if(!this.selModel){
7157             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7158         }
7159         return this.selModel;
7160     },
7161     /*
7162      * Render the Roo.bootstrap object from renderder
7163      */
7164     renderCellObject : function(r)
7165     {
7166         var _this = this;
7167         
7168         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7169         
7170         var t = r.cfg.render(r.container);
7171         
7172         if(r.cfg.cn){
7173             Roo.each(r.cfg.cn, function(c){
7174                 var child = {
7175                     container: t.getChildContainer(),
7176                     cfg: c
7177                 };
7178                 _this.renderCellObject(child);
7179             })
7180         }
7181     },
7182     
7183     getRowIndex : function(row)
7184     {
7185         var rowIndex = -1;
7186         
7187         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7188             if(el != row){
7189                 return;
7190             }
7191             
7192             rowIndex = index;
7193         });
7194         
7195         return rowIndex;
7196     },
7197      /**
7198      * Returns the grid's underlying element = used by panel.Grid
7199      * @return {Element} The element
7200      */
7201     getGridEl : function(){
7202         return this.el;
7203     },
7204      /**
7205      * Forces a resize - used by panel.Grid
7206      * @return {Element} The element
7207      */
7208     autoSize : function()
7209     {
7210         //var ctr = Roo.get(this.container.dom.parentElement);
7211         var ctr = Roo.get(this.el.dom);
7212         
7213         var thd = this.getGridEl().select('thead',true).first();
7214         var tbd = this.getGridEl().select('tbody', true).first();
7215         var tfd = this.getGridEl().select('tfoot', true).first();
7216         
7217         var cw = ctr.getWidth();
7218         
7219         if (tbd) {
7220             
7221             tbd.setSize(ctr.getWidth(),
7222                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7223             );
7224             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7225             cw -= barsize;
7226         }
7227         cw = Math.max(cw, this.totalWidth);
7228         this.getGridEl().select('tr',true).setWidth(cw);
7229         // resize 'expandable coloumn?
7230         
7231         return; // we doe not have a view in this design..
7232         
7233     },
7234     onBodyScroll: function()
7235     {
7236         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7237         if(this.mainHead){
7238             this.mainHead.setStyle({
7239                 'position' : 'relative',
7240                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7241             });
7242         }
7243         
7244         if(this.lazyLoad){
7245             
7246             var scrollHeight = this.mainBody.dom.scrollHeight;
7247             
7248             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7249             
7250             var height = this.mainBody.getHeight();
7251             
7252             if(scrollHeight - height == scrollTop) {
7253                 
7254                 var total = this.ds.getTotalCount();
7255                 
7256                 if(this.footer.cursor + this.footer.pageSize < total){
7257                     
7258                     this.footer.ds.load({
7259                         params : {
7260                             start : this.footer.cursor + this.footer.pageSize,
7261                             limit : this.footer.pageSize
7262                         },
7263                         add : true
7264                     });
7265                 }
7266             }
7267             
7268         }
7269     },
7270     
7271     onHeaderChange : function()
7272     {
7273         var header = this.renderHeader();
7274         var table = this.el.select('table', true).first();
7275         
7276         this.mainHead.remove();
7277         this.mainHead = table.createChild(header, this.mainBody, false);
7278     },
7279     
7280     onHiddenChange : function(colModel, colIndex, hidden)
7281     {
7282         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7283         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7284         
7285         this.CSS.updateRule(thSelector, "display", "");
7286         this.CSS.updateRule(tdSelector, "display", "");
7287         
7288         if(hidden){
7289             this.CSS.updateRule(thSelector, "display", "none");
7290             this.CSS.updateRule(tdSelector, "display", "none");
7291         }
7292         
7293         this.onHeaderChange();
7294         this.onLoad();
7295     },
7296     
7297     setColumnWidth: function(col_index, width)
7298     {
7299         // width = "md-2 xs-2..."
7300         if(!this.colModel.config[col_index]) {
7301             return;
7302         }
7303         
7304         var w = width.split(" ");
7305         
7306         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7307         
7308         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7309         
7310         
7311         for(var j = 0; j < w.length; j++) {
7312             
7313             if(!w[j]) {
7314                 continue;
7315             }
7316             
7317             var size_cls = w[j].split("-");
7318             
7319             if(!Number.isInteger(size_cls[1] * 1)) {
7320                 continue;
7321             }
7322             
7323             if(!this.colModel.config[col_index][size_cls[0]]) {
7324                 continue;
7325             }
7326             
7327             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7328                 continue;
7329             }
7330             
7331             h_row[0].classList.replace(
7332                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7333                 "col-"+size_cls[0]+"-"+size_cls[1]
7334             );
7335             
7336             for(var i = 0; i < rows.length; i++) {
7337                 
7338                 var size_cls = w[j].split("-");
7339                 
7340                 if(!Number.isInteger(size_cls[1] * 1)) {
7341                     continue;
7342                 }
7343                 
7344                 if(!this.colModel.config[col_index][size_cls[0]]) {
7345                     continue;
7346                 }
7347                 
7348                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7349                     continue;
7350                 }
7351                 
7352                 rows[i].classList.replace(
7353                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7354                     "col-"+size_cls[0]+"-"+size_cls[1]
7355                 );
7356             }
7357             
7358             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7359         }
7360     }
7361 });
7362
7363  
7364
7365  /*
7366  * - LGPL
7367  *
7368  * table cell
7369  * 
7370  */
7371
7372 /**
7373  * @class Roo.bootstrap.TableCell
7374  * @extends Roo.bootstrap.Component
7375  * Bootstrap TableCell class
7376  * @cfg {String} html cell contain text
7377  * @cfg {String} cls cell class
7378  * @cfg {String} tag cell tag (td|th) default td
7379  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7380  * @cfg {String} align Aligns the content in a cell
7381  * @cfg {String} axis Categorizes cells
7382  * @cfg {String} bgcolor Specifies the background color of a cell
7383  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7384  * @cfg {Number} colspan Specifies the number of columns a cell should span
7385  * @cfg {String} headers Specifies one or more header cells a cell is related to
7386  * @cfg {Number} height Sets the height of a cell
7387  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7388  * @cfg {Number} rowspan Sets the number of rows a cell should span
7389  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7390  * @cfg {String} valign Vertical aligns the content in a cell
7391  * @cfg {Number} width Specifies the width of a cell
7392  * 
7393  * @constructor
7394  * Create a new TableCell
7395  * @param {Object} config The config object
7396  */
7397
7398 Roo.bootstrap.TableCell = function(config){
7399     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7400 };
7401
7402 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7403     
7404     html: false,
7405     cls: false,
7406     tag: false,
7407     abbr: false,
7408     align: false,
7409     axis: false,
7410     bgcolor: false,
7411     charoff: false,
7412     colspan: false,
7413     headers: false,
7414     height: false,
7415     nowrap: false,
7416     rowspan: false,
7417     scope: false,
7418     valign: false,
7419     width: false,
7420     
7421     
7422     getAutoCreate : function(){
7423         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7424         
7425         cfg = {
7426             tag: 'td'
7427         };
7428         
7429         if(this.tag){
7430             cfg.tag = this.tag;
7431         }
7432         
7433         if (this.html) {
7434             cfg.html=this.html
7435         }
7436         if (this.cls) {
7437             cfg.cls=this.cls
7438         }
7439         if (this.abbr) {
7440             cfg.abbr=this.abbr
7441         }
7442         if (this.align) {
7443             cfg.align=this.align
7444         }
7445         if (this.axis) {
7446             cfg.axis=this.axis
7447         }
7448         if (this.bgcolor) {
7449             cfg.bgcolor=this.bgcolor
7450         }
7451         if (this.charoff) {
7452             cfg.charoff=this.charoff
7453         }
7454         if (this.colspan) {
7455             cfg.colspan=this.colspan
7456         }
7457         if (this.headers) {
7458             cfg.headers=this.headers
7459         }
7460         if (this.height) {
7461             cfg.height=this.height
7462         }
7463         if (this.nowrap) {
7464             cfg.nowrap=this.nowrap
7465         }
7466         if (this.rowspan) {
7467             cfg.rowspan=this.rowspan
7468         }
7469         if (this.scope) {
7470             cfg.scope=this.scope
7471         }
7472         if (this.valign) {
7473             cfg.valign=this.valign
7474         }
7475         if (this.width) {
7476             cfg.width=this.width
7477         }
7478         
7479         
7480         return cfg;
7481     }
7482    
7483 });
7484
7485  
7486
7487  /*
7488  * - LGPL
7489  *
7490  * table row
7491  * 
7492  */
7493
7494 /**
7495  * @class Roo.bootstrap.TableRow
7496  * @extends Roo.bootstrap.Component
7497  * Bootstrap TableRow class
7498  * @cfg {String} cls row class
7499  * @cfg {String} align Aligns the content in a table row
7500  * @cfg {String} bgcolor Specifies a background color for a table row
7501  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7502  * @cfg {String} valign Vertical aligns the content in a table row
7503  * 
7504  * @constructor
7505  * Create a new TableRow
7506  * @param {Object} config The config object
7507  */
7508
7509 Roo.bootstrap.TableRow = function(config){
7510     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7511 };
7512
7513 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7514     
7515     cls: false,
7516     align: false,
7517     bgcolor: false,
7518     charoff: false,
7519     valign: false,
7520     
7521     getAutoCreate : function(){
7522         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7523         
7524         cfg = {
7525             tag: 'tr'
7526         };
7527             
7528         if(this.cls){
7529             cfg.cls = this.cls;
7530         }
7531         if(this.align){
7532             cfg.align = this.align;
7533         }
7534         if(this.bgcolor){
7535             cfg.bgcolor = this.bgcolor;
7536         }
7537         if(this.charoff){
7538             cfg.charoff = this.charoff;
7539         }
7540         if(this.valign){
7541             cfg.valign = this.valign;
7542         }
7543         
7544         return cfg;
7545     }
7546    
7547 });
7548
7549  
7550
7551  /*
7552  * - LGPL
7553  *
7554  * table body
7555  * 
7556  */
7557
7558 /**
7559  * @class Roo.bootstrap.TableBody
7560  * @extends Roo.bootstrap.Component
7561  * Bootstrap TableBody class
7562  * @cfg {String} cls element class
7563  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7564  * @cfg {String} align Aligns the content inside the element
7565  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7566  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7567  * 
7568  * @constructor
7569  * Create a new TableBody
7570  * @param {Object} config The config object
7571  */
7572
7573 Roo.bootstrap.TableBody = function(config){
7574     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7575 };
7576
7577 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7578     
7579     cls: false,
7580     tag: false,
7581     align: false,
7582     charoff: false,
7583     valign: false,
7584     
7585     getAutoCreate : function(){
7586         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7587         
7588         cfg = {
7589             tag: 'tbody'
7590         };
7591             
7592         if (this.cls) {
7593             cfg.cls=this.cls
7594         }
7595         if(this.tag){
7596             cfg.tag = this.tag;
7597         }
7598         
7599         if(this.align){
7600             cfg.align = this.align;
7601         }
7602         if(this.charoff){
7603             cfg.charoff = this.charoff;
7604         }
7605         if(this.valign){
7606             cfg.valign = this.valign;
7607         }
7608         
7609         return cfg;
7610     }
7611     
7612     
7613 //    initEvents : function()
7614 //    {
7615 //        
7616 //        if(!this.store){
7617 //            return;
7618 //        }
7619 //        
7620 //        this.store = Roo.factory(this.store, Roo.data);
7621 //        this.store.on('load', this.onLoad, this);
7622 //        
7623 //        this.store.load();
7624 //        
7625 //    },
7626 //    
7627 //    onLoad: function () 
7628 //    {   
7629 //        this.fireEvent('load', this);
7630 //    }
7631 //    
7632 //   
7633 });
7634
7635  
7636
7637  /*
7638  * Based on:
7639  * Ext JS Library 1.1.1
7640  * Copyright(c) 2006-2007, Ext JS, LLC.
7641  *
7642  * Originally Released Under LGPL - original licence link has changed is not relivant.
7643  *
7644  * Fork - LGPL
7645  * <script type="text/javascript">
7646  */
7647
7648 // as we use this in bootstrap.
7649 Roo.namespace('Roo.form');
7650  /**
7651  * @class Roo.form.Action
7652  * Internal Class used to handle form actions
7653  * @constructor
7654  * @param {Roo.form.BasicForm} el The form element or its id
7655  * @param {Object} config Configuration options
7656  */
7657
7658  
7659  
7660 // define the action interface
7661 Roo.form.Action = function(form, options){
7662     this.form = form;
7663     this.options = options || {};
7664 };
7665 /**
7666  * Client Validation Failed
7667  * @const 
7668  */
7669 Roo.form.Action.CLIENT_INVALID = 'client';
7670 /**
7671  * Server Validation Failed
7672  * @const 
7673  */
7674 Roo.form.Action.SERVER_INVALID = 'server';
7675  /**
7676  * Connect to Server Failed
7677  * @const 
7678  */
7679 Roo.form.Action.CONNECT_FAILURE = 'connect';
7680 /**
7681  * Reading Data from Server Failed
7682  * @const 
7683  */
7684 Roo.form.Action.LOAD_FAILURE = 'load';
7685
7686 Roo.form.Action.prototype = {
7687     type : 'default',
7688     failureType : undefined,
7689     response : undefined,
7690     result : undefined,
7691
7692     // interface method
7693     run : function(options){
7694
7695     },
7696
7697     // interface method
7698     success : function(response){
7699
7700     },
7701
7702     // interface method
7703     handleResponse : function(response){
7704
7705     },
7706
7707     // default connection failure
7708     failure : function(response){
7709         
7710         this.response = response;
7711         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7712         this.form.afterAction(this, false);
7713     },
7714
7715     processResponse : function(response){
7716         this.response = response;
7717         if(!response.responseText){
7718             return true;
7719         }
7720         this.result = this.handleResponse(response);
7721         return this.result;
7722     },
7723
7724     // utility functions used internally
7725     getUrl : function(appendParams){
7726         var url = this.options.url || this.form.url || this.form.el.dom.action;
7727         if(appendParams){
7728             var p = this.getParams();
7729             if(p){
7730                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7731             }
7732         }
7733         return url;
7734     },
7735
7736     getMethod : function(){
7737         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7738     },
7739
7740     getParams : function(){
7741         var bp = this.form.baseParams;
7742         var p = this.options.params;
7743         if(p){
7744             if(typeof p == "object"){
7745                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7746             }else if(typeof p == 'string' && bp){
7747                 p += '&' + Roo.urlEncode(bp);
7748             }
7749         }else if(bp){
7750             p = Roo.urlEncode(bp);
7751         }
7752         return p;
7753     },
7754
7755     createCallback : function(){
7756         return {
7757             success: this.success,
7758             failure: this.failure,
7759             scope: this,
7760             timeout: (this.form.timeout*1000),
7761             upload: this.form.fileUpload ? this.success : undefined
7762         };
7763     }
7764 };
7765
7766 Roo.form.Action.Submit = function(form, options){
7767     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7768 };
7769
7770 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7771     type : 'submit',
7772
7773     haveProgress : false,
7774     uploadComplete : false,
7775     
7776     // uploadProgress indicator.
7777     uploadProgress : function()
7778     {
7779         if (!this.form.progressUrl) {
7780             return;
7781         }
7782         
7783         if (!this.haveProgress) {
7784             Roo.MessageBox.progress("Uploading", "Uploading");
7785         }
7786         if (this.uploadComplete) {
7787            Roo.MessageBox.hide();
7788            return;
7789         }
7790         
7791         this.haveProgress = true;
7792    
7793         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7794         
7795         var c = new Roo.data.Connection();
7796         c.request({
7797             url : this.form.progressUrl,
7798             params: {
7799                 id : uid
7800             },
7801             method: 'GET',
7802             success : function(req){
7803                //console.log(data);
7804                 var rdata = false;
7805                 var edata;
7806                 try  {
7807                    rdata = Roo.decode(req.responseText)
7808                 } catch (e) {
7809                     Roo.log("Invalid data from server..");
7810                     Roo.log(edata);
7811                     return;
7812                 }
7813                 if (!rdata || !rdata.success) {
7814                     Roo.log(rdata);
7815                     Roo.MessageBox.alert(Roo.encode(rdata));
7816                     return;
7817                 }
7818                 var data = rdata.data;
7819                 
7820                 if (this.uploadComplete) {
7821                    Roo.MessageBox.hide();
7822                    return;
7823                 }
7824                    
7825                 if (data){
7826                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7827                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7828                     );
7829                 }
7830                 this.uploadProgress.defer(2000,this);
7831             },
7832        
7833             failure: function(data) {
7834                 Roo.log('progress url failed ');
7835                 Roo.log(data);
7836             },
7837             scope : this
7838         });
7839            
7840     },
7841     
7842     
7843     run : function()
7844     {
7845         // run get Values on the form, so it syncs any secondary forms.
7846         this.form.getValues();
7847         
7848         var o = this.options;
7849         var method = this.getMethod();
7850         var isPost = method == 'POST';
7851         if(o.clientValidation === false || this.form.isValid()){
7852             
7853             if (this.form.progressUrl) {
7854                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7855                     (new Date() * 1) + '' + Math.random());
7856                     
7857             } 
7858             
7859             
7860             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7861                 form:this.form.el.dom,
7862                 url:this.getUrl(!isPost),
7863                 method: method,
7864                 params:isPost ? this.getParams() : null,
7865                 isUpload: this.form.fileUpload
7866             }));
7867             
7868             this.uploadProgress();
7869
7870         }else if (o.clientValidation !== false){ // client validation failed
7871             this.failureType = Roo.form.Action.CLIENT_INVALID;
7872             this.form.afterAction(this, false);
7873         }
7874     },
7875
7876     success : function(response)
7877     {
7878         this.uploadComplete= true;
7879         if (this.haveProgress) {
7880             Roo.MessageBox.hide();
7881         }
7882         
7883         
7884         var result = this.processResponse(response);
7885         if(result === true || result.success){
7886             this.form.afterAction(this, true);
7887             return;
7888         }
7889         if(result.errors){
7890             this.form.markInvalid(result.errors);
7891             this.failureType = Roo.form.Action.SERVER_INVALID;
7892         }
7893         this.form.afterAction(this, false);
7894     },
7895     failure : function(response)
7896     {
7897         this.uploadComplete= true;
7898         if (this.haveProgress) {
7899             Roo.MessageBox.hide();
7900         }
7901         
7902         this.response = response;
7903         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7904         this.form.afterAction(this, false);
7905     },
7906     
7907     handleResponse : function(response){
7908         if(this.form.errorReader){
7909             var rs = this.form.errorReader.read(response);
7910             var errors = [];
7911             if(rs.records){
7912                 for(var i = 0, len = rs.records.length; i < len; i++) {
7913                     var r = rs.records[i];
7914                     errors[i] = r.data;
7915                 }
7916             }
7917             if(errors.length < 1){
7918                 errors = null;
7919             }
7920             return {
7921                 success : rs.success,
7922                 errors : errors
7923             };
7924         }
7925         var ret = false;
7926         try {
7927             ret = Roo.decode(response.responseText);
7928         } catch (e) {
7929             ret = {
7930                 success: false,
7931                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7932                 errors : []
7933             };
7934         }
7935         return ret;
7936         
7937     }
7938 });
7939
7940
7941 Roo.form.Action.Load = function(form, options){
7942     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7943     this.reader = this.form.reader;
7944 };
7945
7946 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7947     type : 'load',
7948
7949     run : function(){
7950         
7951         Roo.Ajax.request(Roo.apply(
7952                 this.createCallback(), {
7953                     method:this.getMethod(),
7954                     url:this.getUrl(false),
7955                     params:this.getParams()
7956         }));
7957     },
7958
7959     success : function(response){
7960         
7961         var result = this.processResponse(response);
7962         if(result === true || !result.success || !result.data){
7963             this.failureType = Roo.form.Action.LOAD_FAILURE;
7964             this.form.afterAction(this, false);
7965             return;
7966         }
7967         this.form.clearInvalid();
7968         this.form.setValues(result.data);
7969         this.form.afterAction(this, true);
7970     },
7971
7972     handleResponse : function(response){
7973         if(this.form.reader){
7974             var rs = this.form.reader.read(response);
7975             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7976             return {
7977                 success : rs.success,
7978                 data : data
7979             };
7980         }
7981         return Roo.decode(response.responseText);
7982     }
7983 });
7984
7985 Roo.form.Action.ACTION_TYPES = {
7986     'load' : Roo.form.Action.Load,
7987     'submit' : Roo.form.Action.Submit
7988 };/*
7989  * - LGPL
7990  *
7991  * form
7992  *
7993  */
7994
7995 /**
7996  * @class Roo.bootstrap.Form
7997  * @extends Roo.bootstrap.Component
7998  * Bootstrap Form class
7999  * @cfg {String} method  GET | POST (default POST)
8000  * @cfg {String} labelAlign top | left (default top)
8001  * @cfg {String} align left  | right - for navbars
8002  * @cfg {Boolean} loadMask load mask when submit (default true)
8003
8004  *
8005  * @constructor
8006  * Create a new Form
8007  * @param {Object} config The config object
8008  */
8009
8010
8011 Roo.bootstrap.Form = function(config){
8012     
8013     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8014     
8015     Roo.bootstrap.Form.popover.apply();
8016     
8017     this.addEvents({
8018         /**
8019          * @event clientvalidation
8020          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8021          * @param {Form} this
8022          * @param {Boolean} valid true if the form has passed client-side validation
8023          */
8024         clientvalidation: true,
8025         /**
8026          * @event beforeaction
8027          * Fires before any action is performed. Return false to cancel the action.
8028          * @param {Form} this
8029          * @param {Action} action The action to be performed
8030          */
8031         beforeaction: true,
8032         /**
8033          * @event actionfailed
8034          * Fires when an action fails.
8035          * @param {Form} this
8036          * @param {Action} action The action that failed
8037          */
8038         actionfailed : true,
8039         /**
8040          * @event actioncomplete
8041          * Fires when an action is completed.
8042          * @param {Form} this
8043          * @param {Action} action The action that completed
8044          */
8045         actioncomplete : true
8046     });
8047 };
8048
8049 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8050
8051      /**
8052      * @cfg {String} method
8053      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8054      */
8055     method : 'POST',
8056     /**
8057      * @cfg {String} url
8058      * The URL to use for form actions if one isn't supplied in the action options.
8059      */
8060     /**
8061      * @cfg {Boolean} fileUpload
8062      * Set to true if this form is a file upload.
8063      */
8064
8065     /**
8066      * @cfg {Object} baseParams
8067      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8068      */
8069
8070     /**
8071      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8072      */
8073     timeout: 30,
8074     /**
8075      * @cfg {Sting} align (left|right) for navbar forms
8076      */
8077     align : 'left',
8078
8079     // private
8080     activeAction : null,
8081
8082     /**
8083      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8084      * element by passing it or its id or mask the form itself by passing in true.
8085      * @type Mixed
8086      */
8087     waitMsgTarget : false,
8088
8089     loadMask : true,
8090     
8091     /**
8092      * @cfg {Boolean} errorMask (true|false) default false
8093      */
8094     errorMask : false,
8095     
8096     /**
8097      * @cfg {Number} maskOffset Default 100
8098      */
8099     maskOffset : 100,
8100     
8101     /**
8102      * @cfg {Boolean} maskBody
8103      */
8104     maskBody : false,
8105
8106     getAutoCreate : function(){
8107
8108         var cfg = {
8109             tag: 'form',
8110             method : this.method || 'POST',
8111             id : this.id || Roo.id(),
8112             cls : ''
8113         };
8114         if (this.parent().xtype.match(/^Nav/)) {
8115             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8116
8117         }
8118
8119         if (this.labelAlign == 'left' ) {
8120             cfg.cls += ' form-horizontal';
8121         }
8122
8123
8124         return cfg;
8125     },
8126     initEvents : function()
8127     {
8128         this.el.on('submit', this.onSubmit, this);
8129         // this was added as random key presses on the form where triggering form submit.
8130         this.el.on('keypress', function(e) {
8131             if (e.getCharCode() != 13) {
8132                 return true;
8133             }
8134             // we might need to allow it for textareas.. and some other items.
8135             // check e.getTarget().
8136
8137             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8138                 return true;
8139             }
8140
8141             Roo.log("keypress blocked");
8142
8143             e.preventDefault();
8144             return false;
8145         });
8146         
8147     },
8148     // private
8149     onSubmit : function(e){
8150         e.stopEvent();
8151     },
8152
8153      /**
8154      * Returns true if client-side validation on the form is successful.
8155      * @return Boolean
8156      */
8157     isValid : function(){
8158         var items = this.getItems();
8159         var valid = true;
8160         var target = false;
8161         
8162         items.each(function(f){
8163             
8164             if(f.validate()){
8165                 return;
8166             }
8167             
8168             Roo.log('invalid field: ' + f.name);
8169             
8170             valid = false;
8171
8172             if(!target && f.el.isVisible(true)){
8173                 target = f;
8174             }
8175            
8176         });
8177         
8178         if(this.errorMask && !valid){
8179             Roo.bootstrap.Form.popover.mask(this, target);
8180         }
8181         
8182         return valid;
8183     },
8184     
8185     /**
8186      * Returns true if any fields in this form have changed since their original load.
8187      * @return Boolean
8188      */
8189     isDirty : function(){
8190         var dirty = false;
8191         var items = this.getItems();
8192         items.each(function(f){
8193            if(f.isDirty()){
8194                dirty = true;
8195                return false;
8196            }
8197            return true;
8198         });
8199         return dirty;
8200     },
8201      /**
8202      * Performs a predefined action (submit or load) or custom actions you define on this form.
8203      * @param {String} actionName The name of the action type
8204      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8205      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8206      * accept other config options):
8207      * <pre>
8208 Property          Type             Description
8209 ----------------  ---------------  ----------------------------------------------------------------------------------
8210 url               String           The url for the action (defaults to the form's url)
8211 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8212 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8213 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8214                                    validate the form on the client (defaults to false)
8215      * </pre>
8216      * @return {BasicForm} this
8217      */
8218     doAction : function(action, options){
8219         if(typeof action == 'string'){
8220             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8221         }
8222         if(this.fireEvent('beforeaction', this, action) !== false){
8223             this.beforeAction(action);
8224             action.run.defer(100, action);
8225         }
8226         return this;
8227     },
8228
8229     // private
8230     beforeAction : function(action){
8231         var o = action.options;
8232         
8233         if(this.loadMask){
8234             
8235             if(this.maskBody){
8236                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8237             } else {
8238                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8239             }
8240         }
8241         // not really supported yet.. ??
8242
8243         //if(this.waitMsgTarget === true){
8244         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8245         //}else if(this.waitMsgTarget){
8246         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8247         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8248         //}else {
8249         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8250        // }
8251
8252     },
8253
8254     // private
8255     afterAction : function(action, success){
8256         this.activeAction = null;
8257         var o = action.options;
8258
8259         if(this.loadMask){
8260             
8261             if(this.maskBody){
8262                 Roo.get(document.body).unmask();
8263             } else {
8264                 this.el.unmask();
8265             }
8266         }
8267         
8268         //if(this.waitMsgTarget === true){
8269 //            this.el.unmask();
8270         //}else if(this.waitMsgTarget){
8271         //    this.waitMsgTarget.unmask();
8272         //}else{
8273         //    Roo.MessageBox.updateProgress(1);
8274         //    Roo.MessageBox.hide();
8275        // }
8276         //
8277         if(success){
8278             if(o.reset){
8279                 this.reset();
8280             }
8281             Roo.callback(o.success, o.scope, [this, action]);
8282             this.fireEvent('actioncomplete', this, action);
8283
8284         }else{
8285
8286             // failure condition..
8287             // we have a scenario where updates need confirming.
8288             // eg. if a locking scenario exists..
8289             // we look for { errors : { needs_confirm : true }} in the response.
8290             if (
8291                 (typeof(action.result) != 'undefined')  &&
8292                 (typeof(action.result.errors) != 'undefined')  &&
8293                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8294            ){
8295                 var _t = this;
8296                 Roo.log("not supported yet");
8297                  /*
8298
8299                 Roo.MessageBox.confirm(
8300                     "Change requires confirmation",
8301                     action.result.errorMsg,
8302                     function(r) {
8303                         if (r != 'yes') {
8304                             return;
8305                         }
8306                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8307                     }
8308
8309                 );
8310                 */
8311
8312
8313                 return;
8314             }
8315
8316             Roo.callback(o.failure, o.scope, [this, action]);
8317             // show an error message if no failed handler is set..
8318             if (!this.hasListener('actionfailed')) {
8319                 Roo.log("need to add dialog support");
8320                 /*
8321                 Roo.MessageBox.alert("Error",
8322                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8323                         action.result.errorMsg :
8324                         "Saving Failed, please check your entries or try again"
8325                 );
8326                 */
8327             }
8328
8329             this.fireEvent('actionfailed', this, action);
8330         }
8331
8332     },
8333     /**
8334      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8335      * @param {String} id The value to search for
8336      * @return Field
8337      */
8338     findField : function(id){
8339         var items = this.getItems();
8340         var field = items.get(id);
8341         if(!field){
8342              items.each(function(f){
8343                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8344                     field = f;
8345                     return false;
8346                 }
8347                 return true;
8348             });
8349         }
8350         return field || null;
8351     },
8352      /**
8353      * Mark fields in this form invalid in bulk.
8354      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8355      * @return {BasicForm} this
8356      */
8357     markInvalid : function(errors){
8358         if(errors instanceof Array){
8359             for(var i = 0, len = errors.length; i < len; i++){
8360                 var fieldError = errors[i];
8361                 var f = this.findField(fieldError.id);
8362                 if(f){
8363                     f.markInvalid(fieldError.msg);
8364                 }
8365             }
8366         }else{
8367             var field, id;
8368             for(id in errors){
8369                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8370                     field.markInvalid(errors[id]);
8371                 }
8372             }
8373         }
8374         //Roo.each(this.childForms || [], function (f) {
8375         //    f.markInvalid(errors);
8376         //});
8377
8378         return this;
8379     },
8380
8381     /**
8382      * Set values for fields in this form in bulk.
8383      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8384      * @return {BasicForm} this
8385      */
8386     setValues : function(values){
8387         if(values instanceof Array){ // array of objects
8388             for(var i = 0, len = values.length; i < len; i++){
8389                 var v = values[i];
8390                 var f = this.findField(v.id);
8391                 if(f){
8392                     f.setValue(v.value);
8393                     if(this.trackResetOnLoad){
8394                         f.originalValue = f.getValue();
8395                     }
8396                 }
8397             }
8398         }else{ // object hash
8399             var field, id;
8400             for(id in values){
8401                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8402
8403                     if (field.setFromData &&
8404                         field.valueField &&
8405                         field.displayField &&
8406                         // combos' with local stores can
8407                         // be queried via setValue()
8408                         // to set their value..
8409                         (field.store && !field.store.isLocal)
8410                         ) {
8411                         // it's a combo
8412                         var sd = { };
8413                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8414                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8415                         field.setFromData(sd);
8416
8417                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8418                         
8419                         field.setFromData(values);
8420                         
8421                     } else {
8422                         field.setValue(values[id]);
8423                     }
8424
8425
8426                     if(this.trackResetOnLoad){
8427                         field.originalValue = field.getValue();
8428                     }
8429                 }
8430             }
8431         }
8432
8433         //Roo.each(this.childForms || [], function (f) {
8434         //    f.setValues(values);
8435         //});
8436
8437         return this;
8438     },
8439
8440     /**
8441      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8442      * they are returned as an array.
8443      * @param {Boolean} asString
8444      * @return {Object}
8445      */
8446     getValues : function(asString){
8447         //if (this.childForms) {
8448             // copy values from the child forms
8449         //    Roo.each(this.childForms, function (f) {
8450         //        this.setValues(f.getValues());
8451         //    }, this);
8452         //}
8453
8454
8455
8456         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8457         if(asString === true){
8458             return fs;
8459         }
8460         return Roo.urlDecode(fs);
8461     },
8462
8463     /**
8464      * Returns the fields in this form as an object with key/value pairs.
8465      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8466      * @return {Object}
8467      */
8468     getFieldValues : function(with_hidden)
8469     {
8470         var items = this.getItems();
8471         var ret = {};
8472         items.each(function(f){
8473             
8474             if (!f.getName()) {
8475                 return;
8476             }
8477             
8478             var v = f.getValue();
8479             
8480             if (f.inputType =='radio') {
8481                 if (typeof(ret[f.getName()]) == 'undefined') {
8482                     ret[f.getName()] = ''; // empty..
8483                 }
8484
8485                 if (!f.el.dom.checked) {
8486                     return;
8487
8488                 }
8489                 v = f.el.dom.value;
8490
8491             }
8492             
8493             if(f.xtype == 'MoneyField'){
8494                 ret[f.currencyName] = f.getCurrency();
8495             }
8496
8497             // not sure if this supported any more..
8498             if ((typeof(v) == 'object') && f.getRawValue) {
8499                 v = f.getRawValue() ; // dates..
8500             }
8501             // combo boxes where name != hiddenName...
8502             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8503                 ret[f.name] = f.getRawValue();
8504             }
8505             ret[f.getName()] = v;
8506         });
8507
8508         return ret;
8509     },
8510
8511     /**
8512      * Clears all invalid messages in this form.
8513      * @return {BasicForm} this
8514      */
8515     clearInvalid : function(){
8516         var items = this.getItems();
8517
8518         items.each(function(f){
8519            f.clearInvalid();
8520         });
8521
8522         return this;
8523     },
8524
8525     /**
8526      * Resets this form.
8527      * @return {BasicForm} this
8528      */
8529     reset : function(){
8530         var items = this.getItems();
8531         items.each(function(f){
8532             f.reset();
8533         });
8534
8535         Roo.each(this.childForms || [], function (f) {
8536             f.reset();
8537         });
8538
8539
8540         return this;
8541     },
8542     
8543     getItems : function()
8544     {
8545         var r=new Roo.util.MixedCollection(false, function(o){
8546             return o.id || (o.id = Roo.id());
8547         });
8548         var iter = function(el) {
8549             if (el.inputEl) {
8550                 r.add(el);
8551             }
8552             if (!el.items) {
8553                 return;
8554             }
8555             Roo.each(el.items,function(e) {
8556                 iter(e);
8557             });
8558         };
8559
8560         iter(this);
8561         return r;
8562     },
8563     
8564     hideFields : function(items)
8565     {
8566         Roo.each(items, function(i){
8567             
8568             var f = this.findField(i);
8569             
8570             if(!f){
8571                 return;
8572             }
8573             
8574             f.hide();
8575             
8576         }, this);
8577     },
8578     
8579     showFields : function(items)
8580     {
8581         Roo.each(items, function(i){
8582             
8583             var f = this.findField(i);
8584             
8585             if(!f){
8586                 return;
8587             }
8588             
8589             f.show();
8590             
8591         }, this);
8592     }
8593
8594 });
8595
8596 Roo.apply(Roo.bootstrap.Form, {
8597     
8598     popover : {
8599         
8600         padding : 5,
8601         
8602         isApplied : false,
8603         
8604         isMasked : false,
8605         
8606         form : false,
8607         
8608         target : false,
8609         
8610         toolTip : false,
8611         
8612         intervalID : false,
8613         
8614         maskEl : false,
8615         
8616         apply : function()
8617         {
8618             if(this.isApplied){
8619                 return;
8620             }
8621             
8622             this.maskEl = {
8623                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8624                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8625                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8626                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8627             };
8628             
8629             this.maskEl.top.enableDisplayMode("block");
8630             this.maskEl.left.enableDisplayMode("block");
8631             this.maskEl.bottom.enableDisplayMode("block");
8632             this.maskEl.right.enableDisplayMode("block");
8633             
8634             this.toolTip = new Roo.bootstrap.Tooltip({
8635                 cls : 'roo-form-error-popover',
8636                 alignment : {
8637                     'left' : ['r-l', [-2,0], 'right'],
8638                     'right' : ['l-r', [2,0], 'left'],
8639                     'bottom' : ['tl-bl', [0,2], 'top'],
8640                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8641                 }
8642             });
8643             
8644             this.toolTip.render(Roo.get(document.body));
8645
8646             this.toolTip.el.enableDisplayMode("block");
8647             
8648             Roo.get(document.body).on('click', function(){
8649                 this.unmask();
8650             }, this);
8651             
8652             Roo.get(document.body).on('touchstart', function(){
8653                 this.unmask();
8654             }, this);
8655             
8656             this.isApplied = true
8657         },
8658         
8659         mask : function(form, target)
8660         {
8661             this.form = form;
8662             
8663             this.target = target;
8664             
8665             if(!this.form.errorMask || !target.el){
8666                 return;
8667             }
8668             
8669             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8670             
8671             Roo.log(scrollable);
8672             
8673             var ot = this.target.el.calcOffsetsTo(scrollable);
8674             
8675             var scrollTo = ot[1] - this.form.maskOffset;
8676             
8677             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8678             
8679             scrollable.scrollTo('top', scrollTo);
8680             
8681             var box = this.target.el.getBox();
8682             Roo.log(box);
8683             var zIndex = Roo.bootstrap.Modal.zIndex++;
8684
8685             
8686             this.maskEl.top.setStyle('position', 'absolute');
8687             this.maskEl.top.setStyle('z-index', zIndex);
8688             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8689             this.maskEl.top.setLeft(0);
8690             this.maskEl.top.setTop(0);
8691             this.maskEl.top.show();
8692             
8693             this.maskEl.left.setStyle('position', 'absolute');
8694             this.maskEl.left.setStyle('z-index', zIndex);
8695             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8696             this.maskEl.left.setLeft(0);
8697             this.maskEl.left.setTop(box.y - this.padding);
8698             this.maskEl.left.show();
8699
8700             this.maskEl.bottom.setStyle('position', 'absolute');
8701             this.maskEl.bottom.setStyle('z-index', zIndex);
8702             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8703             this.maskEl.bottom.setLeft(0);
8704             this.maskEl.bottom.setTop(box.bottom + this.padding);
8705             this.maskEl.bottom.show();
8706
8707             this.maskEl.right.setStyle('position', 'absolute');
8708             this.maskEl.right.setStyle('z-index', zIndex);
8709             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8710             this.maskEl.right.setLeft(box.right + this.padding);
8711             this.maskEl.right.setTop(box.y - this.padding);
8712             this.maskEl.right.show();
8713
8714             this.toolTip.bindEl = this.target.el;
8715
8716             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8717
8718             var tip = this.target.blankText;
8719
8720             if(this.target.getValue() !== '' ) {
8721                 
8722                 if (this.target.invalidText.length) {
8723                     tip = this.target.invalidText;
8724                 } else if (this.target.regexText.length){
8725                     tip = this.target.regexText;
8726                 }
8727             }
8728
8729             this.toolTip.show(tip);
8730
8731             this.intervalID = window.setInterval(function() {
8732                 Roo.bootstrap.Form.popover.unmask();
8733             }, 10000);
8734
8735             window.onwheel = function(){ return false;};
8736             
8737             (function(){ this.isMasked = true; }).defer(500, this);
8738             
8739         },
8740         
8741         unmask : function()
8742         {
8743             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8744                 return;
8745             }
8746             
8747             this.maskEl.top.setStyle('position', 'absolute');
8748             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8749             this.maskEl.top.hide();
8750
8751             this.maskEl.left.setStyle('position', 'absolute');
8752             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8753             this.maskEl.left.hide();
8754
8755             this.maskEl.bottom.setStyle('position', 'absolute');
8756             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8757             this.maskEl.bottom.hide();
8758
8759             this.maskEl.right.setStyle('position', 'absolute');
8760             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8761             this.maskEl.right.hide();
8762             
8763             this.toolTip.hide();
8764             
8765             this.toolTip.el.hide();
8766             
8767             window.onwheel = function(){ return true;};
8768             
8769             if(this.intervalID){
8770                 window.clearInterval(this.intervalID);
8771                 this.intervalID = false;
8772             }
8773             
8774             this.isMasked = false;
8775             
8776         }
8777         
8778     }
8779     
8780 });
8781
8782 /*
8783  * Based on:
8784  * Ext JS Library 1.1.1
8785  * Copyright(c) 2006-2007, Ext JS, LLC.
8786  *
8787  * Originally Released Under LGPL - original licence link has changed is not relivant.
8788  *
8789  * Fork - LGPL
8790  * <script type="text/javascript">
8791  */
8792 /**
8793  * @class Roo.form.VTypes
8794  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8795  * @singleton
8796  */
8797 Roo.form.VTypes = function(){
8798     // closure these in so they are only created once.
8799     var alpha = /^[a-zA-Z_]+$/;
8800     var alphanum = /^[a-zA-Z0-9_]+$/;
8801     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8802     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8803
8804     // All these messages and functions are configurable
8805     return {
8806         /**
8807          * The function used to validate email addresses
8808          * @param {String} value The email address
8809          */
8810         'email' : function(v){
8811             return email.test(v);
8812         },
8813         /**
8814          * The error text to display when the email validation function returns false
8815          * @type String
8816          */
8817         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8818         /**
8819          * The keystroke filter mask to be applied on email input
8820          * @type RegExp
8821          */
8822         'emailMask' : /[a-z0-9_\.\-@]/i,
8823
8824         /**
8825          * The function used to validate URLs
8826          * @param {String} value The URL
8827          */
8828         'url' : function(v){
8829             return url.test(v);
8830         },
8831         /**
8832          * The error text to display when the url validation function returns false
8833          * @type String
8834          */
8835         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8836         
8837         /**
8838          * The function used to validate alpha values
8839          * @param {String} value The value
8840          */
8841         'alpha' : function(v){
8842             return alpha.test(v);
8843         },
8844         /**
8845          * The error text to display when the alpha validation function returns false
8846          * @type String
8847          */
8848         'alphaText' : 'This field should only contain letters and _',
8849         /**
8850          * The keystroke filter mask to be applied on alpha input
8851          * @type RegExp
8852          */
8853         'alphaMask' : /[a-z_]/i,
8854
8855         /**
8856          * The function used to validate alphanumeric values
8857          * @param {String} value The value
8858          */
8859         'alphanum' : function(v){
8860             return alphanum.test(v);
8861         },
8862         /**
8863          * The error text to display when the alphanumeric validation function returns false
8864          * @type String
8865          */
8866         'alphanumText' : 'This field should only contain letters, numbers and _',
8867         /**
8868          * The keystroke filter mask to be applied on alphanumeric input
8869          * @type RegExp
8870          */
8871         'alphanumMask' : /[a-z0-9_]/i
8872     };
8873 }();/*
8874  * - LGPL
8875  *
8876  * Input
8877  * 
8878  */
8879
8880 /**
8881  * @class Roo.bootstrap.Input
8882  * @extends Roo.bootstrap.Component
8883  * Bootstrap Input class
8884  * @cfg {Boolean} disabled is it disabled
8885  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8886  * @cfg {String} name name of the input
8887  * @cfg {string} fieldLabel - the label associated
8888  * @cfg {string} placeholder - placeholder to put in text.
8889  * @cfg {string}  before - input group add on before
8890  * @cfg {string} after - input group add on after
8891  * @cfg {string} size - (lg|sm) or leave empty..
8892  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8893  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8894  * @cfg {Number} md colspan out of 12 for computer-sized screens
8895  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8896  * @cfg {string} value default value of the input
8897  * @cfg {Number} labelWidth set the width of label 
8898  * @cfg {Number} labellg set the width of label (1-12)
8899  * @cfg {Number} labelmd set the width of label (1-12)
8900  * @cfg {Number} labelsm set the width of label (1-12)
8901  * @cfg {Number} labelxs set the width of label (1-12)
8902  * @cfg {String} labelAlign (top|left)
8903  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8904  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8905  * @cfg {String} indicatorpos (left|right) default left
8906  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8907  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8908
8909  * @cfg {String} align (left|center|right) Default left
8910  * @cfg {Boolean} forceFeedback (true|false) Default false
8911  * 
8912  * @constructor
8913  * Create a new Input
8914  * @param {Object} config The config object
8915  */
8916
8917 Roo.bootstrap.Input = function(config){
8918     
8919     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8920     
8921     this.addEvents({
8922         /**
8923          * @event focus
8924          * Fires when this field receives input focus.
8925          * @param {Roo.form.Field} this
8926          */
8927         focus : true,
8928         /**
8929          * @event blur
8930          * Fires when this field loses input focus.
8931          * @param {Roo.form.Field} this
8932          */
8933         blur : true,
8934         /**
8935          * @event specialkey
8936          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8937          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8938          * @param {Roo.form.Field} this
8939          * @param {Roo.EventObject} e The event object
8940          */
8941         specialkey : true,
8942         /**
8943          * @event change
8944          * Fires just before the field blurs if the field value has changed.
8945          * @param {Roo.form.Field} this
8946          * @param {Mixed} newValue The new value
8947          * @param {Mixed} oldValue The original value
8948          */
8949         change : true,
8950         /**
8951          * @event invalid
8952          * Fires after the field has been marked as invalid.
8953          * @param {Roo.form.Field} this
8954          * @param {String} msg The validation message
8955          */
8956         invalid : true,
8957         /**
8958          * @event valid
8959          * Fires after the field has been validated with no errors.
8960          * @param {Roo.form.Field} this
8961          */
8962         valid : true,
8963          /**
8964          * @event keyup
8965          * Fires after the key up
8966          * @param {Roo.form.Field} this
8967          * @param {Roo.EventObject}  e The event Object
8968          */
8969         keyup : true
8970     });
8971 };
8972
8973 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8974      /**
8975      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8976       automatic validation (defaults to "keyup").
8977      */
8978     validationEvent : "keyup",
8979      /**
8980      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8981      */
8982     validateOnBlur : true,
8983     /**
8984      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8985      */
8986     validationDelay : 250,
8987      /**
8988      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8989      */
8990     focusClass : "x-form-focus",  // not needed???
8991     
8992        
8993     /**
8994      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8995      */
8996     invalidClass : "has-warning",
8997     
8998     /**
8999      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
9000      */
9001     validClass : "has-success",
9002     
9003     /**
9004      * @cfg {Boolean} hasFeedback (true|false) default true
9005      */
9006     hasFeedback : true,
9007     
9008     /**
9009      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9010      */
9011     invalidFeedbackClass : "glyphicon-warning-sign",
9012     
9013     /**
9014      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9015      */
9016     validFeedbackClass : "glyphicon-ok",
9017     
9018     /**
9019      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9020      */
9021     selectOnFocus : false,
9022     
9023      /**
9024      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9025      */
9026     maskRe : null,
9027        /**
9028      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9029      */
9030     vtype : null,
9031     
9032       /**
9033      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9034      */
9035     disableKeyFilter : false,
9036     
9037        /**
9038      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9039      */
9040     disabled : false,
9041      /**
9042      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9043      */
9044     allowBlank : true,
9045     /**
9046      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9047      */
9048     blankText : "Please complete this mandatory field",
9049     
9050      /**
9051      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9052      */
9053     minLength : 0,
9054     /**
9055      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9056      */
9057     maxLength : Number.MAX_VALUE,
9058     /**
9059      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9060      */
9061     minLengthText : "The minimum length for this field is {0}",
9062     /**
9063      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9064      */
9065     maxLengthText : "The maximum length for this field is {0}",
9066   
9067     
9068     /**
9069      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9070      * If available, this function will be called only after the basic validators all return true, and will be passed the
9071      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9072      */
9073     validator : null,
9074     /**
9075      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9076      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9077      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9078      */
9079     regex : null,
9080     /**
9081      * @cfg {String} regexText -- Depricated - use Invalid Text
9082      */
9083     regexText : "",
9084     
9085     /**
9086      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9087      */
9088     invalidText : "",
9089     
9090     
9091     
9092     autocomplete: false,
9093     
9094     
9095     fieldLabel : '',
9096     inputType : 'text',
9097     
9098     name : false,
9099     placeholder: false,
9100     before : false,
9101     after : false,
9102     size : false,
9103     hasFocus : false,
9104     preventMark: false,
9105     isFormField : true,
9106     value : '',
9107     labelWidth : 2,
9108     labelAlign : false,
9109     readOnly : false,
9110     align : false,
9111     formatedValue : false,
9112     forceFeedback : false,
9113     
9114     indicatorpos : 'left',
9115     
9116     labellg : 0,
9117     labelmd : 0,
9118     labelsm : 0,
9119     labelxs : 0,
9120     
9121     capture : '',
9122     accept : '',
9123     
9124     parentLabelAlign : function()
9125     {
9126         var parent = this;
9127         while (parent.parent()) {
9128             parent = parent.parent();
9129             if (typeof(parent.labelAlign) !='undefined') {
9130                 return parent.labelAlign;
9131             }
9132         }
9133         return 'left';
9134         
9135     },
9136     
9137     getAutoCreate : function()
9138     {
9139         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9140         
9141         var id = Roo.id();
9142         
9143         var cfg = {};
9144         
9145         if(this.inputType != 'hidden'){
9146             cfg.cls = 'form-group' //input-group
9147         }
9148         
9149         var input =  {
9150             tag: 'input',
9151             id : id,
9152             type : this.inputType,
9153             value : this.value,
9154             cls : 'form-control',
9155             placeholder : this.placeholder || '',
9156             autocomplete : this.autocomplete || 'new-password'
9157         };
9158         
9159         if(this.capture.length){
9160             input.capture = this.capture;
9161         }
9162         
9163         if(this.accept.length){
9164             input.accept = this.accept + "/*";
9165         }
9166         
9167         if(this.align){
9168             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9169         }
9170         
9171         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9172             input.maxLength = this.maxLength;
9173         }
9174         
9175         if (this.disabled) {
9176             input.disabled=true;
9177         }
9178         
9179         if (this.readOnly) {
9180             input.readonly=true;
9181         }
9182         
9183         if (this.name) {
9184             input.name = this.name;
9185         }
9186         
9187         if (this.size) {
9188             input.cls += ' input-' + this.size;
9189         }
9190         
9191         var settings=this;
9192         ['xs','sm','md','lg'].map(function(size){
9193             if (settings[size]) {
9194                 cfg.cls += ' col-' + size + '-' + settings[size];
9195             }
9196         });
9197         
9198         var inputblock = input;
9199         
9200         var feedback = {
9201             tag: 'span',
9202             cls: 'glyphicon form-control-feedback'
9203         };
9204             
9205         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9206             
9207             inputblock = {
9208                 cls : 'has-feedback',
9209                 cn :  [
9210                     input,
9211                     feedback
9212                 ] 
9213             };  
9214         }
9215         
9216         if (this.before || this.after) {
9217             
9218             inputblock = {
9219                 cls : 'input-group',
9220                 cn :  [] 
9221             };
9222             
9223             if (this.before && typeof(this.before) == 'string') {
9224                 
9225                 inputblock.cn.push({
9226                     tag :'span',
9227                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9228                     html : this.before
9229                 });
9230             }
9231             if (this.before && typeof(this.before) == 'object') {
9232                 this.before = Roo.factory(this.before);
9233                 
9234                 inputblock.cn.push({
9235                     tag :'span',
9236                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9237                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9238                 });
9239             }
9240             
9241             inputblock.cn.push(input);
9242             
9243             if (this.after && typeof(this.after) == 'string') {
9244                 inputblock.cn.push({
9245                     tag :'span',
9246                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9247                     html : this.after
9248                 });
9249             }
9250             if (this.after && typeof(this.after) == 'object') {
9251                 this.after = Roo.factory(this.after);
9252                 
9253                 inputblock.cn.push({
9254                     tag :'span',
9255                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9256                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9257                 });
9258             }
9259             
9260             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9261                 inputblock.cls += ' has-feedback';
9262                 inputblock.cn.push(feedback);
9263             }
9264         };
9265         var indicator = {
9266             tag : 'i',
9267             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9268             tooltip : 'This field is required'
9269         };
9270         if (Roo.bootstrap.version == 4) {
9271             indicator = {
9272                 tag : 'i',
9273                 style : 'display-none'
9274             };
9275         }
9276         if (align ==='left' && this.fieldLabel.length) {
9277             
9278             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9279             
9280             cfg.cn = [
9281                 indicator,
9282                 {
9283                     tag: 'label',
9284                     'for' :  id,
9285                     cls : 'control-label col-form-label',
9286                     html : this.fieldLabel
9287
9288                 },
9289                 {
9290                     cls : "", 
9291                     cn: [
9292                         inputblock
9293                     ]
9294                 }
9295             ];
9296             
9297             var labelCfg = cfg.cn[1];
9298             var contentCfg = cfg.cn[2];
9299             
9300             if(this.indicatorpos == 'right'){
9301                 cfg.cn = [
9302                     {
9303                         tag: 'label',
9304                         'for' :  id,
9305                         cls : 'control-label col-form-label',
9306                         cn : [
9307                             {
9308                                 tag : 'span',
9309                                 html : this.fieldLabel
9310                             },
9311                             indicator
9312                         ]
9313                     },
9314                     {
9315                         cls : "",
9316                         cn: [
9317                             inputblock
9318                         ]
9319                     }
9320
9321                 ];
9322                 
9323                 labelCfg = cfg.cn[0];
9324                 contentCfg = cfg.cn[1];
9325             
9326             }
9327             
9328             if(this.labelWidth > 12){
9329                 labelCfg.style = "width: " + this.labelWidth + 'px';
9330             }
9331             
9332             if(this.labelWidth < 13 && this.labelmd == 0){
9333                 this.labelmd = this.labelWidth;
9334             }
9335             
9336             if(this.labellg > 0){
9337                 labelCfg.cls += ' col-lg-' + this.labellg;
9338                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9339             }
9340             
9341             if(this.labelmd > 0){
9342                 labelCfg.cls += ' col-md-' + this.labelmd;
9343                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9344             }
9345             
9346             if(this.labelsm > 0){
9347                 labelCfg.cls += ' col-sm-' + this.labelsm;
9348                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9349             }
9350             
9351             if(this.labelxs > 0){
9352                 labelCfg.cls += ' col-xs-' + this.labelxs;
9353                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9354             }
9355             
9356             
9357         } else if ( this.fieldLabel.length) {
9358                 
9359             cfg.cn = [
9360                 {
9361                     tag : 'i',
9362                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9363                     tooltip : 'This field is required'
9364                 },
9365                 {
9366                     tag: 'label',
9367                    //cls : 'input-group-addon',
9368                     html : this.fieldLabel
9369
9370                 },
9371
9372                inputblock
9373
9374            ];
9375            
9376            if(this.indicatorpos == 'right'){
9377                 
9378                 cfg.cn = [
9379                     {
9380                         tag: 'label',
9381                        //cls : 'input-group-addon',
9382                         html : this.fieldLabel
9383
9384                     },
9385                     {
9386                         tag : 'i',
9387                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9388                         tooltip : 'This field is required'
9389                     },
9390
9391                    inputblock
9392
9393                ];
9394
9395             }
9396
9397         } else {
9398             
9399             cfg.cn = [
9400
9401                     inputblock
9402
9403             ];
9404                 
9405                 
9406         };
9407         
9408         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9409            cfg.cls += ' navbar-form';
9410         }
9411         
9412         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9413             // on BS4 we do this only if not form 
9414             cfg.cls += ' navbar-form';
9415             cfg.tag = 'li';
9416         }
9417         
9418         return cfg;
9419         
9420     },
9421     /**
9422      * return the real input element.
9423      */
9424     inputEl: function ()
9425     {
9426         return this.el.select('input.form-control',true).first();
9427     },
9428     
9429     tooltipEl : function()
9430     {
9431         return this.inputEl();
9432     },
9433     
9434     indicatorEl : function()
9435     {
9436         if (Roo.bootstrap.version == 4) {
9437             return false; // not enabled in v4 yet.
9438         }
9439         
9440         var indicator = this.el.select('i.roo-required-indicator',true).first();
9441         
9442         if(!indicator){
9443             return false;
9444         }
9445         
9446         return indicator;
9447         
9448     },
9449     
9450     setDisabled : function(v)
9451     {
9452         var i  = this.inputEl().dom;
9453         if (!v) {
9454             i.removeAttribute('disabled');
9455             return;
9456             
9457         }
9458         i.setAttribute('disabled','true');
9459     },
9460     initEvents : function()
9461     {
9462           
9463         this.inputEl().on("keydown" , this.fireKey,  this);
9464         this.inputEl().on("focus", this.onFocus,  this);
9465         this.inputEl().on("blur", this.onBlur,  this);
9466         
9467         this.inputEl().relayEvent('keyup', this);
9468         
9469         this.indicator = this.indicatorEl();
9470         
9471         if(this.indicator){
9472             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9473         }
9474  
9475         // reference to original value for reset
9476         this.originalValue = this.getValue();
9477         //Roo.form.TextField.superclass.initEvents.call(this);
9478         if(this.validationEvent == 'keyup'){
9479             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9480             this.inputEl().on('keyup', this.filterValidation, this);
9481         }
9482         else if(this.validationEvent !== false){
9483             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9484         }
9485         
9486         if(this.selectOnFocus){
9487             this.on("focus", this.preFocus, this);
9488             
9489         }
9490         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9491             this.inputEl().on("keypress", this.filterKeys, this);
9492         } else {
9493             this.inputEl().relayEvent('keypress', this);
9494         }
9495        /* if(this.grow){
9496             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9497             this.el.on("click", this.autoSize,  this);
9498         }
9499         */
9500         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9501             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9502         }
9503         
9504         if (typeof(this.before) == 'object') {
9505             this.before.render(this.el.select('.roo-input-before',true).first());
9506         }
9507         if (typeof(this.after) == 'object') {
9508             this.after.render(this.el.select('.roo-input-after',true).first());
9509         }
9510         
9511         this.inputEl().on('change', this.onChange, this);
9512         
9513     },
9514     filterValidation : function(e){
9515         if(!e.isNavKeyPress()){
9516             this.validationTask.delay(this.validationDelay);
9517         }
9518     },
9519      /**
9520      * Validates the field value
9521      * @return {Boolean} True if the value is valid, else false
9522      */
9523     validate : function(){
9524         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9525         if(this.disabled || this.validateValue(this.getRawValue())){
9526             this.markValid();
9527             return true;
9528         }
9529         
9530         this.markInvalid();
9531         return false;
9532     },
9533     
9534     
9535     /**
9536      * Validates a value according to the field's validation rules and marks the field as invalid
9537      * if the validation fails
9538      * @param {Mixed} value The value to validate
9539      * @return {Boolean} True if the value is valid, else false
9540      */
9541     validateValue : function(value)
9542     {
9543         if(this.getVisibilityEl().hasClass('hidden')){
9544             return true;
9545         }
9546         
9547         if(value.length < 1)  { // if it's blank
9548             if(this.allowBlank){
9549                 return true;
9550             }
9551             return false;
9552         }
9553         
9554         if(value.length < this.minLength){
9555             return false;
9556         }
9557         if(value.length > this.maxLength){
9558             return false;
9559         }
9560         if(this.vtype){
9561             var vt = Roo.form.VTypes;
9562             if(!vt[this.vtype](value, this)){
9563                 return false;
9564             }
9565         }
9566         if(typeof this.validator == "function"){
9567             var msg = this.validator(value);
9568             if(msg !== true){
9569                 return false;
9570             }
9571             if (typeof(msg) == 'string') {
9572                 this.invalidText = msg;
9573             }
9574         }
9575         
9576         if(this.regex && !this.regex.test(value)){
9577             return false;
9578         }
9579         
9580         return true;
9581     },
9582     
9583      // private
9584     fireKey : function(e){
9585         //Roo.log('field ' + e.getKey());
9586         if(e.isNavKeyPress()){
9587             this.fireEvent("specialkey", this, e);
9588         }
9589     },
9590     focus : function (selectText){
9591         if(this.rendered){
9592             this.inputEl().focus();
9593             if(selectText === true){
9594                 this.inputEl().dom.select();
9595             }
9596         }
9597         return this;
9598     } ,
9599     
9600     onFocus : function(){
9601         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9602            // this.el.addClass(this.focusClass);
9603         }
9604         if(!this.hasFocus){
9605             this.hasFocus = true;
9606             this.startValue = this.getValue();
9607             this.fireEvent("focus", this);
9608         }
9609     },
9610     
9611     beforeBlur : Roo.emptyFn,
9612
9613     
9614     // private
9615     onBlur : function(){
9616         this.beforeBlur();
9617         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9618             //this.el.removeClass(this.focusClass);
9619         }
9620         this.hasFocus = false;
9621         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9622             this.validate();
9623         }
9624         var v = this.getValue();
9625         if(String(v) !== String(this.startValue)){
9626             this.fireEvent('change', this, v, this.startValue);
9627         }
9628         this.fireEvent("blur", this);
9629     },
9630     
9631     onChange : function(e)
9632     {
9633         var v = this.getValue();
9634         if(String(v) !== String(this.startValue)){
9635             this.fireEvent('change', this, v, this.startValue);
9636         }
9637         
9638     },
9639     
9640     /**
9641      * Resets the current field value to the originally loaded value and clears any validation messages
9642      */
9643     reset : function(){
9644         this.setValue(this.originalValue);
9645         this.validate();
9646     },
9647      /**
9648      * Returns the name of the field
9649      * @return {Mixed} name The name field
9650      */
9651     getName: function(){
9652         return this.name;
9653     },
9654      /**
9655      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9656      * @return {Mixed} value The field value
9657      */
9658     getValue : function(){
9659         
9660         var v = this.inputEl().getValue();
9661         
9662         return v;
9663     },
9664     /**
9665      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9666      * @return {Mixed} value The field value
9667      */
9668     getRawValue : function(){
9669         var v = this.inputEl().getValue();
9670         
9671         return v;
9672     },
9673     
9674     /**
9675      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9676      * @param {Mixed} value The value to set
9677      */
9678     setRawValue : function(v){
9679         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9680     },
9681     
9682     selectText : function(start, end){
9683         var v = this.getRawValue();
9684         if(v.length > 0){
9685             start = start === undefined ? 0 : start;
9686             end = end === undefined ? v.length : end;
9687             var d = this.inputEl().dom;
9688             if(d.setSelectionRange){
9689                 d.setSelectionRange(start, end);
9690             }else if(d.createTextRange){
9691                 var range = d.createTextRange();
9692                 range.moveStart("character", start);
9693                 range.moveEnd("character", v.length-end);
9694                 range.select();
9695             }
9696         }
9697     },
9698     
9699     /**
9700      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9701      * @param {Mixed} value The value to set
9702      */
9703     setValue : function(v){
9704         this.value = v;
9705         if(this.rendered){
9706             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9707             this.validate();
9708         }
9709     },
9710     
9711     /*
9712     processValue : function(value){
9713         if(this.stripCharsRe){
9714             var newValue = value.replace(this.stripCharsRe, '');
9715             if(newValue !== value){
9716                 this.setRawValue(newValue);
9717                 return newValue;
9718             }
9719         }
9720         return value;
9721     },
9722   */
9723     preFocus : function(){
9724         
9725         if(this.selectOnFocus){
9726             this.inputEl().dom.select();
9727         }
9728     },
9729     filterKeys : function(e){
9730         var k = e.getKey();
9731         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9732             return;
9733         }
9734         var c = e.getCharCode(), cc = String.fromCharCode(c);
9735         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9736             return;
9737         }
9738         if(!this.maskRe.test(cc)){
9739             e.stopEvent();
9740         }
9741     },
9742      /**
9743      * Clear any invalid styles/messages for this field
9744      */
9745     clearInvalid : function(){
9746         
9747         if(!this.el || this.preventMark){ // not rendered
9748             return;
9749         }
9750         
9751      
9752         this.el.removeClass(this.invalidClass);
9753         
9754         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9755             
9756             var feedback = this.el.select('.form-control-feedback', true).first();
9757             
9758             if(feedback){
9759                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9760             }
9761             
9762         }
9763         
9764         if(this.indicator){
9765             this.indicator.removeClass('visible');
9766             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9767         }
9768         
9769         this.fireEvent('valid', this);
9770     },
9771     
9772      /**
9773      * Mark this field as valid
9774      */
9775     markValid : function()
9776     {
9777         if(!this.el  || this.preventMark){ // not rendered...
9778             return;
9779         }
9780         
9781         this.el.removeClass([this.invalidClass, this.validClass]);
9782         
9783         var feedback = this.el.select('.form-control-feedback', true).first();
9784             
9785         if(feedback){
9786             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9787         }
9788         
9789         if(this.indicator){
9790             this.indicator.removeClass('visible');
9791             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9792         }
9793         
9794         if(this.disabled){
9795             return;
9796         }
9797         
9798         if(this.allowBlank && !this.getRawValue().length){
9799             return;
9800         }
9801         
9802         this.el.addClass(this.validClass);
9803         
9804         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9805             
9806             var feedback = this.el.select('.form-control-feedback', true).first();
9807             
9808             if(feedback){
9809                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9810                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9811             }
9812             
9813         }
9814         
9815         this.fireEvent('valid', this);
9816     },
9817     
9818      /**
9819      * Mark this field as invalid
9820      * @param {String} msg The validation message
9821      */
9822     markInvalid : function(msg)
9823     {
9824         if(!this.el  || this.preventMark){ // not rendered
9825             return;
9826         }
9827         
9828         this.el.removeClass([this.invalidClass, this.validClass]);
9829         
9830         var feedback = this.el.select('.form-control-feedback', true).first();
9831             
9832         if(feedback){
9833             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9834         }
9835
9836         if(this.disabled){
9837             return;
9838         }
9839         
9840         if(this.allowBlank && !this.getRawValue().length){
9841             return;
9842         }
9843         
9844         if(this.indicator){
9845             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9846             this.indicator.addClass('visible');
9847         }
9848         
9849         this.el.addClass(this.invalidClass);
9850         
9851         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9852             
9853             var feedback = this.el.select('.form-control-feedback', true).first();
9854             
9855             if(feedback){
9856                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9857                 
9858                 if(this.getValue().length || this.forceFeedback){
9859                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9860                 }
9861                 
9862             }
9863             
9864         }
9865         
9866         this.fireEvent('invalid', this, msg);
9867     },
9868     // private
9869     SafariOnKeyDown : function(event)
9870     {
9871         // this is a workaround for a password hang bug on chrome/ webkit.
9872         if (this.inputEl().dom.type != 'password') {
9873             return;
9874         }
9875         
9876         var isSelectAll = false;
9877         
9878         if(this.inputEl().dom.selectionEnd > 0){
9879             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9880         }
9881         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9882             event.preventDefault();
9883             this.setValue('');
9884             return;
9885         }
9886         
9887         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9888             
9889             event.preventDefault();
9890             // this is very hacky as keydown always get's upper case.
9891             //
9892             var cc = String.fromCharCode(event.getCharCode());
9893             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9894             
9895         }
9896     },
9897     adjustWidth : function(tag, w){
9898         tag = tag.toLowerCase();
9899         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9900             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9901                 if(tag == 'input'){
9902                     return w + 2;
9903                 }
9904                 if(tag == 'textarea'){
9905                     return w-2;
9906                 }
9907             }else if(Roo.isOpera){
9908                 if(tag == 'input'){
9909                     return w + 2;
9910                 }
9911                 if(tag == 'textarea'){
9912                     return w-2;
9913                 }
9914             }
9915         }
9916         return w;
9917     },
9918     
9919     setFieldLabel : function(v)
9920     {
9921         if(!this.rendered){
9922             return;
9923         }
9924         
9925         if(this.indicatorEl()){
9926             var ar = this.el.select('label > span',true);
9927             
9928             if (ar.elements.length) {
9929                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9930                 this.fieldLabel = v;
9931                 return;
9932             }
9933             
9934             var br = this.el.select('label',true);
9935             
9936             if(br.elements.length) {
9937                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9938                 this.fieldLabel = v;
9939                 return;
9940             }
9941             
9942             Roo.log('Cannot Found any of label > span || label in input');
9943             return;
9944         }
9945         
9946         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9947         this.fieldLabel = v;
9948         
9949         
9950     }
9951 });
9952
9953  
9954 /*
9955  * - LGPL
9956  *
9957  * Input
9958  * 
9959  */
9960
9961 /**
9962  * @class Roo.bootstrap.TextArea
9963  * @extends Roo.bootstrap.Input
9964  * Bootstrap TextArea class
9965  * @cfg {Number} cols Specifies the visible width of a text area
9966  * @cfg {Number} rows Specifies the visible number of lines in a text area
9967  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9968  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9969  * @cfg {string} html text
9970  * 
9971  * @constructor
9972  * Create a new TextArea
9973  * @param {Object} config The config object
9974  */
9975
9976 Roo.bootstrap.TextArea = function(config){
9977     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9978    
9979 };
9980
9981 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9982      
9983     cols : false,
9984     rows : 5,
9985     readOnly : false,
9986     warp : 'soft',
9987     resize : false,
9988     value: false,
9989     html: false,
9990     
9991     getAutoCreate : function(){
9992         
9993         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9994         
9995         var id = Roo.id();
9996         
9997         var cfg = {};
9998         
9999         if(this.inputType != 'hidden'){
10000             cfg.cls = 'form-group' //input-group
10001         }
10002         
10003         var input =  {
10004             tag: 'textarea',
10005             id : id,
10006             warp : this.warp,
10007             rows : this.rows,
10008             value : this.value || '',
10009             html: this.html || '',
10010             cls : 'form-control',
10011             placeholder : this.placeholder || '' 
10012             
10013         };
10014         
10015         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10016             input.maxLength = this.maxLength;
10017         }
10018         
10019         if(this.resize){
10020             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10021         }
10022         
10023         if(this.cols){
10024             input.cols = this.cols;
10025         }
10026         
10027         if (this.readOnly) {
10028             input.readonly = true;
10029         }
10030         
10031         if (this.name) {
10032             input.name = this.name;
10033         }
10034         
10035         if (this.size) {
10036             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10037         }
10038         
10039         var settings=this;
10040         ['xs','sm','md','lg'].map(function(size){
10041             if (settings[size]) {
10042                 cfg.cls += ' col-' + size + '-' + settings[size];
10043             }
10044         });
10045         
10046         var inputblock = input;
10047         
10048         if(this.hasFeedback && !this.allowBlank){
10049             
10050             var feedback = {
10051                 tag: 'span',
10052                 cls: 'glyphicon form-control-feedback'
10053             };
10054
10055             inputblock = {
10056                 cls : 'has-feedback',
10057                 cn :  [
10058                     input,
10059                     feedback
10060                 ] 
10061             };  
10062         }
10063         
10064         
10065         if (this.before || this.after) {
10066             
10067             inputblock = {
10068                 cls : 'input-group',
10069                 cn :  [] 
10070             };
10071             if (this.before) {
10072                 inputblock.cn.push({
10073                     tag :'span',
10074                     cls : 'input-group-addon',
10075                     html : this.before
10076                 });
10077             }
10078             
10079             inputblock.cn.push(input);
10080             
10081             if(this.hasFeedback && !this.allowBlank){
10082                 inputblock.cls += ' has-feedback';
10083                 inputblock.cn.push(feedback);
10084             }
10085             
10086             if (this.after) {
10087                 inputblock.cn.push({
10088                     tag :'span',
10089                     cls : 'input-group-addon',
10090                     html : this.after
10091                 });
10092             }
10093             
10094         }
10095         
10096         if (align ==='left' && this.fieldLabel.length) {
10097             cfg.cn = [
10098                 {
10099                     tag: 'label',
10100                     'for' :  id,
10101                     cls : 'control-label',
10102                     html : this.fieldLabel
10103                 },
10104                 {
10105                     cls : "",
10106                     cn: [
10107                         inputblock
10108                     ]
10109                 }
10110
10111             ];
10112             
10113             if(this.labelWidth > 12){
10114                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10115             }
10116
10117             if(this.labelWidth < 13 && this.labelmd == 0){
10118                 this.labelmd = this.labelWidth;
10119             }
10120
10121             if(this.labellg > 0){
10122                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10123                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10124             }
10125
10126             if(this.labelmd > 0){
10127                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10128                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10129             }
10130
10131             if(this.labelsm > 0){
10132                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10133                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10134             }
10135
10136             if(this.labelxs > 0){
10137                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10138                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10139             }
10140             
10141         } else if ( this.fieldLabel.length) {
10142             cfg.cn = [
10143
10144                {
10145                    tag: 'label',
10146                    //cls : 'input-group-addon',
10147                    html : this.fieldLabel
10148
10149                },
10150
10151                inputblock
10152
10153            ];
10154
10155         } else {
10156
10157             cfg.cn = [
10158
10159                 inputblock
10160
10161             ];
10162                 
10163         }
10164         
10165         if (this.disabled) {
10166             input.disabled=true;
10167         }
10168         
10169         return cfg;
10170         
10171     },
10172     /**
10173      * return the real textarea element.
10174      */
10175     inputEl: function ()
10176     {
10177         return this.el.select('textarea.form-control',true).first();
10178     },
10179     
10180     /**
10181      * Clear any invalid styles/messages for this field
10182      */
10183     clearInvalid : function()
10184     {
10185         
10186         if(!this.el || this.preventMark){ // not rendered
10187             return;
10188         }
10189         
10190         var label = this.el.select('label', true).first();
10191         var icon = this.el.select('i.fa-star', true).first();
10192         
10193         if(label && icon){
10194             icon.remove();
10195         }
10196         
10197         this.el.removeClass(this.invalidClass);
10198         
10199         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10200             
10201             var feedback = this.el.select('.form-control-feedback', true).first();
10202             
10203             if(feedback){
10204                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10205             }
10206             
10207         }
10208         
10209         this.fireEvent('valid', this);
10210     },
10211     
10212      /**
10213      * Mark this field as valid
10214      */
10215     markValid : function()
10216     {
10217         if(!this.el  || this.preventMark){ // not rendered
10218             return;
10219         }
10220         
10221         this.el.removeClass([this.invalidClass, this.validClass]);
10222         
10223         var feedback = this.el.select('.form-control-feedback', true).first();
10224             
10225         if(feedback){
10226             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10227         }
10228
10229         if(this.disabled || this.allowBlank){
10230             return;
10231         }
10232         
10233         var label = this.el.select('label', true).first();
10234         var icon = this.el.select('i.fa-star', true).first();
10235         
10236         if(label && icon){
10237             icon.remove();
10238         }
10239         
10240         this.el.addClass(this.validClass);
10241         
10242         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10243             
10244             var feedback = this.el.select('.form-control-feedback', true).first();
10245             
10246             if(feedback){
10247                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10248                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10249             }
10250             
10251         }
10252         
10253         this.fireEvent('valid', this);
10254     },
10255     
10256      /**
10257      * Mark this field as invalid
10258      * @param {String} msg The validation message
10259      */
10260     markInvalid : function(msg)
10261     {
10262         if(!this.el  || this.preventMark){ // not rendered
10263             return;
10264         }
10265         
10266         this.el.removeClass([this.invalidClass, this.validClass]);
10267         
10268         var feedback = this.el.select('.form-control-feedback', true).first();
10269             
10270         if(feedback){
10271             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10272         }
10273
10274         if(this.disabled || this.allowBlank){
10275             return;
10276         }
10277         
10278         var label = this.el.select('label', true).first();
10279         var icon = this.el.select('i.fa-star', true).first();
10280         
10281         if(!this.getValue().length && label && !icon){
10282             this.el.createChild({
10283                 tag : 'i',
10284                 cls : 'text-danger fa fa-lg fa-star',
10285                 tooltip : 'This field is required',
10286                 style : 'margin-right:5px;'
10287             }, label, true);
10288         }
10289
10290         this.el.addClass(this.invalidClass);
10291         
10292         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10293             
10294             var feedback = this.el.select('.form-control-feedback', true).first();
10295             
10296             if(feedback){
10297                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10298                 
10299                 if(this.getValue().length || this.forceFeedback){
10300                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10301                 }
10302                 
10303             }
10304             
10305         }
10306         
10307         this.fireEvent('invalid', this, msg);
10308     }
10309 });
10310
10311  
10312 /*
10313  * - LGPL
10314  *
10315  * trigger field - base class for combo..
10316  * 
10317  */
10318  
10319 /**
10320  * @class Roo.bootstrap.TriggerField
10321  * @extends Roo.bootstrap.Input
10322  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10323  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10324  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10325  * for which you can provide a custom implementation.  For example:
10326  * <pre><code>
10327 var trigger = new Roo.bootstrap.TriggerField();
10328 trigger.onTriggerClick = myTriggerFn;
10329 trigger.applyTo('my-field');
10330 </code></pre>
10331  *
10332  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10333  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10334  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10335  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10336  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10337
10338  * @constructor
10339  * Create a new TriggerField.
10340  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10341  * to the base TextField)
10342  */
10343 Roo.bootstrap.TriggerField = function(config){
10344     this.mimicing = false;
10345     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10346 };
10347
10348 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10349     /**
10350      * @cfg {String} triggerClass A CSS class to apply to the trigger
10351      */
10352      /**
10353      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10354      */
10355     hideTrigger:false,
10356
10357     /**
10358      * @cfg {Boolean} removable (true|false) special filter default false
10359      */
10360     removable : false,
10361     
10362     /** @cfg {Boolean} grow @hide */
10363     /** @cfg {Number} growMin @hide */
10364     /** @cfg {Number} growMax @hide */
10365
10366     /**
10367      * @hide 
10368      * @method
10369      */
10370     autoSize: Roo.emptyFn,
10371     // private
10372     monitorTab : true,
10373     // private
10374     deferHeight : true,
10375
10376     
10377     actionMode : 'wrap',
10378     
10379     caret : false,
10380     
10381     
10382     getAutoCreate : function(){
10383        
10384         var align = this.labelAlign || this.parentLabelAlign();
10385         
10386         var id = Roo.id();
10387         
10388         var cfg = {
10389             cls: 'form-group' //input-group
10390         };
10391         
10392         
10393         var input =  {
10394             tag: 'input',
10395             id : id,
10396             type : this.inputType,
10397             cls : 'form-control',
10398             autocomplete: 'new-password',
10399             placeholder : this.placeholder || '' 
10400             
10401         };
10402         if (this.name) {
10403             input.name = this.name;
10404         }
10405         if (this.size) {
10406             input.cls += ' input-' + this.size;
10407         }
10408         
10409         if (this.disabled) {
10410             input.disabled=true;
10411         }
10412         
10413         var inputblock = input;
10414         
10415         if(this.hasFeedback && !this.allowBlank){
10416             
10417             var feedback = {
10418                 tag: 'span',
10419                 cls: 'glyphicon form-control-feedback'
10420             };
10421             
10422             if(this.removable && !this.editable && !this.tickable){
10423                 inputblock = {
10424                     cls : 'has-feedback',
10425                     cn :  [
10426                         inputblock,
10427                         {
10428                             tag: 'button',
10429                             html : 'x',
10430                             cls : 'roo-combo-removable-btn close'
10431                         },
10432                         feedback
10433                     ] 
10434                 };
10435             } else {
10436                 inputblock = {
10437                     cls : 'has-feedback',
10438                     cn :  [
10439                         inputblock,
10440                         feedback
10441                     ] 
10442                 };
10443             }
10444
10445         } else {
10446             if(this.removable && !this.editable && !this.tickable){
10447                 inputblock = {
10448                     cls : 'roo-removable',
10449                     cn :  [
10450                         inputblock,
10451                         {
10452                             tag: 'button',
10453                             html : 'x',
10454                             cls : 'roo-combo-removable-btn close'
10455                         }
10456                     ] 
10457                 };
10458             }
10459         }
10460         
10461         if (this.before || this.after) {
10462             
10463             inputblock = {
10464                 cls : 'input-group',
10465                 cn :  [] 
10466             };
10467             if (this.before) {
10468                 inputblock.cn.push({
10469                     tag :'span',
10470                     cls : 'input-group-addon input-group-prepend input-group-text',
10471                     html : this.before
10472                 });
10473             }
10474             
10475             inputblock.cn.push(input);
10476             
10477             if(this.hasFeedback && !this.allowBlank){
10478                 inputblock.cls += ' has-feedback';
10479                 inputblock.cn.push(feedback);
10480             }
10481             
10482             if (this.after) {
10483                 inputblock.cn.push({
10484                     tag :'span',
10485                     cls : 'input-group-addon input-group-append input-group-text',
10486                     html : this.after
10487                 });
10488             }
10489             
10490         };
10491         
10492       
10493         
10494         var ibwrap = inputblock;
10495         
10496         if(this.multiple){
10497             ibwrap = {
10498                 tag: 'ul',
10499                 cls: 'roo-select2-choices',
10500                 cn:[
10501                     {
10502                         tag: 'li',
10503                         cls: 'roo-select2-search-field',
10504                         cn: [
10505
10506                             inputblock
10507                         ]
10508                     }
10509                 ]
10510             };
10511                 
10512         }
10513         
10514         var combobox = {
10515             cls: 'roo-select2-container input-group',
10516             cn: [
10517                  {
10518                     tag: 'input',
10519                     type : 'hidden',
10520                     cls: 'form-hidden-field'
10521                 },
10522                 ibwrap
10523             ]
10524         };
10525         
10526         if(!this.multiple && this.showToggleBtn){
10527             
10528             var caret = {
10529                         tag: 'span',
10530                         cls: 'caret'
10531              };
10532             if (this.caret != false) {
10533                 caret = {
10534                      tag: 'i',
10535                      cls: 'fa fa-' + this.caret
10536                 };
10537                 
10538             }
10539             
10540             combobox.cn.push({
10541                 tag :'span',
10542                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10543                 cn : [
10544                     caret,
10545                     {
10546                         tag: 'span',
10547                         cls: 'combobox-clear',
10548                         cn  : [
10549                             {
10550                                 tag : 'i',
10551                                 cls: 'icon-remove'
10552                             }
10553                         ]
10554                     }
10555                 ]
10556
10557             })
10558         }
10559         
10560         if(this.multiple){
10561             combobox.cls += ' roo-select2-container-multi';
10562         }
10563          var indicator = {
10564             tag : 'i',
10565             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10566             tooltip : 'This field is required'
10567         };
10568         if (Roo.bootstrap.version == 4) {
10569             indicator = {
10570                 tag : 'i',
10571                 style : 'display:none'
10572             };
10573         }
10574         
10575         
10576         if (align ==='left' && this.fieldLabel.length) {
10577             
10578             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10579
10580             cfg.cn = [
10581                 indicator,
10582                 {
10583                     tag: 'label',
10584                     'for' :  id,
10585                     cls : 'control-label',
10586                     html : this.fieldLabel
10587
10588                 },
10589                 {
10590                     cls : "", 
10591                     cn: [
10592                         combobox
10593                     ]
10594                 }
10595
10596             ];
10597             
10598             var labelCfg = cfg.cn[1];
10599             var contentCfg = cfg.cn[2];
10600             
10601             if(this.indicatorpos == 'right'){
10602                 cfg.cn = [
10603                     {
10604                         tag: 'label',
10605                         'for' :  id,
10606                         cls : 'control-label',
10607                         cn : [
10608                             {
10609                                 tag : 'span',
10610                                 html : this.fieldLabel
10611                             },
10612                             indicator
10613                         ]
10614                     },
10615                     {
10616                         cls : "", 
10617                         cn: [
10618                             combobox
10619                         ]
10620                     }
10621
10622                 ];
10623                 
10624                 labelCfg = cfg.cn[0];
10625                 contentCfg = cfg.cn[1];
10626             }
10627             
10628             if(this.labelWidth > 12){
10629                 labelCfg.style = "width: " + this.labelWidth + 'px';
10630             }
10631             
10632             if(this.labelWidth < 13 && this.labelmd == 0){
10633                 this.labelmd = this.labelWidth;
10634             }
10635             
10636             if(this.labellg > 0){
10637                 labelCfg.cls += ' col-lg-' + this.labellg;
10638                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10639             }
10640             
10641             if(this.labelmd > 0){
10642                 labelCfg.cls += ' col-md-' + this.labelmd;
10643                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10644             }
10645             
10646             if(this.labelsm > 0){
10647                 labelCfg.cls += ' col-sm-' + this.labelsm;
10648                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10649             }
10650             
10651             if(this.labelxs > 0){
10652                 labelCfg.cls += ' col-xs-' + this.labelxs;
10653                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10654             }
10655             
10656         } else if ( this.fieldLabel.length) {
10657 //                Roo.log(" label");
10658             cfg.cn = [
10659                 indicator,
10660                {
10661                    tag: 'label',
10662                    //cls : 'input-group-addon',
10663                    html : this.fieldLabel
10664
10665                },
10666
10667                combobox
10668
10669             ];
10670             
10671             if(this.indicatorpos == 'right'){
10672                 
10673                 cfg.cn = [
10674                     {
10675                        tag: 'label',
10676                        cn : [
10677                            {
10678                                tag : 'span',
10679                                html : this.fieldLabel
10680                            },
10681                            indicator
10682                        ]
10683
10684                     },
10685                     combobox
10686
10687                 ];
10688
10689             }
10690
10691         } else {
10692             
10693 //                Roo.log(" no label && no align");
10694                 cfg = combobox
10695                      
10696                 
10697         }
10698         
10699         var settings=this;
10700         ['xs','sm','md','lg'].map(function(size){
10701             if (settings[size]) {
10702                 cfg.cls += ' col-' + size + '-' + settings[size];
10703             }
10704         });
10705         
10706         return cfg;
10707         
10708     },
10709     
10710     
10711     
10712     // private
10713     onResize : function(w, h){
10714 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10715 //        if(typeof w == 'number'){
10716 //            var x = w - this.trigger.getWidth();
10717 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10718 //            this.trigger.setStyle('left', x+'px');
10719 //        }
10720     },
10721
10722     // private
10723     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10724
10725     // private
10726     getResizeEl : function(){
10727         return this.inputEl();
10728     },
10729
10730     // private
10731     getPositionEl : function(){
10732         return this.inputEl();
10733     },
10734
10735     // private
10736     alignErrorIcon : function(){
10737         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10738     },
10739
10740     // private
10741     initEvents : function(){
10742         
10743         this.createList();
10744         
10745         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10746         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10747         if(!this.multiple && this.showToggleBtn){
10748             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10749             if(this.hideTrigger){
10750                 this.trigger.setDisplayed(false);
10751             }
10752             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10753         }
10754         
10755         if(this.multiple){
10756             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10757         }
10758         
10759         if(this.removable && !this.editable && !this.tickable){
10760             var close = this.closeTriggerEl();
10761             
10762             if(close){
10763                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10764                 close.on('click', this.removeBtnClick, this, close);
10765             }
10766         }
10767         
10768         //this.trigger.addClassOnOver('x-form-trigger-over');
10769         //this.trigger.addClassOnClick('x-form-trigger-click');
10770         
10771         //if(!this.width){
10772         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10773         //}
10774     },
10775     
10776     closeTriggerEl : function()
10777     {
10778         var close = this.el.select('.roo-combo-removable-btn', true).first();
10779         return close ? close : false;
10780     },
10781     
10782     removeBtnClick : function(e, h, el)
10783     {
10784         e.preventDefault();
10785         
10786         if(this.fireEvent("remove", this) !== false){
10787             this.reset();
10788             this.fireEvent("afterremove", this)
10789         }
10790     },
10791     
10792     createList : function()
10793     {
10794         this.list = Roo.get(document.body).createChild({
10795             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10796             cls: 'typeahead typeahead-long dropdown-menu',
10797             style: 'display:none'
10798         });
10799         
10800         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10801         
10802     },
10803
10804     // private
10805     initTrigger : function(){
10806        
10807     },
10808
10809     // private
10810     onDestroy : function(){
10811         if(this.trigger){
10812             this.trigger.removeAllListeners();
10813           //  this.trigger.remove();
10814         }
10815         //if(this.wrap){
10816         //    this.wrap.remove();
10817         //}
10818         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10819     },
10820
10821     // private
10822     onFocus : function(){
10823         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10824         /*
10825         if(!this.mimicing){
10826             this.wrap.addClass('x-trigger-wrap-focus');
10827             this.mimicing = true;
10828             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10829             if(this.monitorTab){
10830                 this.el.on("keydown", this.checkTab, this);
10831             }
10832         }
10833         */
10834     },
10835
10836     // private
10837     checkTab : function(e){
10838         if(e.getKey() == e.TAB){
10839             this.triggerBlur();
10840         }
10841     },
10842
10843     // private
10844     onBlur : function(){
10845         // do nothing
10846     },
10847
10848     // private
10849     mimicBlur : function(e, t){
10850         /*
10851         if(!this.wrap.contains(t) && this.validateBlur()){
10852             this.triggerBlur();
10853         }
10854         */
10855     },
10856
10857     // private
10858     triggerBlur : function(){
10859         this.mimicing = false;
10860         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10861         if(this.monitorTab){
10862             this.el.un("keydown", this.checkTab, this);
10863         }
10864         //this.wrap.removeClass('x-trigger-wrap-focus');
10865         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10866     },
10867
10868     // private
10869     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10870     validateBlur : function(e, t){
10871         return true;
10872     },
10873
10874     // private
10875     onDisable : function(){
10876         this.inputEl().dom.disabled = true;
10877         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10878         //if(this.wrap){
10879         //    this.wrap.addClass('x-item-disabled');
10880         //}
10881     },
10882
10883     // private
10884     onEnable : function(){
10885         this.inputEl().dom.disabled = false;
10886         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10887         //if(this.wrap){
10888         //    this.el.removeClass('x-item-disabled');
10889         //}
10890     },
10891
10892     // private
10893     onShow : function(){
10894         var ae = this.getActionEl();
10895         
10896         if(ae){
10897             ae.dom.style.display = '';
10898             ae.dom.style.visibility = 'visible';
10899         }
10900     },
10901
10902     // private
10903     
10904     onHide : function(){
10905         var ae = this.getActionEl();
10906         ae.dom.style.display = 'none';
10907     },
10908
10909     /**
10910      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10911      * by an implementing function.
10912      * @method
10913      * @param {EventObject} e
10914      */
10915     onTriggerClick : Roo.emptyFn
10916 });
10917  /*
10918  * Based on:
10919  * Ext JS Library 1.1.1
10920  * Copyright(c) 2006-2007, Ext JS, LLC.
10921  *
10922  * Originally Released Under LGPL - original licence link has changed is not relivant.
10923  *
10924  * Fork - LGPL
10925  * <script type="text/javascript">
10926  */
10927
10928
10929 /**
10930  * @class Roo.data.SortTypes
10931  * @singleton
10932  * Defines the default sorting (casting?) comparison functions used when sorting data.
10933  */
10934 Roo.data.SortTypes = {
10935     /**
10936      * Default sort that does nothing
10937      * @param {Mixed} s The value being converted
10938      * @return {Mixed} The comparison value
10939      */
10940     none : function(s){
10941         return s;
10942     },
10943     
10944     /**
10945      * The regular expression used to strip tags
10946      * @type {RegExp}
10947      * @property
10948      */
10949     stripTagsRE : /<\/?[^>]+>/gi,
10950     
10951     /**
10952      * Strips all HTML tags to sort on text only
10953      * @param {Mixed} s The value being converted
10954      * @return {String} The comparison value
10955      */
10956     asText : function(s){
10957         return String(s).replace(this.stripTagsRE, "");
10958     },
10959     
10960     /**
10961      * Strips all HTML tags to sort on text only - Case insensitive
10962      * @param {Mixed} s The value being converted
10963      * @return {String} The comparison value
10964      */
10965     asUCText : function(s){
10966         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10967     },
10968     
10969     /**
10970      * Case insensitive string
10971      * @param {Mixed} s The value being converted
10972      * @return {String} The comparison value
10973      */
10974     asUCString : function(s) {
10975         return String(s).toUpperCase();
10976     },
10977     
10978     /**
10979      * Date sorting
10980      * @param {Mixed} s The value being converted
10981      * @return {Number} The comparison value
10982      */
10983     asDate : function(s) {
10984         if(!s){
10985             return 0;
10986         }
10987         if(s instanceof Date){
10988             return s.getTime();
10989         }
10990         return Date.parse(String(s));
10991     },
10992     
10993     /**
10994      * Float sorting
10995      * @param {Mixed} s The value being converted
10996      * @return {Float} The comparison value
10997      */
10998     asFloat : function(s) {
10999         var val = parseFloat(String(s).replace(/,/g, ""));
11000         if(isNaN(val)) {
11001             val = 0;
11002         }
11003         return val;
11004     },
11005     
11006     /**
11007      * Integer sorting
11008      * @param {Mixed} s The value being converted
11009      * @return {Number} The comparison value
11010      */
11011     asInt : function(s) {
11012         var val = parseInt(String(s).replace(/,/g, ""));
11013         if(isNaN(val)) {
11014             val = 0;
11015         }
11016         return val;
11017     }
11018 };/*
11019  * Based on:
11020  * Ext JS Library 1.1.1
11021  * Copyright(c) 2006-2007, Ext JS, LLC.
11022  *
11023  * Originally Released Under LGPL - original licence link has changed is not relivant.
11024  *
11025  * Fork - LGPL
11026  * <script type="text/javascript">
11027  */
11028
11029 /**
11030 * @class Roo.data.Record
11031  * Instances of this class encapsulate both record <em>definition</em> information, and record
11032  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11033  * to access Records cached in an {@link Roo.data.Store} object.<br>
11034  * <p>
11035  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11036  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11037  * objects.<br>
11038  * <p>
11039  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11040  * @constructor
11041  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11042  * {@link #create}. The parameters are the same.
11043  * @param {Array} data An associative Array of data values keyed by the field name.
11044  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11045  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11046  * not specified an integer id is generated.
11047  */
11048 Roo.data.Record = function(data, id){
11049     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11050     this.data = data;
11051 };
11052
11053 /**
11054  * Generate a constructor for a specific record layout.
11055  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11056  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11057  * Each field definition object may contain the following properties: <ul>
11058  * <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,
11059  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11060  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11061  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11062  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11063  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11064  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11065  * this may be omitted.</p></li>
11066  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11067  * <ul><li>auto (Default, implies no conversion)</li>
11068  * <li>string</li>
11069  * <li>int</li>
11070  * <li>float</li>
11071  * <li>boolean</li>
11072  * <li>date</li></ul></p></li>
11073  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11074  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11075  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11076  * by the Reader into an object that will be stored in the Record. It is passed the
11077  * following parameters:<ul>
11078  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11079  * </ul></p></li>
11080  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11081  * </ul>
11082  * <br>usage:<br><pre><code>
11083 var TopicRecord = Roo.data.Record.create(
11084     {name: 'title', mapping: 'topic_title'},
11085     {name: 'author', mapping: 'username'},
11086     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11087     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11088     {name: 'lastPoster', mapping: 'user2'},
11089     {name: 'excerpt', mapping: 'post_text'}
11090 );
11091
11092 var myNewRecord = new TopicRecord({
11093     title: 'Do my job please',
11094     author: 'noobie',
11095     totalPosts: 1,
11096     lastPost: new Date(),
11097     lastPoster: 'Animal',
11098     excerpt: 'No way dude!'
11099 });
11100 myStore.add(myNewRecord);
11101 </code></pre>
11102  * @method create
11103  * @static
11104  */
11105 Roo.data.Record.create = function(o){
11106     var f = function(){
11107         f.superclass.constructor.apply(this, arguments);
11108     };
11109     Roo.extend(f, Roo.data.Record);
11110     var p = f.prototype;
11111     p.fields = new Roo.util.MixedCollection(false, function(field){
11112         return field.name;
11113     });
11114     for(var i = 0, len = o.length; i < len; i++){
11115         p.fields.add(new Roo.data.Field(o[i]));
11116     }
11117     f.getField = function(name){
11118         return p.fields.get(name);  
11119     };
11120     return f;
11121 };
11122
11123 Roo.data.Record.AUTO_ID = 1000;
11124 Roo.data.Record.EDIT = 'edit';
11125 Roo.data.Record.REJECT = 'reject';
11126 Roo.data.Record.COMMIT = 'commit';
11127
11128 Roo.data.Record.prototype = {
11129     /**
11130      * Readonly flag - true if this record has been modified.
11131      * @type Boolean
11132      */
11133     dirty : false,
11134     editing : false,
11135     error: null,
11136     modified: null,
11137
11138     // private
11139     join : function(store){
11140         this.store = store;
11141     },
11142
11143     /**
11144      * Set the named field to the specified value.
11145      * @param {String} name The name of the field to set.
11146      * @param {Object} value The value to set the field to.
11147      */
11148     set : function(name, value){
11149         if(this.data[name] == value){
11150             return;
11151         }
11152         this.dirty = true;
11153         if(!this.modified){
11154             this.modified = {};
11155         }
11156         if(typeof this.modified[name] == 'undefined'){
11157             this.modified[name] = this.data[name];
11158         }
11159         this.data[name] = value;
11160         if(!this.editing && this.store){
11161             this.store.afterEdit(this);
11162         }       
11163     },
11164
11165     /**
11166      * Get the value of the named field.
11167      * @param {String} name The name of the field to get the value of.
11168      * @return {Object} The value of the field.
11169      */
11170     get : function(name){
11171         return this.data[name]; 
11172     },
11173
11174     // private
11175     beginEdit : function(){
11176         this.editing = true;
11177         this.modified = {}; 
11178     },
11179
11180     // private
11181     cancelEdit : function(){
11182         this.editing = false;
11183         delete this.modified;
11184     },
11185
11186     // private
11187     endEdit : function(){
11188         this.editing = false;
11189         if(this.dirty && this.store){
11190             this.store.afterEdit(this);
11191         }
11192     },
11193
11194     /**
11195      * Usually called by the {@link Roo.data.Store} which owns the Record.
11196      * Rejects all changes made to the Record since either creation, or the last commit operation.
11197      * Modified fields are reverted to their original values.
11198      * <p>
11199      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11200      * of reject operations.
11201      */
11202     reject : function(){
11203         var m = this.modified;
11204         for(var n in m){
11205             if(typeof m[n] != "function"){
11206                 this.data[n] = m[n];
11207             }
11208         }
11209         this.dirty = false;
11210         delete this.modified;
11211         this.editing = false;
11212         if(this.store){
11213             this.store.afterReject(this);
11214         }
11215     },
11216
11217     /**
11218      * Usually called by the {@link Roo.data.Store} which owns the Record.
11219      * Commits all changes made to the Record since either creation, or the last commit operation.
11220      * <p>
11221      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11222      * of commit operations.
11223      */
11224     commit : function(){
11225         this.dirty = false;
11226         delete this.modified;
11227         this.editing = false;
11228         if(this.store){
11229             this.store.afterCommit(this);
11230         }
11231     },
11232
11233     // private
11234     hasError : function(){
11235         return this.error != null;
11236     },
11237
11238     // private
11239     clearError : function(){
11240         this.error = null;
11241     },
11242
11243     /**
11244      * Creates a copy of this record.
11245      * @param {String} id (optional) A new record id if you don't want to use this record's id
11246      * @return {Record}
11247      */
11248     copy : function(newId) {
11249         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11250     }
11251 };/*
11252  * Based on:
11253  * Ext JS Library 1.1.1
11254  * Copyright(c) 2006-2007, Ext JS, LLC.
11255  *
11256  * Originally Released Under LGPL - original licence link has changed is not relivant.
11257  *
11258  * Fork - LGPL
11259  * <script type="text/javascript">
11260  */
11261
11262
11263
11264 /**
11265  * @class Roo.data.Store
11266  * @extends Roo.util.Observable
11267  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11268  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11269  * <p>
11270  * 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
11271  * has no knowledge of the format of the data returned by the Proxy.<br>
11272  * <p>
11273  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11274  * instances from the data object. These records are cached and made available through accessor functions.
11275  * @constructor
11276  * Creates a new Store.
11277  * @param {Object} config A config object containing the objects needed for the Store to access data,
11278  * and read the data into Records.
11279  */
11280 Roo.data.Store = function(config){
11281     this.data = new Roo.util.MixedCollection(false);
11282     this.data.getKey = function(o){
11283         return o.id;
11284     };
11285     this.baseParams = {};
11286     // private
11287     this.paramNames = {
11288         "start" : "start",
11289         "limit" : "limit",
11290         "sort" : "sort",
11291         "dir" : "dir",
11292         "multisort" : "_multisort"
11293     };
11294
11295     if(config && config.data){
11296         this.inlineData = config.data;
11297         delete config.data;
11298     }
11299
11300     Roo.apply(this, config);
11301     
11302     if(this.reader){ // reader passed
11303         this.reader = Roo.factory(this.reader, Roo.data);
11304         this.reader.xmodule = this.xmodule || false;
11305         if(!this.recordType){
11306             this.recordType = this.reader.recordType;
11307         }
11308         if(this.reader.onMetaChange){
11309             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11310         }
11311     }
11312
11313     if(this.recordType){
11314         this.fields = this.recordType.prototype.fields;
11315     }
11316     this.modified = [];
11317
11318     this.addEvents({
11319         /**
11320          * @event datachanged
11321          * Fires when the data cache has changed, and a widget which is using this Store
11322          * as a Record cache should refresh its view.
11323          * @param {Store} this
11324          */
11325         datachanged : true,
11326         /**
11327          * @event metachange
11328          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11329          * @param {Store} this
11330          * @param {Object} meta The JSON metadata
11331          */
11332         metachange : true,
11333         /**
11334          * @event add
11335          * Fires when Records have been added to the Store
11336          * @param {Store} this
11337          * @param {Roo.data.Record[]} records The array of Records added
11338          * @param {Number} index The index at which the record(s) were added
11339          */
11340         add : true,
11341         /**
11342          * @event remove
11343          * Fires when a Record has been removed from the Store
11344          * @param {Store} this
11345          * @param {Roo.data.Record} record The Record that was removed
11346          * @param {Number} index The index at which the record was removed
11347          */
11348         remove : true,
11349         /**
11350          * @event update
11351          * Fires when a Record has been updated
11352          * @param {Store} this
11353          * @param {Roo.data.Record} record The Record that was updated
11354          * @param {String} operation The update operation being performed.  Value may be one of:
11355          * <pre><code>
11356  Roo.data.Record.EDIT
11357  Roo.data.Record.REJECT
11358  Roo.data.Record.COMMIT
11359          * </code></pre>
11360          */
11361         update : true,
11362         /**
11363          * @event clear
11364          * Fires when the data cache has been cleared.
11365          * @param {Store} this
11366          */
11367         clear : true,
11368         /**
11369          * @event beforeload
11370          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11371          * the load action will be canceled.
11372          * @param {Store} this
11373          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11374          */
11375         beforeload : true,
11376         /**
11377          * @event beforeloadadd
11378          * Fires after a new set of Records has been loaded.
11379          * @param {Store} this
11380          * @param {Roo.data.Record[]} records The Records that were loaded
11381          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11382          */
11383         beforeloadadd : true,
11384         /**
11385          * @event load
11386          * Fires after a new set of Records has been loaded, before they are added to the store.
11387          * @param {Store} this
11388          * @param {Roo.data.Record[]} records The Records that were loaded
11389          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11390          * @params {Object} return from reader
11391          */
11392         load : true,
11393         /**
11394          * @event loadexception
11395          * Fires if an exception occurs in the Proxy during loading.
11396          * Called with the signature of the Proxy's "loadexception" event.
11397          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11398          * 
11399          * @param {Proxy} 
11400          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11401          * @param {Object} load options 
11402          * @param {Object} jsonData from your request (normally this contains the Exception)
11403          */
11404         loadexception : true
11405     });
11406     
11407     if(this.proxy){
11408         this.proxy = Roo.factory(this.proxy, Roo.data);
11409         this.proxy.xmodule = this.xmodule || false;
11410         this.relayEvents(this.proxy,  ["loadexception"]);
11411     }
11412     this.sortToggle = {};
11413     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11414
11415     Roo.data.Store.superclass.constructor.call(this);
11416
11417     if(this.inlineData){
11418         this.loadData(this.inlineData);
11419         delete this.inlineData;
11420     }
11421 };
11422
11423 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11424      /**
11425     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11426     * without a remote query - used by combo/forms at present.
11427     */
11428     
11429     /**
11430     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11431     */
11432     /**
11433     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11434     */
11435     /**
11436     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11437     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11438     */
11439     /**
11440     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11441     * on any HTTP request
11442     */
11443     /**
11444     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11445     */
11446     /**
11447     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11448     */
11449     multiSort: false,
11450     /**
11451     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11452     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11453     */
11454     remoteSort : false,
11455
11456     /**
11457     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11458      * loaded or when a record is removed. (defaults to false).
11459     */
11460     pruneModifiedRecords : false,
11461
11462     // private
11463     lastOptions : null,
11464
11465     /**
11466      * Add Records to the Store and fires the add event.
11467      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11468      */
11469     add : function(records){
11470         records = [].concat(records);
11471         for(var i = 0, len = records.length; i < len; i++){
11472             records[i].join(this);
11473         }
11474         var index = this.data.length;
11475         this.data.addAll(records);
11476         this.fireEvent("add", this, records, index);
11477     },
11478
11479     /**
11480      * Remove a Record from the Store and fires the remove event.
11481      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11482      */
11483     remove : function(record){
11484         var index = this.data.indexOf(record);
11485         this.data.removeAt(index);
11486  
11487         if(this.pruneModifiedRecords){
11488             this.modified.remove(record);
11489         }
11490         this.fireEvent("remove", this, record, index);
11491     },
11492
11493     /**
11494      * Remove all Records from the Store and fires the clear event.
11495      */
11496     removeAll : function(){
11497         this.data.clear();
11498         if(this.pruneModifiedRecords){
11499             this.modified = [];
11500         }
11501         this.fireEvent("clear", this);
11502     },
11503
11504     /**
11505      * Inserts Records to the Store at the given index and fires the add event.
11506      * @param {Number} index The start index at which to insert the passed Records.
11507      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11508      */
11509     insert : function(index, records){
11510         records = [].concat(records);
11511         for(var i = 0, len = records.length; i < len; i++){
11512             this.data.insert(index, records[i]);
11513             records[i].join(this);
11514         }
11515         this.fireEvent("add", this, records, index);
11516     },
11517
11518     /**
11519      * Get the index within the cache of the passed Record.
11520      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11521      * @return {Number} The index of the passed Record. Returns -1 if not found.
11522      */
11523     indexOf : function(record){
11524         return this.data.indexOf(record);
11525     },
11526
11527     /**
11528      * Get the index within the cache of the Record with the passed id.
11529      * @param {String} id The id of the Record to find.
11530      * @return {Number} The index of the Record. Returns -1 if not found.
11531      */
11532     indexOfId : function(id){
11533         return this.data.indexOfKey(id);
11534     },
11535
11536     /**
11537      * Get the Record with the specified id.
11538      * @param {String} id The id of the Record to find.
11539      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11540      */
11541     getById : function(id){
11542         return this.data.key(id);
11543     },
11544
11545     /**
11546      * Get the Record at the specified index.
11547      * @param {Number} index The index of the Record to find.
11548      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11549      */
11550     getAt : function(index){
11551         return this.data.itemAt(index);
11552     },
11553
11554     /**
11555      * Returns a range of Records between specified indices.
11556      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11557      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11558      * @return {Roo.data.Record[]} An array of Records
11559      */
11560     getRange : function(start, end){
11561         return this.data.getRange(start, end);
11562     },
11563
11564     // private
11565     storeOptions : function(o){
11566         o = Roo.apply({}, o);
11567         delete o.callback;
11568         delete o.scope;
11569         this.lastOptions = o;
11570     },
11571
11572     /**
11573      * Loads the Record cache from the configured Proxy using the configured Reader.
11574      * <p>
11575      * If using remote paging, then the first load call must specify the <em>start</em>
11576      * and <em>limit</em> properties in the options.params property to establish the initial
11577      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11578      * <p>
11579      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11580      * and this call will return before the new data has been loaded. Perform any post-processing
11581      * in a callback function, or in a "load" event handler.</strong>
11582      * <p>
11583      * @param {Object} options An object containing properties which control loading options:<ul>
11584      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11585      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11586      * passed the following arguments:<ul>
11587      * <li>r : Roo.data.Record[]</li>
11588      * <li>options: Options object from the load call</li>
11589      * <li>success: Boolean success indicator</li></ul></li>
11590      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11591      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11592      * </ul>
11593      */
11594     load : function(options){
11595         options = options || {};
11596         if(this.fireEvent("beforeload", this, options) !== false){
11597             this.storeOptions(options);
11598             var p = Roo.apply(options.params || {}, this.baseParams);
11599             // if meta was not loaded from remote source.. try requesting it.
11600             if (!this.reader.metaFromRemote) {
11601                 p._requestMeta = 1;
11602             }
11603             if(this.sortInfo && this.remoteSort){
11604                 var pn = this.paramNames;
11605                 p[pn["sort"]] = this.sortInfo.field;
11606                 p[pn["dir"]] = this.sortInfo.direction;
11607             }
11608             if (this.multiSort) {
11609                 var pn = this.paramNames;
11610                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11611             }
11612             
11613             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11614         }
11615     },
11616
11617     /**
11618      * Reloads the Record cache from the configured Proxy using the configured Reader and
11619      * the options from the last load operation performed.
11620      * @param {Object} options (optional) An object containing properties which may override the options
11621      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11622      * the most recently used options are reused).
11623      */
11624     reload : function(options){
11625         this.load(Roo.applyIf(options||{}, this.lastOptions));
11626     },
11627
11628     // private
11629     // Called as a callback by the Reader during a load operation.
11630     loadRecords : function(o, options, success){
11631         if(!o || success === false){
11632             if(success !== false){
11633                 this.fireEvent("load", this, [], options, o);
11634             }
11635             if(options.callback){
11636                 options.callback.call(options.scope || this, [], options, false);
11637             }
11638             return;
11639         }
11640         // if data returned failure - throw an exception.
11641         if (o.success === false) {
11642             // show a message if no listener is registered.
11643             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11644                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11645             }
11646             // loadmask wil be hooked into this..
11647             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11648             return;
11649         }
11650         var r = o.records, t = o.totalRecords || r.length;
11651         
11652         this.fireEvent("beforeloadadd", this, r, options, o);
11653         
11654         if(!options || options.add !== true){
11655             if(this.pruneModifiedRecords){
11656                 this.modified = [];
11657             }
11658             for(var i = 0, len = r.length; i < len; i++){
11659                 r[i].join(this);
11660             }
11661             if(this.snapshot){
11662                 this.data = this.snapshot;
11663                 delete this.snapshot;
11664             }
11665             this.data.clear();
11666             this.data.addAll(r);
11667             this.totalLength = t;
11668             this.applySort();
11669             this.fireEvent("datachanged", this);
11670         }else{
11671             this.totalLength = Math.max(t, this.data.length+r.length);
11672             this.add(r);
11673         }
11674         
11675         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11676                 
11677             var e = new Roo.data.Record({});
11678
11679             e.set(this.parent.displayField, this.parent.emptyTitle);
11680             e.set(this.parent.valueField, '');
11681
11682             this.insert(0, e);
11683         }
11684             
11685         this.fireEvent("load", this, r, options, o);
11686         if(options.callback){
11687             options.callback.call(options.scope || this, r, options, true);
11688         }
11689     },
11690
11691
11692     /**
11693      * Loads data from a passed data block. A Reader which understands the format of the data
11694      * must have been configured in the constructor.
11695      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11696      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11697      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11698      */
11699     loadData : function(o, append){
11700         var r = this.reader.readRecords(o);
11701         this.loadRecords(r, {add: append}, true);
11702     },
11703
11704     /**
11705      * Gets the number of cached records.
11706      * <p>
11707      * <em>If using paging, this may not be the total size of the dataset. If the data object
11708      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11709      * the data set size</em>
11710      */
11711     getCount : function(){
11712         return this.data.length || 0;
11713     },
11714
11715     /**
11716      * Gets the total number of records in the dataset as returned by the server.
11717      * <p>
11718      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11719      * the dataset size</em>
11720      */
11721     getTotalCount : function(){
11722         return this.totalLength || 0;
11723     },
11724
11725     /**
11726      * Returns the sort state of the Store as an object with two properties:
11727      * <pre><code>
11728  field {String} The name of the field by which the Records are sorted
11729  direction {String} The sort order, "ASC" or "DESC"
11730      * </code></pre>
11731      */
11732     getSortState : function(){
11733         return this.sortInfo;
11734     },
11735
11736     // private
11737     applySort : function(){
11738         if(this.sortInfo && !this.remoteSort){
11739             var s = this.sortInfo, f = s.field;
11740             var st = this.fields.get(f).sortType;
11741             var fn = function(r1, r2){
11742                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11743                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11744             };
11745             this.data.sort(s.direction, fn);
11746             if(this.snapshot && this.snapshot != this.data){
11747                 this.snapshot.sort(s.direction, fn);
11748             }
11749         }
11750     },
11751
11752     /**
11753      * Sets the default sort column and order to be used by the next load operation.
11754      * @param {String} fieldName The name of the field to sort by.
11755      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11756      */
11757     setDefaultSort : function(field, dir){
11758         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11759     },
11760
11761     /**
11762      * Sort the Records.
11763      * If remote sorting is used, the sort is performed on the server, and the cache is
11764      * reloaded. If local sorting is used, the cache is sorted internally.
11765      * @param {String} fieldName The name of the field to sort by.
11766      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11767      */
11768     sort : function(fieldName, dir){
11769         var f = this.fields.get(fieldName);
11770         if(!dir){
11771             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11772             
11773             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11774                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11775             }else{
11776                 dir = f.sortDir;
11777             }
11778         }
11779         this.sortToggle[f.name] = dir;
11780         this.sortInfo = {field: f.name, direction: dir};
11781         if(!this.remoteSort){
11782             this.applySort();
11783             this.fireEvent("datachanged", this);
11784         }else{
11785             this.load(this.lastOptions);
11786         }
11787     },
11788
11789     /**
11790      * Calls the specified function for each of the Records in the cache.
11791      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11792      * Returning <em>false</em> aborts and exits the iteration.
11793      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11794      */
11795     each : function(fn, scope){
11796         this.data.each(fn, scope);
11797     },
11798
11799     /**
11800      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11801      * (e.g., during paging).
11802      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11803      */
11804     getModifiedRecords : function(){
11805         return this.modified;
11806     },
11807
11808     // private
11809     createFilterFn : function(property, value, anyMatch){
11810         if(!value.exec){ // not a regex
11811             value = String(value);
11812             if(value.length == 0){
11813                 return false;
11814             }
11815             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11816         }
11817         return function(r){
11818             return value.test(r.data[property]);
11819         };
11820     },
11821
11822     /**
11823      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11824      * @param {String} property A field on your records
11825      * @param {Number} start The record index to start at (defaults to 0)
11826      * @param {Number} end The last record index to include (defaults to length - 1)
11827      * @return {Number} The sum
11828      */
11829     sum : function(property, start, end){
11830         var rs = this.data.items, v = 0;
11831         start = start || 0;
11832         end = (end || end === 0) ? end : rs.length-1;
11833
11834         for(var i = start; i <= end; i++){
11835             v += (rs[i].data[property] || 0);
11836         }
11837         return v;
11838     },
11839
11840     /**
11841      * Filter the records by a specified property.
11842      * @param {String} field A field on your records
11843      * @param {String/RegExp} value Either a string that the field
11844      * should start with or a RegExp to test against the field
11845      * @param {Boolean} anyMatch True to match any part not just the beginning
11846      */
11847     filter : function(property, value, anyMatch){
11848         var fn = this.createFilterFn(property, value, anyMatch);
11849         return fn ? this.filterBy(fn) : this.clearFilter();
11850     },
11851
11852     /**
11853      * Filter by a function. The specified function will be called with each
11854      * record in this data source. If the function returns true the record is included,
11855      * otherwise it is filtered.
11856      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11857      * @param {Object} scope (optional) The scope of the function (defaults to this)
11858      */
11859     filterBy : function(fn, scope){
11860         this.snapshot = this.snapshot || this.data;
11861         this.data = this.queryBy(fn, scope||this);
11862         this.fireEvent("datachanged", this);
11863     },
11864
11865     /**
11866      * Query the records by a specified property.
11867      * @param {String} field A field on your records
11868      * @param {String/RegExp} value Either a string that the field
11869      * should start with or a RegExp to test against the field
11870      * @param {Boolean} anyMatch True to match any part not just the beginning
11871      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11872      */
11873     query : function(property, value, anyMatch){
11874         var fn = this.createFilterFn(property, value, anyMatch);
11875         return fn ? this.queryBy(fn) : this.data.clone();
11876     },
11877
11878     /**
11879      * Query by a function. The specified function will be called with each
11880      * record in this data source. If the function returns true the record is included
11881      * in the results.
11882      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11883      * @param {Object} scope (optional) The scope of the function (defaults to this)
11884       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11885      **/
11886     queryBy : function(fn, scope){
11887         var data = this.snapshot || this.data;
11888         return data.filterBy(fn, scope||this);
11889     },
11890
11891     /**
11892      * Collects unique values for a particular dataIndex from this store.
11893      * @param {String} dataIndex The property to collect
11894      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11895      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11896      * @return {Array} An array of the unique values
11897      **/
11898     collect : function(dataIndex, allowNull, bypassFilter){
11899         var d = (bypassFilter === true && this.snapshot) ?
11900                 this.snapshot.items : this.data.items;
11901         var v, sv, r = [], l = {};
11902         for(var i = 0, len = d.length; i < len; i++){
11903             v = d[i].data[dataIndex];
11904             sv = String(v);
11905             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11906                 l[sv] = true;
11907                 r[r.length] = v;
11908             }
11909         }
11910         return r;
11911     },
11912
11913     /**
11914      * Revert to a view of the Record cache with no filtering applied.
11915      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11916      */
11917     clearFilter : function(suppressEvent){
11918         if(this.snapshot && this.snapshot != this.data){
11919             this.data = this.snapshot;
11920             delete this.snapshot;
11921             if(suppressEvent !== true){
11922                 this.fireEvent("datachanged", this);
11923             }
11924         }
11925     },
11926
11927     // private
11928     afterEdit : function(record){
11929         if(this.modified.indexOf(record) == -1){
11930             this.modified.push(record);
11931         }
11932         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11933     },
11934     
11935     // private
11936     afterReject : function(record){
11937         this.modified.remove(record);
11938         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11939     },
11940
11941     // private
11942     afterCommit : function(record){
11943         this.modified.remove(record);
11944         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11945     },
11946
11947     /**
11948      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11949      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11950      */
11951     commitChanges : function(){
11952         var m = this.modified.slice(0);
11953         this.modified = [];
11954         for(var i = 0, len = m.length; i < len; i++){
11955             m[i].commit();
11956         }
11957     },
11958
11959     /**
11960      * Cancel outstanding changes on all changed records.
11961      */
11962     rejectChanges : function(){
11963         var m = this.modified.slice(0);
11964         this.modified = [];
11965         for(var i = 0, len = m.length; i < len; i++){
11966             m[i].reject();
11967         }
11968     },
11969
11970     onMetaChange : function(meta, rtype, o){
11971         this.recordType = rtype;
11972         this.fields = rtype.prototype.fields;
11973         delete this.snapshot;
11974         this.sortInfo = meta.sortInfo || this.sortInfo;
11975         this.modified = [];
11976         this.fireEvent('metachange', this, this.reader.meta);
11977     },
11978     
11979     moveIndex : function(data, type)
11980     {
11981         var index = this.indexOf(data);
11982         
11983         var newIndex = index + type;
11984         
11985         this.remove(data);
11986         
11987         this.insert(newIndex, data);
11988         
11989     }
11990 });/*
11991  * Based on:
11992  * Ext JS Library 1.1.1
11993  * Copyright(c) 2006-2007, Ext JS, LLC.
11994  *
11995  * Originally Released Under LGPL - original licence link has changed is not relivant.
11996  *
11997  * Fork - LGPL
11998  * <script type="text/javascript">
11999  */
12000
12001 /**
12002  * @class Roo.data.SimpleStore
12003  * @extends Roo.data.Store
12004  * Small helper class to make creating Stores from Array data easier.
12005  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12006  * @cfg {Array} fields An array of field definition objects, or field name strings.
12007  * @cfg {Array} data The multi-dimensional array of data
12008  * @constructor
12009  * @param {Object} config
12010  */
12011 Roo.data.SimpleStore = function(config){
12012     Roo.data.SimpleStore.superclass.constructor.call(this, {
12013         isLocal : true,
12014         reader: new Roo.data.ArrayReader({
12015                 id: config.id
12016             },
12017             Roo.data.Record.create(config.fields)
12018         ),
12019         proxy : new Roo.data.MemoryProxy(config.data)
12020     });
12021     this.load();
12022 };
12023 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12024  * Based on:
12025  * Ext JS Library 1.1.1
12026  * Copyright(c) 2006-2007, Ext JS, LLC.
12027  *
12028  * Originally Released Under LGPL - original licence link has changed is not relivant.
12029  *
12030  * Fork - LGPL
12031  * <script type="text/javascript">
12032  */
12033
12034 /**
12035 /**
12036  * @extends Roo.data.Store
12037  * @class Roo.data.JsonStore
12038  * Small helper class to make creating Stores for JSON data easier. <br/>
12039 <pre><code>
12040 var store = new Roo.data.JsonStore({
12041     url: 'get-images.php',
12042     root: 'images',
12043     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12044 });
12045 </code></pre>
12046  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12047  * JsonReader and HttpProxy (unless inline data is provided).</b>
12048  * @cfg {Array} fields An array of field definition objects, or field name strings.
12049  * @constructor
12050  * @param {Object} config
12051  */
12052 Roo.data.JsonStore = function(c){
12053     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12054         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12055         reader: new Roo.data.JsonReader(c, c.fields)
12056     }));
12057 };
12058 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12059  * Based on:
12060  * Ext JS Library 1.1.1
12061  * Copyright(c) 2006-2007, Ext JS, LLC.
12062  *
12063  * Originally Released Under LGPL - original licence link has changed is not relivant.
12064  *
12065  * Fork - LGPL
12066  * <script type="text/javascript">
12067  */
12068
12069  
12070 Roo.data.Field = function(config){
12071     if(typeof config == "string"){
12072         config = {name: config};
12073     }
12074     Roo.apply(this, config);
12075     
12076     if(!this.type){
12077         this.type = "auto";
12078     }
12079     
12080     var st = Roo.data.SortTypes;
12081     // named sortTypes are supported, here we look them up
12082     if(typeof this.sortType == "string"){
12083         this.sortType = st[this.sortType];
12084     }
12085     
12086     // set default sortType for strings and dates
12087     if(!this.sortType){
12088         switch(this.type){
12089             case "string":
12090                 this.sortType = st.asUCString;
12091                 break;
12092             case "date":
12093                 this.sortType = st.asDate;
12094                 break;
12095             default:
12096                 this.sortType = st.none;
12097         }
12098     }
12099
12100     // define once
12101     var stripRe = /[\$,%]/g;
12102
12103     // prebuilt conversion function for this field, instead of
12104     // switching every time we're reading a value
12105     if(!this.convert){
12106         var cv, dateFormat = this.dateFormat;
12107         switch(this.type){
12108             case "":
12109             case "auto":
12110             case undefined:
12111                 cv = function(v){ return v; };
12112                 break;
12113             case "string":
12114                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12115                 break;
12116             case "int":
12117                 cv = function(v){
12118                     return v !== undefined && v !== null && v !== '' ?
12119                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12120                     };
12121                 break;
12122             case "float":
12123                 cv = function(v){
12124                     return v !== undefined && v !== null && v !== '' ?
12125                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12126                     };
12127                 break;
12128             case "bool":
12129             case "boolean":
12130                 cv = function(v){ return v === true || v === "true" || v == 1; };
12131                 break;
12132             case "date":
12133                 cv = function(v){
12134                     if(!v){
12135                         return '';
12136                     }
12137                     if(v instanceof Date){
12138                         return v;
12139                     }
12140                     if(dateFormat){
12141                         if(dateFormat == "timestamp"){
12142                             return new Date(v*1000);
12143                         }
12144                         return Date.parseDate(v, dateFormat);
12145                     }
12146                     var parsed = Date.parse(v);
12147                     return parsed ? new Date(parsed) : null;
12148                 };
12149              break;
12150             
12151         }
12152         this.convert = cv;
12153     }
12154 };
12155
12156 Roo.data.Field.prototype = {
12157     dateFormat: null,
12158     defaultValue: "",
12159     mapping: null,
12160     sortType : null,
12161     sortDir : "ASC"
12162 };/*
12163  * Based on:
12164  * Ext JS Library 1.1.1
12165  * Copyright(c) 2006-2007, Ext JS, LLC.
12166  *
12167  * Originally Released Under LGPL - original licence link has changed is not relivant.
12168  *
12169  * Fork - LGPL
12170  * <script type="text/javascript">
12171  */
12172  
12173 // Base class for reading structured data from a data source.  This class is intended to be
12174 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12175
12176 /**
12177  * @class Roo.data.DataReader
12178  * Base class for reading structured data from a data source.  This class is intended to be
12179  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12180  */
12181
12182 Roo.data.DataReader = function(meta, recordType){
12183     
12184     this.meta = meta;
12185     
12186     this.recordType = recordType instanceof Array ? 
12187         Roo.data.Record.create(recordType) : recordType;
12188 };
12189
12190 Roo.data.DataReader.prototype = {
12191      /**
12192      * Create an empty record
12193      * @param {Object} data (optional) - overlay some values
12194      * @return {Roo.data.Record} record created.
12195      */
12196     newRow :  function(d) {
12197         var da =  {};
12198         this.recordType.prototype.fields.each(function(c) {
12199             switch( c.type) {
12200                 case 'int' : da[c.name] = 0; break;
12201                 case 'date' : da[c.name] = new Date(); break;
12202                 case 'float' : da[c.name] = 0.0; break;
12203                 case 'boolean' : da[c.name] = false; break;
12204                 default : da[c.name] = ""; break;
12205             }
12206             
12207         });
12208         return new this.recordType(Roo.apply(da, d));
12209     }
12210     
12211 };/*
12212  * Based on:
12213  * Ext JS Library 1.1.1
12214  * Copyright(c) 2006-2007, Ext JS, LLC.
12215  *
12216  * Originally Released Under LGPL - original licence link has changed is not relivant.
12217  *
12218  * Fork - LGPL
12219  * <script type="text/javascript">
12220  */
12221
12222 /**
12223  * @class Roo.data.DataProxy
12224  * @extends Roo.data.Observable
12225  * This class is an abstract base class for implementations which provide retrieval of
12226  * unformatted data objects.<br>
12227  * <p>
12228  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12229  * (of the appropriate type which knows how to parse the data object) to provide a block of
12230  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12231  * <p>
12232  * Custom implementations must implement the load method as described in
12233  * {@link Roo.data.HttpProxy#load}.
12234  */
12235 Roo.data.DataProxy = function(){
12236     this.addEvents({
12237         /**
12238          * @event beforeload
12239          * Fires before a network request is made to retrieve a data object.
12240          * @param {Object} This DataProxy object.
12241          * @param {Object} params The params parameter to the load function.
12242          */
12243         beforeload : true,
12244         /**
12245          * @event load
12246          * Fires before the load method's callback is called.
12247          * @param {Object} This DataProxy object.
12248          * @param {Object} o The data object.
12249          * @param {Object} arg The callback argument object passed to the load function.
12250          */
12251         load : true,
12252         /**
12253          * @event loadexception
12254          * Fires if an Exception occurs during data retrieval.
12255          * @param {Object} This DataProxy object.
12256          * @param {Object} o The data object.
12257          * @param {Object} arg The callback argument object passed to the load function.
12258          * @param {Object} e The Exception.
12259          */
12260         loadexception : true
12261     });
12262     Roo.data.DataProxy.superclass.constructor.call(this);
12263 };
12264
12265 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12266
12267     /**
12268      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12269      */
12270 /*
12271  * Based on:
12272  * Ext JS Library 1.1.1
12273  * Copyright(c) 2006-2007, Ext JS, LLC.
12274  *
12275  * Originally Released Under LGPL - original licence link has changed is not relivant.
12276  *
12277  * Fork - LGPL
12278  * <script type="text/javascript">
12279  */
12280 /**
12281  * @class Roo.data.MemoryProxy
12282  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12283  * to the Reader when its load method is called.
12284  * @constructor
12285  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12286  */
12287 Roo.data.MemoryProxy = function(data){
12288     if (data.data) {
12289         data = data.data;
12290     }
12291     Roo.data.MemoryProxy.superclass.constructor.call(this);
12292     this.data = data;
12293 };
12294
12295 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12296     
12297     /**
12298      * Load data from the requested source (in this case an in-memory
12299      * data object passed to the constructor), read the data object into
12300      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12301      * process that block using the passed callback.
12302      * @param {Object} params This parameter is not used by the MemoryProxy class.
12303      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12304      * object into a block of Roo.data.Records.
12305      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12306      * The function must be passed <ul>
12307      * <li>The Record block object</li>
12308      * <li>The "arg" argument from the load function</li>
12309      * <li>A boolean success indicator</li>
12310      * </ul>
12311      * @param {Object} scope The scope in which to call the callback
12312      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12313      */
12314     load : function(params, reader, callback, scope, arg){
12315         params = params || {};
12316         var result;
12317         try {
12318             result = reader.readRecords(this.data);
12319         }catch(e){
12320             this.fireEvent("loadexception", this, arg, null, e);
12321             callback.call(scope, null, arg, false);
12322             return;
12323         }
12324         callback.call(scope, result, arg, true);
12325     },
12326     
12327     // private
12328     update : function(params, records){
12329         
12330     }
12331 });/*
12332  * Based on:
12333  * Ext JS Library 1.1.1
12334  * Copyright(c) 2006-2007, Ext JS, LLC.
12335  *
12336  * Originally Released Under LGPL - original licence link has changed is not relivant.
12337  *
12338  * Fork - LGPL
12339  * <script type="text/javascript">
12340  */
12341 /**
12342  * @class Roo.data.HttpProxy
12343  * @extends Roo.data.DataProxy
12344  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12345  * configured to reference a certain URL.<br><br>
12346  * <p>
12347  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12348  * from which the running page was served.<br><br>
12349  * <p>
12350  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12351  * <p>
12352  * Be aware that to enable the browser to parse an XML document, the server must set
12353  * the Content-Type header in the HTTP response to "text/xml".
12354  * @constructor
12355  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12356  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12357  * will be used to make the request.
12358  */
12359 Roo.data.HttpProxy = function(conn){
12360     Roo.data.HttpProxy.superclass.constructor.call(this);
12361     // is conn a conn config or a real conn?
12362     this.conn = conn;
12363     this.useAjax = !conn || !conn.events;
12364   
12365 };
12366
12367 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12368     // thse are take from connection...
12369     
12370     /**
12371      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12372      */
12373     /**
12374      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12375      * extra parameters to each request made by this object. (defaults to undefined)
12376      */
12377     /**
12378      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12379      *  to each request made by this object. (defaults to undefined)
12380      */
12381     /**
12382      * @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)
12383      */
12384     /**
12385      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12386      */
12387      /**
12388      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12389      * @type Boolean
12390      */
12391   
12392
12393     /**
12394      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12395      * @type Boolean
12396      */
12397     /**
12398      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12399      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12400      * a finer-grained basis than the DataProxy events.
12401      */
12402     getConnection : function(){
12403         return this.useAjax ? Roo.Ajax : this.conn;
12404     },
12405
12406     /**
12407      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12408      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12409      * process that block using the passed callback.
12410      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12411      * for the request to the remote server.
12412      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12413      * object into a block of Roo.data.Records.
12414      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12415      * The function must be passed <ul>
12416      * <li>The Record block object</li>
12417      * <li>The "arg" argument from the load function</li>
12418      * <li>A boolean success indicator</li>
12419      * </ul>
12420      * @param {Object} scope The scope in which to call the callback
12421      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12422      */
12423     load : function(params, reader, callback, scope, arg){
12424         if(this.fireEvent("beforeload", this, params) !== false){
12425             var  o = {
12426                 params : params || {},
12427                 request: {
12428                     callback : callback,
12429                     scope : scope,
12430                     arg : arg
12431                 },
12432                 reader: reader,
12433                 callback : this.loadResponse,
12434                 scope: this
12435             };
12436             if(this.useAjax){
12437                 Roo.applyIf(o, this.conn);
12438                 if(this.activeRequest){
12439                     Roo.Ajax.abort(this.activeRequest);
12440                 }
12441                 this.activeRequest = Roo.Ajax.request(o);
12442             }else{
12443                 this.conn.request(o);
12444             }
12445         }else{
12446             callback.call(scope||this, null, arg, false);
12447         }
12448     },
12449
12450     // private
12451     loadResponse : function(o, success, response){
12452         delete this.activeRequest;
12453         if(!success){
12454             this.fireEvent("loadexception", this, o, response);
12455             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12456             return;
12457         }
12458         var result;
12459         try {
12460             result = o.reader.read(response);
12461         }catch(e){
12462             this.fireEvent("loadexception", this, o, response, e);
12463             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12464             return;
12465         }
12466         
12467         this.fireEvent("load", this, o, o.request.arg);
12468         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12469     },
12470
12471     // private
12472     update : function(dataSet){
12473
12474     },
12475
12476     // private
12477     updateResponse : function(dataSet){
12478
12479     }
12480 });/*
12481  * Based on:
12482  * Ext JS Library 1.1.1
12483  * Copyright(c) 2006-2007, Ext JS, LLC.
12484  *
12485  * Originally Released Under LGPL - original licence link has changed is not relivant.
12486  *
12487  * Fork - LGPL
12488  * <script type="text/javascript">
12489  */
12490
12491 /**
12492  * @class Roo.data.ScriptTagProxy
12493  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12494  * other than the originating domain of the running page.<br><br>
12495  * <p>
12496  * <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
12497  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12498  * <p>
12499  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12500  * source code that is used as the source inside a &lt;script> tag.<br><br>
12501  * <p>
12502  * In order for the browser to process the returned data, the server must wrap the data object
12503  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12504  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12505  * depending on whether the callback name was passed:
12506  * <p>
12507  * <pre><code>
12508 boolean scriptTag = false;
12509 String cb = request.getParameter("callback");
12510 if (cb != null) {
12511     scriptTag = true;
12512     response.setContentType("text/javascript");
12513 } else {
12514     response.setContentType("application/x-json");
12515 }
12516 Writer out = response.getWriter();
12517 if (scriptTag) {
12518     out.write(cb + "(");
12519 }
12520 out.print(dataBlock.toJsonString());
12521 if (scriptTag) {
12522     out.write(");");
12523 }
12524 </pre></code>
12525  *
12526  * @constructor
12527  * @param {Object} config A configuration object.
12528  */
12529 Roo.data.ScriptTagProxy = function(config){
12530     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12531     Roo.apply(this, config);
12532     this.head = document.getElementsByTagName("head")[0];
12533 };
12534
12535 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12536
12537 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12538     /**
12539      * @cfg {String} url The URL from which to request the data object.
12540      */
12541     /**
12542      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12543      */
12544     timeout : 30000,
12545     /**
12546      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12547      * the server the name of the callback function set up by the load call to process the returned data object.
12548      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12549      * javascript output which calls this named function passing the data object as its only parameter.
12550      */
12551     callbackParam : "callback",
12552     /**
12553      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12554      * name to the request.
12555      */
12556     nocache : true,
12557
12558     /**
12559      * Load data from the configured URL, read the data object into
12560      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12561      * process that block using the passed callback.
12562      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12563      * for the request to the remote server.
12564      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12565      * object into a block of Roo.data.Records.
12566      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12567      * The function must be passed <ul>
12568      * <li>The Record block object</li>
12569      * <li>The "arg" argument from the load function</li>
12570      * <li>A boolean success indicator</li>
12571      * </ul>
12572      * @param {Object} scope The scope in which to call the callback
12573      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12574      */
12575     load : function(params, reader, callback, scope, arg){
12576         if(this.fireEvent("beforeload", this, params) !== false){
12577
12578             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12579
12580             var url = this.url;
12581             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12582             if(this.nocache){
12583                 url += "&_dc=" + (new Date().getTime());
12584             }
12585             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12586             var trans = {
12587                 id : transId,
12588                 cb : "stcCallback"+transId,
12589                 scriptId : "stcScript"+transId,
12590                 params : params,
12591                 arg : arg,
12592                 url : url,
12593                 callback : callback,
12594                 scope : scope,
12595                 reader : reader
12596             };
12597             var conn = this;
12598
12599             window[trans.cb] = function(o){
12600                 conn.handleResponse(o, trans);
12601             };
12602
12603             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12604
12605             if(this.autoAbort !== false){
12606                 this.abort();
12607             }
12608
12609             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12610
12611             var script = document.createElement("script");
12612             script.setAttribute("src", url);
12613             script.setAttribute("type", "text/javascript");
12614             script.setAttribute("id", trans.scriptId);
12615             this.head.appendChild(script);
12616
12617             this.trans = trans;
12618         }else{
12619             callback.call(scope||this, null, arg, false);
12620         }
12621     },
12622
12623     // private
12624     isLoading : function(){
12625         return this.trans ? true : false;
12626     },
12627
12628     /**
12629      * Abort the current server request.
12630      */
12631     abort : function(){
12632         if(this.isLoading()){
12633             this.destroyTrans(this.trans);
12634         }
12635     },
12636
12637     // private
12638     destroyTrans : function(trans, isLoaded){
12639         this.head.removeChild(document.getElementById(trans.scriptId));
12640         clearTimeout(trans.timeoutId);
12641         if(isLoaded){
12642             window[trans.cb] = undefined;
12643             try{
12644                 delete window[trans.cb];
12645             }catch(e){}
12646         }else{
12647             // if hasn't been loaded, wait for load to remove it to prevent script error
12648             window[trans.cb] = function(){
12649                 window[trans.cb] = undefined;
12650                 try{
12651                     delete window[trans.cb];
12652                 }catch(e){}
12653             };
12654         }
12655     },
12656
12657     // private
12658     handleResponse : function(o, trans){
12659         this.trans = false;
12660         this.destroyTrans(trans, true);
12661         var result;
12662         try {
12663             result = trans.reader.readRecords(o);
12664         }catch(e){
12665             this.fireEvent("loadexception", this, o, trans.arg, e);
12666             trans.callback.call(trans.scope||window, null, trans.arg, false);
12667             return;
12668         }
12669         this.fireEvent("load", this, o, trans.arg);
12670         trans.callback.call(trans.scope||window, result, trans.arg, true);
12671     },
12672
12673     // private
12674     handleFailure : function(trans){
12675         this.trans = false;
12676         this.destroyTrans(trans, false);
12677         this.fireEvent("loadexception", this, null, trans.arg);
12678         trans.callback.call(trans.scope||window, null, trans.arg, false);
12679     }
12680 });/*
12681  * Based on:
12682  * Ext JS Library 1.1.1
12683  * Copyright(c) 2006-2007, Ext JS, LLC.
12684  *
12685  * Originally Released Under LGPL - original licence link has changed is not relivant.
12686  *
12687  * Fork - LGPL
12688  * <script type="text/javascript">
12689  */
12690
12691 /**
12692  * @class Roo.data.JsonReader
12693  * @extends Roo.data.DataReader
12694  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12695  * based on mappings in a provided Roo.data.Record constructor.
12696  * 
12697  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12698  * in the reply previously. 
12699  * 
12700  * <p>
12701  * Example code:
12702  * <pre><code>
12703 var RecordDef = Roo.data.Record.create([
12704     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12705     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12706 ]);
12707 var myReader = new Roo.data.JsonReader({
12708     totalProperty: "results",    // The property which contains the total dataset size (optional)
12709     root: "rows",                // The property which contains an Array of row objects
12710     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12711 }, RecordDef);
12712 </code></pre>
12713  * <p>
12714  * This would consume a JSON file like this:
12715  * <pre><code>
12716 { 'results': 2, 'rows': [
12717     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12718     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12719 }
12720 </code></pre>
12721  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12722  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12723  * paged from the remote server.
12724  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12725  * @cfg {String} root name of the property which contains the Array of row objects.
12726  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12727  * @cfg {Array} fields Array of field definition objects
12728  * @constructor
12729  * Create a new JsonReader
12730  * @param {Object} meta Metadata configuration options
12731  * @param {Object} recordType Either an Array of field definition objects,
12732  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12733  */
12734 Roo.data.JsonReader = function(meta, recordType){
12735     
12736     meta = meta || {};
12737     // set some defaults:
12738     Roo.applyIf(meta, {
12739         totalProperty: 'total',
12740         successProperty : 'success',
12741         root : 'data',
12742         id : 'id'
12743     });
12744     
12745     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12746 };
12747 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12748     
12749     /**
12750      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12751      * Used by Store query builder to append _requestMeta to params.
12752      * 
12753      */
12754     metaFromRemote : false,
12755     /**
12756      * This method is only used by a DataProxy which has retrieved data from a remote server.
12757      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12758      * @return {Object} data A data block which is used by an Roo.data.Store object as
12759      * a cache of Roo.data.Records.
12760      */
12761     read : function(response){
12762         var json = response.responseText;
12763        
12764         var o = /* eval:var:o */ eval("("+json+")");
12765         if(!o) {
12766             throw {message: "JsonReader.read: Json object not found"};
12767         }
12768         
12769         if(o.metaData){
12770             
12771             delete this.ef;
12772             this.metaFromRemote = true;
12773             this.meta = o.metaData;
12774             this.recordType = Roo.data.Record.create(o.metaData.fields);
12775             this.onMetaChange(this.meta, this.recordType, o);
12776         }
12777         return this.readRecords(o);
12778     },
12779
12780     // private function a store will implement
12781     onMetaChange : function(meta, recordType, o){
12782
12783     },
12784
12785     /**
12786          * @ignore
12787          */
12788     simpleAccess: function(obj, subsc) {
12789         return obj[subsc];
12790     },
12791
12792         /**
12793          * @ignore
12794          */
12795     getJsonAccessor: function(){
12796         var re = /[\[\.]/;
12797         return function(expr) {
12798             try {
12799                 return(re.test(expr))
12800                     ? new Function("obj", "return obj." + expr)
12801                     : function(obj){
12802                         return obj[expr];
12803                     };
12804             } catch(e){}
12805             return Roo.emptyFn;
12806         };
12807     }(),
12808
12809     /**
12810      * Create a data block containing Roo.data.Records from an XML document.
12811      * @param {Object} o An object which contains an Array of row objects in the property specified
12812      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12813      * which contains the total size of the dataset.
12814      * @return {Object} data A data block which is used by an Roo.data.Store object as
12815      * a cache of Roo.data.Records.
12816      */
12817     readRecords : function(o){
12818         /**
12819          * After any data loads, the raw JSON data is available for further custom processing.
12820          * @type Object
12821          */
12822         this.o = o;
12823         var s = this.meta, Record = this.recordType,
12824             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12825
12826 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12827         if (!this.ef) {
12828             if(s.totalProperty) {
12829                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12830                 }
12831                 if(s.successProperty) {
12832                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12833                 }
12834                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12835                 if (s.id) {
12836                         var g = this.getJsonAccessor(s.id);
12837                         this.getId = function(rec) {
12838                                 var r = g(rec);  
12839                                 return (r === undefined || r === "") ? null : r;
12840                         };
12841                 } else {
12842                         this.getId = function(){return null;};
12843                 }
12844             this.ef = [];
12845             for(var jj = 0; jj < fl; jj++){
12846                 f = fi[jj];
12847                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12848                 this.ef[jj] = this.getJsonAccessor(map);
12849             }
12850         }
12851
12852         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12853         if(s.totalProperty){
12854             var vt = parseInt(this.getTotal(o), 10);
12855             if(!isNaN(vt)){
12856                 totalRecords = vt;
12857             }
12858         }
12859         if(s.successProperty){
12860             var vs = this.getSuccess(o);
12861             if(vs === false || vs === 'false'){
12862                 success = false;
12863             }
12864         }
12865         var records = [];
12866         for(var i = 0; i < c; i++){
12867                 var n = root[i];
12868             var values = {};
12869             var id = this.getId(n);
12870             for(var j = 0; j < fl; j++){
12871                 f = fi[j];
12872             var v = this.ef[j](n);
12873             if (!f.convert) {
12874                 Roo.log('missing convert for ' + f.name);
12875                 Roo.log(f);
12876                 continue;
12877             }
12878             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12879             }
12880             var record = new Record(values, id);
12881             record.json = n;
12882             records[i] = record;
12883         }
12884         return {
12885             raw : o,
12886             success : success,
12887             records : records,
12888             totalRecords : totalRecords
12889         };
12890     }
12891 });/*
12892  * Based on:
12893  * Ext JS Library 1.1.1
12894  * Copyright(c) 2006-2007, Ext JS, LLC.
12895  *
12896  * Originally Released Under LGPL - original licence link has changed is not relivant.
12897  *
12898  * Fork - LGPL
12899  * <script type="text/javascript">
12900  */
12901
12902 /**
12903  * @class Roo.data.ArrayReader
12904  * @extends Roo.data.DataReader
12905  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12906  * Each element of that Array represents a row of data fields. The
12907  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12908  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12909  * <p>
12910  * Example code:.
12911  * <pre><code>
12912 var RecordDef = Roo.data.Record.create([
12913     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12914     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12915 ]);
12916 var myReader = new Roo.data.ArrayReader({
12917     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12918 }, RecordDef);
12919 </code></pre>
12920  * <p>
12921  * This would consume an Array like this:
12922  * <pre><code>
12923 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12924   </code></pre>
12925  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12926  * @constructor
12927  * Create a new JsonReader
12928  * @param {Object} meta Metadata configuration options.
12929  * @param {Object} recordType Either an Array of field definition objects
12930  * as specified to {@link Roo.data.Record#create},
12931  * or an {@link Roo.data.Record} object
12932  * created using {@link Roo.data.Record#create}.
12933  */
12934 Roo.data.ArrayReader = function(meta, recordType){
12935     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12936 };
12937
12938 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12939     /**
12940      * Create a data block containing Roo.data.Records from an XML document.
12941      * @param {Object} o An Array of row objects which represents the dataset.
12942      * @return {Object} data A data block which is used by an Roo.data.Store object as
12943      * a cache of Roo.data.Records.
12944      */
12945     readRecords : function(o){
12946         var sid = this.meta ? this.meta.id : null;
12947         var recordType = this.recordType, fields = recordType.prototype.fields;
12948         var records = [];
12949         var root = o;
12950             for(var i = 0; i < root.length; i++){
12951                     var n = root[i];
12952                 var values = {};
12953                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12954                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12955                 var f = fields.items[j];
12956                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12957                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12958                 v = f.convert(v);
12959                 values[f.name] = v;
12960             }
12961                 var record = new recordType(values, id);
12962                 record.json = n;
12963                 records[records.length] = record;
12964             }
12965             return {
12966                 records : records,
12967                 totalRecords : records.length
12968             };
12969     }
12970 });/*
12971  * - LGPL
12972  * * 
12973  */
12974
12975 /**
12976  * @class Roo.bootstrap.ComboBox
12977  * @extends Roo.bootstrap.TriggerField
12978  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12979  * @cfg {Boolean} append (true|false) default false
12980  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12981  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12982  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12983  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12984  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12985  * @cfg {Boolean} animate default true
12986  * @cfg {Boolean} emptyResultText only for touch device
12987  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12988  * @cfg {String} emptyTitle default ''
12989  * @constructor
12990  * Create a new ComboBox.
12991  * @param {Object} config Configuration options
12992  */
12993 Roo.bootstrap.ComboBox = function(config){
12994     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12995     this.addEvents({
12996         /**
12997          * @event expand
12998          * Fires when the dropdown list is expanded
12999         * @param {Roo.bootstrap.ComboBox} combo This combo box
13000         */
13001         'expand' : true,
13002         /**
13003          * @event collapse
13004          * Fires when the dropdown list is collapsed
13005         * @param {Roo.bootstrap.ComboBox} combo This combo box
13006         */
13007         'collapse' : true,
13008         /**
13009          * @event beforeselect
13010          * Fires before a list item is selected. Return false to cancel the selection.
13011         * @param {Roo.bootstrap.ComboBox} combo This combo box
13012         * @param {Roo.data.Record} record The data record returned from the underlying store
13013         * @param {Number} index The index of the selected item in the dropdown list
13014         */
13015         'beforeselect' : true,
13016         /**
13017          * @event select
13018          * Fires when a list item is selected
13019         * @param {Roo.bootstrap.ComboBox} combo This combo box
13020         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13021         * @param {Number} index The index of the selected item in the dropdown list
13022         */
13023         'select' : true,
13024         /**
13025          * @event beforequery
13026          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13027          * The event object passed has these properties:
13028         * @param {Roo.bootstrap.ComboBox} combo This combo box
13029         * @param {String} query The query
13030         * @param {Boolean} forceAll true to force "all" query
13031         * @param {Boolean} cancel true to cancel the query
13032         * @param {Object} e The query event object
13033         */
13034         'beforequery': true,
13035          /**
13036          * @event add
13037          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13038         * @param {Roo.bootstrap.ComboBox} combo This combo box
13039         */
13040         'add' : true,
13041         /**
13042          * @event edit
13043          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13044         * @param {Roo.bootstrap.ComboBox} combo This combo box
13045         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13046         */
13047         'edit' : true,
13048         /**
13049          * @event remove
13050          * Fires when the remove value from the combobox array
13051         * @param {Roo.bootstrap.ComboBox} combo This combo box
13052         */
13053         'remove' : true,
13054         /**
13055          * @event afterremove
13056          * Fires when the remove value from the combobox array
13057         * @param {Roo.bootstrap.ComboBox} combo This combo box
13058         */
13059         'afterremove' : true,
13060         /**
13061          * @event specialfilter
13062          * Fires when specialfilter
13063             * @param {Roo.bootstrap.ComboBox} combo This combo box
13064             */
13065         'specialfilter' : true,
13066         /**
13067          * @event tick
13068          * Fires when tick the element
13069             * @param {Roo.bootstrap.ComboBox} combo This combo box
13070             */
13071         'tick' : true,
13072         /**
13073          * @event touchviewdisplay
13074          * Fires when touch view require special display (default is using displayField)
13075             * @param {Roo.bootstrap.ComboBox} combo This combo box
13076             * @param {Object} cfg set html .
13077             */
13078         'touchviewdisplay' : true
13079         
13080     });
13081     
13082     this.item = [];
13083     this.tickItems = [];
13084     
13085     this.selectedIndex = -1;
13086     if(this.mode == 'local'){
13087         if(config.queryDelay === undefined){
13088             this.queryDelay = 10;
13089         }
13090         if(config.minChars === undefined){
13091             this.minChars = 0;
13092         }
13093     }
13094 };
13095
13096 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13097      
13098     /**
13099      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13100      * rendering into an Roo.Editor, defaults to false)
13101      */
13102     /**
13103      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13104      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13105      */
13106     /**
13107      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13108      */
13109     /**
13110      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13111      * the dropdown list (defaults to undefined, with no header element)
13112      */
13113
13114      /**
13115      * @cfg {String/Roo.Template} tpl The template to use to render the output
13116      */
13117      
13118      /**
13119      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13120      */
13121     listWidth: undefined,
13122     /**
13123      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13124      * mode = 'remote' or 'text' if mode = 'local')
13125      */
13126     displayField: undefined,
13127     
13128     /**
13129      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13130      * mode = 'remote' or 'value' if mode = 'local'). 
13131      * Note: use of a valueField requires the user make a selection
13132      * in order for a value to be mapped.
13133      */
13134     valueField: undefined,
13135     /**
13136      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13137      */
13138     modalTitle : '',
13139     
13140     /**
13141      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13142      * field's data value (defaults to the underlying DOM element's name)
13143      */
13144     hiddenName: undefined,
13145     /**
13146      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13147      */
13148     listClass: '',
13149     /**
13150      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13151      */
13152     selectedClass: 'active',
13153     
13154     /**
13155      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13156      */
13157     shadow:'sides',
13158     /**
13159      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13160      * anchor positions (defaults to 'tl-bl')
13161      */
13162     listAlign: 'tl-bl?',
13163     /**
13164      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13165      */
13166     maxHeight: 300,
13167     /**
13168      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13169      * query specified by the allQuery config option (defaults to 'query')
13170      */
13171     triggerAction: 'query',
13172     /**
13173      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13174      * (defaults to 4, does not apply if editable = false)
13175      */
13176     minChars : 4,
13177     /**
13178      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13179      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13180      */
13181     typeAhead: false,
13182     /**
13183      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13184      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13185      */
13186     queryDelay: 500,
13187     /**
13188      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13189      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13190      */
13191     pageSize: 0,
13192     /**
13193      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13194      * when editable = true (defaults to false)
13195      */
13196     selectOnFocus:false,
13197     /**
13198      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13199      */
13200     queryParam: 'query',
13201     /**
13202      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13203      * when mode = 'remote' (defaults to 'Loading...')
13204      */
13205     loadingText: 'Loading...',
13206     /**
13207      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13208      */
13209     resizable: false,
13210     /**
13211      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13212      */
13213     handleHeight : 8,
13214     /**
13215      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13216      * traditional select (defaults to true)
13217      */
13218     editable: true,
13219     /**
13220      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13221      */
13222     allQuery: '',
13223     /**
13224      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13225      */
13226     mode: 'remote',
13227     /**
13228      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13229      * listWidth has a higher value)
13230      */
13231     minListWidth : 70,
13232     /**
13233      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13234      * allow the user to set arbitrary text into the field (defaults to false)
13235      */
13236     forceSelection:false,
13237     /**
13238      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13239      * if typeAhead = true (defaults to 250)
13240      */
13241     typeAheadDelay : 250,
13242     /**
13243      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13244      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13245      */
13246     valueNotFoundText : undefined,
13247     /**
13248      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13249      */
13250     blockFocus : false,
13251     
13252     /**
13253      * @cfg {Boolean} disableClear Disable showing of clear button.
13254      */
13255     disableClear : false,
13256     /**
13257      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13258      */
13259     alwaysQuery : false,
13260     
13261     /**
13262      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13263      */
13264     multiple : false,
13265     
13266     /**
13267      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13268      */
13269     invalidClass : "has-warning",
13270     
13271     /**
13272      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13273      */
13274     validClass : "has-success",
13275     
13276     /**
13277      * @cfg {Boolean} specialFilter (true|false) special filter default false
13278      */
13279     specialFilter : false,
13280     
13281     /**
13282      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13283      */
13284     mobileTouchView : true,
13285     
13286     /**
13287      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13288      */
13289     useNativeIOS : false,
13290     
13291     /**
13292      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13293      */
13294     mobile_restrict_height : false,
13295     
13296     ios_options : false,
13297     
13298     //private
13299     addicon : false,
13300     editicon: false,
13301     
13302     page: 0,
13303     hasQuery: false,
13304     append: false,
13305     loadNext: false,
13306     autoFocus : true,
13307     tickable : false,
13308     btnPosition : 'right',
13309     triggerList : true,
13310     showToggleBtn : true,
13311     animate : true,
13312     emptyResultText: 'Empty',
13313     triggerText : 'Select',
13314     emptyTitle : '',
13315     
13316     // element that contains real text value.. (when hidden is used..)
13317     
13318     getAutoCreate : function()
13319     {   
13320         var cfg = false;
13321         //render
13322         /*
13323          * Render classic select for iso
13324          */
13325         
13326         if(Roo.isIOS && this.useNativeIOS){
13327             cfg = this.getAutoCreateNativeIOS();
13328             return cfg;
13329         }
13330         
13331         /*
13332          * Touch Devices
13333          */
13334         
13335         if(Roo.isTouch && this.mobileTouchView){
13336             cfg = this.getAutoCreateTouchView();
13337             return cfg;;
13338         }
13339         
13340         /*
13341          *  Normal ComboBox
13342          */
13343         if(!this.tickable){
13344             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13345             return cfg;
13346         }
13347         
13348         /*
13349          *  ComboBox with tickable selections
13350          */
13351              
13352         var align = this.labelAlign || this.parentLabelAlign();
13353         
13354         cfg = {
13355             cls : 'form-group roo-combobox-tickable' //input-group
13356         };
13357         
13358         var btn_text_select = '';
13359         var btn_text_done = '';
13360         var btn_text_cancel = '';
13361         
13362         if (this.btn_text_show) {
13363             btn_text_select = 'Select';
13364             btn_text_done = 'Done';
13365             btn_text_cancel = 'Cancel'; 
13366         }
13367         
13368         var buttons = {
13369             tag : 'div',
13370             cls : 'tickable-buttons',
13371             cn : [
13372                 {
13373                     tag : 'button',
13374                     type : 'button',
13375                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13376                     //html : this.triggerText
13377                     html: btn_text_select
13378                 },
13379                 {
13380                     tag : 'button',
13381                     type : 'button',
13382                     name : 'ok',
13383                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13384                     //html : 'Done'
13385                     html: btn_text_done
13386                 },
13387                 {
13388                     tag : 'button',
13389                     type : 'button',
13390                     name : 'cancel',
13391                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13392                     //html : 'Cancel'
13393                     html: btn_text_cancel
13394                 }
13395             ]
13396         };
13397         
13398         if(this.editable){
13399             buttons.cn.unshift({
13400                 tag: 'input',
13401                 cls: 'roo-select2-search-field-input'
13402             });
13403         }
13404         
13405         var _this = this;
13406         
13407         Roo.each(buttons.cn, function(c){
13408             if (_this.size) {
13409                 c.cls += ' btn-' + _this.size;
13410             }
13411
13412             if (_this.disabled) {
13413                 c.disabled = true;
13414             }
13415         });
13416         
13417         var box = {
13418             tag: 'div',
13419             style : 'display: contents',
13420             cn: [
13421                 {
13422                     tag: 'input',
13423                     type : 'hidden',
13424                     cls: 'form-hidden-field'
13425                 },
13426                 {
13427                     tag: 'ul',
13428                     cls: 'roo-select2-choices',
13429                     cn:[
13430                         {
13431                             tag: 'li',
13432                             cls: 'roo-select2-search-field',
13433                             cn: [
13434                                 buttons
13435                             ]
13436                         }
13437                     ]
13438                 }
13439             ]
13440         };
13441         
13442         var combobox = {
13443             cls: 'roo-select2-container input-group roo-select2-container-multi',
13444             cn: [
13445                 
13446                 box
13447 //                {
13448 //                    tag: 'ul',
13449 //                    cls: 'typeahead typeahead-long dropdown-menu',
13450 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13451 //                }
13452             ]
13453         };
13454         
13455         if(this.hasFeedback && !this.allowBlank){
13456             
13457             var feedback = {
13458                 tag: 'span',
13459                 cls: 'glyphicon form-control-feedback'
13460             };
13461
13462             combobox.cn.push(feedback);
13463         }
13464         
13465         var indicator = {
13466             tag : 'i',
13467             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13468             tooltip : 'This field is required'
13469         };
13470         if (Roo.bootstrap.version == 4) {
13471             indicator = {
13472                 tag : 'i',
13473                 style : 'display:none'
13474             };
13475         }
13476         if (align ==='left' && this.fieldLabel.length) {
13477             
13478             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13479             
13480             cfg.cn = [
13481                 indicator,
13482                 {
13483                     tag: 'label',
13484                     'for' :  id,
13485                     cls : 'control-label col-form-label',
13486                     html : this.fieldLabel
13487
13488                 },
13489                 {
13490                     cls : "", 
13491                     cn: [
13492                         combobox
13493                     ]
13494                 }
13495
13496             ];
13497             
13498             var labelCfg = cfg.cn[1];
13499             var contentCfg = cfg.cn[2];
13500             
13501
13502             if(this.indicatorpos == 'right'){
13503                 
13504                 cfg.cn = [
13505                     {
13506                         tag: 'label',
13507                         'for' :  id,
13508                         cls : 'control-label col-form-label',
13509                         cn : [
13510                             {
13511                                 tag : 'span',
13512                                 html : this.fieldLabel
13513                             },
13514                             indicator
13515                         ]
13516                     },
13517                     {
13518                         cls : "",
13519                         cn: [
13520                             combobox
13521                         ]
13522                     }
13523
13524                 ];
13525                 
13526                 
13527                 
13528                 labelCfg = cfg.cn[0];
13529                 contentCfg = cfg.cn[1];
13530             
13531             }
13532             
13533             if(this.labelWidth > 12){
13534                 labelCfg.style = "width: " + this.labelWidth + 'px';
13535             }
13536             
13537             if(this.labelWidth < 13 && this.labelmd == 0){
13538                 this.labelmd = this.labelWidth;
13539             }
13540             
13541             if(this.labellg > 0){
13542                 labelCfg.cls += ' col-lg-' + this.labellg;
13543                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13544             }
13545             
13546             if(this.labelmd > 0){
13547                 labelCfg.cls += ' col-md-' + this.labelmd;
13548                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13549             }
13550             
13551             if(this.labelsm > 0){
13552                 labelCfg.cls += ' col-sm-' + this.labelsm;
13553                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13554             }
13555             
13556             if(this.labelxs > 0){
13557                 labelCfg.cls += ' col-xs-' + this.labelxs;
13558                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13559             }
13560                 
13561                 
13562         } else if ( this.fieldLabel.length) {
13563 //                Roo.log(" label");
13564                  cfg.cn = [
13565                    indicator,
13566                     {
13567                         tag: 'label',
13568                         //cls : 'input-group-addon',
13569                         html : this.fieldLabel
13570                     },
13571                     combobox
13572                 ];
13573                 
13574                 if(this.indicatorpos == 'right'){
13575                     cfg.cn = [
13576                         {
13577                             tag: 'label',
13578                             //cls : 'input-group-addon',
13579                             html : this.fieldLabel
13580                         },
13581                         indicator,
13582                         combobox
13583                     ];
13584                     
13585                 }
13586
13587         } else {
13588             
13589 //                Roo.log(" no label && no align");
13590                 cfg = combobox
13591                      
13592                 
13593         }
13594          
13595         var settings=this;
13596         ['xs','sm','md','lg'].map(function(size){
13597             if (settings[size]) {
13598                 cfg.cls += ' col-' + size + '-' + settings[size];
13599             }
13600         });
13601         
13602         return cfg;
13603         
13604     },
13605     
13606     _initEventsCalled : false,
13607     
13608     // private
13609     initEvents: function()
13610     {   
13611         if (this._initEventsCalled) { // as we call render... prevent looping...
13612             return;
13613         }
13614         this._initEventsCalled = true;
13615         
13616         if (!this.store) {
13617             throw "can not find store for combo";
13618         }
13619         
13620         this.indicator = this.indicatorEl();
13621         
13622         this.store = Roo.factory(this.store, Roo.data);
13623         this.store.parent = this;
13624         
13625         // if we are building from html. then this element is so complex, that we can not really
13626         // use the rendered HTML.
13627         // so we have to trash and replace the previous code.
13628         if (Roo.XComponent.build_from_html) {
13629             // remove this element....
13630             var e = this.el.dom, k=0;
13631             while (e ) { e = e.previousSibling;  ++k;}
13632
13633             this.el.remove();
13634             
13635             this.el=false;
13636             this.rendered = false;
13637             
13638             this.render(this.parent().getChildContainer(true), k);
13639         }
13640         
13641         if(Roo.isIOS && this.useNativeIOS){
13642             this.initIOSView();
13643             return;
13644         }
13645         
13646         /*
13647          * Touch Devices
13648          */
13649         
13650         if(Roo.isTouch && this.mobileTouchView){
13651             this.initTouchView();
13652             return;
13653         }
13654         
13655         if(this.tickable){
13656             this.initTickableEvents();
13657             return;
13658         }
13659         
13660         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13661         
13662         if(this.hiddenName){
13663             
13664             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13665             
13666             this.hiddenField.dom.value =
13667                 this.hiddenValue !== undefined ? this.hiddenValue :
13668                 this.value !== undefined ? this.value : '';
13669
13670             // prevent input submission
13671             this.el.dom.removeAttribute('name');
13672             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13673              
13674              
13675         }
13676         //if(Roo.isGecko){
13677         //    this.el.dom.setAttribute('autocomplete', 'off');
13678         //}
13679         
13680         var cls = 'x-combo-list';
13681         
13682         //this.list = new Roo.Layer({
13683         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13684         //});
13685         
13686         var _this = this;
13687         
13688         (function(){
13689             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13690             _this.list.setWidth(lw);
13691         }).defer(100);
13692         
13693         this.list.on('mouseover', this.onViewOver, this);
13694         this.list.on('mousemove', this.onViewMove, this);
13695         this.list.on('scroll', this.onViewScroll, this);
13696         
13697         /*
13698         this.list.swallowEvent('mousewheel');
13699         this.assetHeight = 0;
13700
13701         if(this.title){
13702             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13703             this.assetHeight += this.header.getHeight();
13704         }
13705
13706         this.innerList = this.list.createChild({cls:cls+'-inner'});
13707         this.innerList.on('mouseover', this.onViewOver, this);
13708         this.innerList.on('mousemove', this.onViewMove, this);
13709         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13710         
13711         if(this.allowBlank && !this.pageSize && !this.disableClear){
13712             this.footer = this.list.createChild({cls:cls+'-ft'});
13713             this.pageTb = new Roo.Toolbar(this.footer);
13714            
13715         }
13716         if(this.pageSize){
13717             this.footer = this.list.createChild({cls:cls+'-ft'});
13718             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13719                     {pageSize: this.pageSize});
13720             
13721         }
13722         
13723         if (this.pageTb && this.allowBlank && !this.disableClear) {
13724             var _this = this;
13725             this.pageTb.add(new Roo.Toolbar.Fill(), {
13726                 cls: 'x-btn-icon x-btn-clear',
13727                 text: '&#160;',
13728                 handler: function()
13729                 {
13730                     _this.collapse();
13731                     _this.clearValue();
13732                     _this.onSelect(false, -1);
13733                 }
13734             });
13735         }
13736         if (this.footer) {
13737             this.assetHeight += this.footer.getHeight();
13738         }
13739         */
13740             
13741         if(!this.tpl){
13742             this.tpl = Roo.bootstrap.version == 4 ?
13743                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13744                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13745         }
13746
13747         this.view = new Roo.View(this.list, this.tpl, {
13748             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13749         });
13750         //this.view.wrapEl.setDisplayed(false);
13751         this.view.on('click', this.onViewClick, this);
13752         
13753         
13754         this.store.on('beforeload', this.onBeforeLoad, this);
13755         this.store.on('load', this.onLoad, this);
13756         this.store.on('loadexception', this.onLoadException, this);
13757         /*
13758         if(this.resizable){
13759             this.resizer = new Roo.Resizable(this.list,  {
13760                pinned:true, handles:'se'
13761             });
13762             this.resizer.on('resize', function(r, w, h){
13763                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13764                 this.listWidth = w;
13765                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13766                 this.restrictHeight();
13767             }, this);
13768             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13769         }
13770         */
13771         if(!this.editable){
13772             this.editable = true;
13773             this.setEditable(false);
13774         }
13775         
13776         /*
13777         
13778         if (typeof(this.events.add.listeners) != 'undefined') {
13779             
13780             this.addicon = this.wrap.createChild(
13781                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13782        
13783             this.addicon.on('click', function(e) {
13784                 this.fireEvent('add', this);
13785             }, this);
13786         }
13787         if (typeof(this.events.edit.listeners) != 'undefined') {
13788             
13789             this.editicon = this.wrap.createChild(
13790                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13791             if (this.addicon) {
13792                 this.editicon.setStyle('margin-left', '40px');
13793             }
13794             this.editicon.on('click', function(e) {
13795                 
13796                 // we fire even  if inothing is selected..
13797                 this.fireEvent('edit', this, this.lastData );
13798                 
13799             }, this);
13800         }
13801         */
13802         
13803         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13804             "up" : function(e){
13805                 this.inKeyMode = true;
13806                 this.selectPrev();
13807             },
13808
13809             "down" : function(e){
13810                 if(!this.isExpanded()){
13811                     this.onTriggerClick();
13812                 }else{
13813                     this.inKeyMode = true;
13814                     this.selectNext();
13815                 }
13816             },
13817
13818             "enter" : function(e){
13819 //                this.onViewClick();
13820                 //return true;
13821                 this.collapse();
13822                 
13823                 if(this.fireEvent("specialkey", this, e)){
13824                     this.onViewClick(false);
13825                 }
13826                 
13827                 return true;
13828             },
13829
13830             "esc" : function(e){
13831                 this.collapse();
13832             },
13833
13834             "tab" : function(e){
13835                 this.collapse();
13836                 
13837                 if(this.fireEvent("specialkey", this, e)){
13838                     this.onViewClick(false);
13839                 }
13840                 
13841                 return true;
13842             },
13843
13844             scope : this,
13845
13846             doRelay : function(foo, bar, hname){
13847                 if(hname == 'down' || this.scope.isExpanded()){
13848                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13849                 }
13850                 return true;
13851             },
13852
13853             forceKeyDown: true
13854         });
13855         
13856         
13857         this.queryDelay = Math.max(this.queryDelay || 10,
13858                 this.mode == 'local' ? 10 : 250);
13859         
13860         
13861         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13862         
13863         if(this.typeAhead){
13864             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13865         }
13866         if(this.editable !== false){
13867             this.inputEl().on("keyup", this.onKeyUp, this);
13868         }
13869         if(this.forceSelection){
13870             this.inputEl().on('blur', this.doForce, this);
13871         }
13872         
13873         if(this.multiple){
13874             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13875             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13876         }
13877     },
13878     
13879     initTickableEvents: function()
13880     {   
13881         this.createList();
13882         
13883         if(this.hiddenName){
13884             
13885             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13886             
13887             this.hiddenField.dom.value =
13888                 this.hiddenValue !== undefined ? this.hiddenValue :
13889                 this.value !== undefined ? this.value : '';
13890
13891             // prevent input submission
13892             this.el.dom.removeAttribute('name');
13893             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13894              
13895              
13896         }
13897         
13898 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13899         
13900         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13901         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13902         if(this.triggerList){
13903             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13904         }
13905          
13906         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13907         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13908         
13909         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13910         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13911         
13912         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13913         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13914         
13915         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13916         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13917         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13918         
13919         this.okBtn.hide();
13920         this.cancelBtn.hide();
13921         
13922         var _this = this;
13923         
13924         (function(){
13925             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13926             _this.list.setWidth(lw);
13927         }).defer(100);
13928         
13929         this.list.on('mouseover', this.onViewOver, this);
13930         this.list.on('mousemove', this.onViewMove, this);
13931         
13932         this.list.on('scroll', this.onViewScroll, this);
13933         
13934         if(!this.tpl){
13935             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13936                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13937         }
13938
13939         this.view = new Roo.View(this.list, this.tpl, {
13940             singleSelect:true,
13941             tickable:true,
13942             parent:this,
13943             store: this.store,
13944             selectedClass: this.selectedClass
13945         });
13946         
13947         //this.view.wrapEl.setDisplayed(false);
13948         this.view.on('click', this.onViewClick, this);
13949         
13950         
13951         
13952         this.store.on('beforeload', this.onBeforeLoad, this);
13953         this.store.on('load', this.onLoad, this);
13954         this.store.on('loadexception', this.onLoadException, this);
13955         
13956         if(this.editable){
13957             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13958                 "up" : function(e){
13959                     this.inKeyMode = true;
13960                     this.selectPrev();
13961                 },
13962
13963                 "down" : function(e){
13964                     this.inKeyMode = true;
13965                     this.selectNext();
13966                 },
13967
13968                 "enter" : function(e){
13969                     if(this.fireEvent("specialkey", this, e)){
13970                         this.onViewClick(false);
13971                     }
13972                     
13973                     return true;
13974                 },
13975
13976                 "esc" : function(e){
13977                     this.onTickableFooterButtonClick(e, false, false);
13978                 },
13979
13980                 "tab" : function(e){
13981                     this.fireEvent("specialkey", this, e);
13982                     
13983                     this.onTickableFooterButtonClick(e, false, false);
13984                     
13985                     return true;
13986                 },
13987
13988                 scope : this,
13989
13990                 doRelay : function(e, fn, key){
13991                     if(this.scope.isExpanded()){
13992                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13993                     }
13994                     return true;
13995                 },
13996
13997                 forceKeyDown: true
13998             });
13999         }
14000         
14001         this.queryDelay = Math.max(this.queryDelay || 10,
14002                 this.mode == 'local' ? 10 : 250);
14003         
14004         
14005         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14006         
14007         if(this.typeAhead){
14008             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14009         }
14010         
14011         if(this.editable !== false){
14012             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14013         }
14014         
14015         this.indicator = this.indicatorEl();
14016         
14017         if(this.indicator){
14018             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14019             this.indicator.hide();
14020         }
14021         
14022     },
14023
14024     onDestroy : function(){
14025         if(this.view){
14026             this.view.setStore(null);
14027             this.view.el.removeAllListeners();
14028             this.view.el.remove();
14029             this.view.purgeListeners();
14030         }
14031         if(this.list){
14032             this.list.dom.innerHTML  = '';
14033         }
14034         
14035         if(this.store){
14036             this.store.un('beforeload', this.onBeforeLoad, this);
14037             this.store.un('load', this.onLoad, this);
14038             this.store.un('loadexception', this.onLoadException, this);
14039         }
14040         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14041     },
14042
14043     // private
14044     fireKey : function(e){
14045         if(e.isNavKeyPress() && !this.list.isVisible()){
14046             this.fireEvent("specialkey", this, e);
14047         }
14048     },
14049
14050     // private
14051     onResize: function(w, h){
14052 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14053 //        
14054 //        if(typeof w != 'number'){
14055 //            // we do not handle it!?!?
14056 //            return;
14057 //        }
14058 //        var tw = this.trigger.getWidth();
14059 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14060 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14061 //        var x = w - tw;
14062 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14063 //            
14064 //        //this.trigger.setStyle('left', x+'px');
14065 //        
14066 //        if(this.list && this.listWidth === undefined){
14067 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14068 //            this.list.setWidth(lw);
14069 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14070 //        }
14071         
14072     
14073         
14074     },
14075
14076     /**
14077      * Allow or prevent the user from directly editing the field text.  If false is passed,
14078      * the user will only be able to select from the items defined in the dropdown list.  This method
14079      * is the runtime equivalent of setting the 'editable' config option at config time.
14080      * @param {Boolean} value True to allow the user to directly edit the field text
14081      */
14082     setEditable : function(value){
14083         if(value == this.editable){
14084             return;
14085         }
14086         this.editable = value;
14087         if(!value){
14088             this.inputEl().dom.setAttribute('readOnly', true);
14089             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14090             this.inputEl().addClass('x-combo-noedit');
14091         }else{
14092             this.inputEl().dom.setAttribute('readOnly', false);
14093             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14094             this.inputEl().removeClass('x-combo-noedit');
14095         }
14096     },
14097
14098     // private
14099     
14100     onBeforeLoad : function(combo,opts){
14101         if(!this.hasFocus){
14102             return;
14103         }
14104          if (!opts.add) {
14105             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14106          }
14107         this.restrictHeight();
14108         this.selectedIndex = -1;
14109     },
14110
14111     // private
14112     onLoad : function(){
14113         
14114         this.hasQuery = false;
14115         
14116         if(!this.hasFocus){
14117             return;
14118         }
14119         
14120         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14121             this.loading.hide();
14122         }
14123         
14124         if(this.store.getCount() > 0){
14125             
14126             this.expand();
14127             this.restrictHeight();
14128             if(this.lastQuery == this.allQuery){
14129                 if(this.editable && !this.tickable){
14130                     this.inputEl().dom.select();
14131                 }
14132                 
14133                 if(
14134                     !this.selectByValue(this.value, true) &&
14135                     this.autoFocus && 
14136                     (
14137                         !this.store.lastOptions ||
14138                         typeof(this.store.lastOptions.add) == 'undefined' || 
14139                         this.store.lastOptions.add != true
14140                     )
14141                 ){
14142                     this.select(0, true);
14143                 }
14144             }else{
14145                 if(this.autoFocus){
14146                     this.selectNext();
14147                 }
14148                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14149                     this.taTask.delay(this.typeAheadDelay);
14150                 }
14151             }
14152         }else{
14153             this.onEmptyResults();
14154         }
14155         
14156         //this.el.focus();
14157     },
14158     // private
14159     onLoadException : function()
14160     {
14161         this.hasQuery = false;
14162         
14163         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14164             this.loading.hide();
14165         }
14166         
14167         if(this.tickable && this.editable){
14168             return;
14169         }
14170         
14171         this.collapse();
14172         // only causes errors at present
14173         //Roo.log(this.store.reader.jsonData);
14174         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14175             // fixme
14176             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14177         //}
14178         
14179         
14180     },
14181     // private
14182     onTypeAhead : function(){
14183         if(this.store.getCount() > 0){
14184             var r = this.store.getAt(0);
14185             var newValue = r.data[this.displayField];
14186             var len = newValue.length;
14187             var selStart = this.getRawValue().length;
14188             
14189             if(selStart != len){
14190                 this.setRawValue(newValue);
14191                 this.selectText(selStart, newValue.length);
14192             }
14193         }
14194     },
14195
14196     // private
14197     onSelect : function(record, index){
14198         
14199         if(this.fireEvent('beforeselect', this, record, index) !== false){
14200         
14201             this.setFromData(index > -1 ? record.data : false);
14202             
14203             this.collapse();
14204             this.fireEvent('select', this, record, index);
14205         }
14206     },
14207
14208     /**
14209      * Returns the currently selected field value or empty string if no value is set.
14210      * @return {String} value The selected value
14211      */
14212     getValue : function()
14213     {
14214         if(Roo.isIOS && this.useNativeIOS){
14215             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14216         }
14217         
14218         if(this.multiple){
14219             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14220         }
14221         
14222         if(this.valueField){
14223             return typeof this.value != 'undefined' ? this.value : '';
14224         }else{
14225             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14226         }
14227     },
14228     
14229     getRawValue : function()
14230     {
14231         if(Roo.isIOS && this.useNativeIOS){
14232             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14233         }
14234         
14235         var v = this.inputEl().getValue();
14236         
14237         return v;
14238     },
14239
14240     /**
14241      * Clears any text/value currently set in the field
14242      */
14243     clearValue : function(){
14244         
14245         if(this.hiddenField){
14246             this.hiddenField.dom.value = '';
14247         }
14248         this.value = '';
14249         this.setRawValue('');
14250         this.lastSelectionText = '';
14251         this.lastData = false;
14252         
14253         var close = this.closeTriggerEl();
14254         
14255         if(close){
14256             close.hide();
14257         }
14258         
14259         this.validate();
14260         
14261     },
14262
14263     /**
14264      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14265      * will be displayed in the field.  If the value does not match the data value of an existing item,
14266      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14267      * Otherwise the field will be blank (although the value will still be set).
14268      * @param {String} value The value to match
14269      */
14270     setValue : function(v)
14271     {
14272         if(Roo.isIOS && this.useNativeIOS){
14273             this.setIOSValue(v);
14274             return;
14275         }
14276         
14277         if(this.multiple){
14278             this.syncValue();
14279             return;
14280         }
14281         
14282         var text = v;
14283         if(this.valueField){
14284             var r = this.findRecord(this.valueField, v);
14285             if(r){
14286                 text = r.data[this.displayField];
14287             }else if(this.valueNotFoundText !== undefined){
14288                 text = this.valueNotFoundText;
14289             }
14290         }
14291         this.lastSelectionText = text;
14292         if(this.hiddenField){
14293             this.hiddenField.dom.value = v;
14294         }
14295         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14296         this.value = v;
14297         
14298         var close = this.closeTriggerEl();
14299         
14300         if(close){
14301             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14302         }
14303         
14304         this.validate();
14305     },
14306     /**
14307      * @property {Object} the last set data for the element
14308      */
14309     
14310     lastData : false,
14311     /**
14312      * Sets the value of the field based on a object which is related to the record format for the store.
14313      * @param {Object} value the value to set as. or false on reset?
14314      */
14315     setFromData : function(o){
14316         
14317         if(this.multiple){
14318             this.addItem(o);
14319             return;
14320         }
14321             
14322         var dv = ''; // display value
14323         var vv = ''; // value value..
14324         this.lastData = o;
14325         if (this.displayField) {
14326             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14327         } else {
14328             // this is an error condition!!!
14329             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14330         }
14331         
14332         if(this.valueField){
14333             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14334         }
14335         
14336         var close = this.closeTriggerEl();
14337         
14338         if(close){
14339             if(dv.length || vv * 1 > 0){
14340                 close.show() ;
14341                 this.blockFocus=true;
14342             } else {
14343                 close.hide();
14344             }             
14345         }
14346         
14347         if(this.hiddenField){
14348             this.hiddenField.dom.value = vv;
14349             
14350             this.lastSelectionText = dv;
14351             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14352             this.value = vv;
14353             return;
14354         }
14355         // no hidden field.. - we store the value in 'value', but still display
14356         // display field!!!!
14357         this.lastSelectionText = dv;
14358         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14359         this.value = vv;
14360         
14361         
14362         
14363     },
14364     // private
14365     reset : function(){
14366         // overridden so that last data is reset..
14367         
14368         if(this.multiple){
14369             this.clearItem();
14370             return;
14371         }
14372         
14373         this.setValue(this.originalValue);
14374         //this.clearInvalid();
14375         this.lastData = false;
14376         if (this.view) {
14377             this.view.clearSelections();
14378         }
14379         
14380         this.validate();
14381     },
14382     // private
14383     findRecord : function(prop, value){
14384         var record;
14385         if(this.store.getCount() > 0){
14386             this.store.each(function(r){
14387                 if(r.data[prop] == value){
14388                     record = r;
14389                     return false;
14390                 }
14391                 return true;
14392             });
14393         }
14394         return record;
14395     },
14396     
14397     getName: function()
14398     {
14399         // returns hidden if it's set..
14400         if (!this.rendered) {return ''};
14401         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14402         
14403     },
14404     // private
14405     onViewMove : function(e, t){
14406         this.inKeyMode = false;
14407     },
14408
14409     // private
14410     onViewOver : function(e, t){
14411         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14412             return;
14413         }
14414         var item = this.view.findItemFromChild(t);
14415         
14416         if(item){
14417             var index = this.view.indexOf(item);
14418             this.select(index, false);
14419         }
14420     },
14421
14422     // private
14423     onViewClick : function(view, doFocus, el, e)
14424     {
14425         var index = this.view.getSelectedIndexes()[0];
14426         
14427         var r = this.store.getAt(index);
14428         
14429         if(this.tickable){
14430             
14431             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14432                 return;
14433             }
14434             
14435             var rm = false;
14436             var _this = this;
14437             
14438             Roo.each(this.tickItems, function(v,k){
14439                 
14440                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14441                     Roo.log(v);
14442                     _this.tickItems.splice(k, 1);
14443                     
14444                     if(typeof(e) == 'undefined' && view == false){
14445                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14446                     }
14447                     
14448                     rm = true;
14449                     return;
14450                 }
14451             });
14452             
14453             if(rm){
14454                 return;
14455             }
14456             
14457             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14458                 this.tickItems.push(r.data);
14459             }
14460             
14461             if(typeof(e) == 'undefined' && view == false){
14462                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14463             }
14464                     
14465             return;
14466         }
14467         
14468         if(r){
14469             this.onSelect(r, index);
14470         }
14471         if(doFocus !== false && !this.blockFocus){
14472             this.inputEl().focus();
14473         }
14474     },
14475
14476     // private
14477     restrictHeight : function(){
14478         //this.innerList.dom.style.height = '';
14479         //var inner = this.innerList.dom;
14480         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14481         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14482         //this.list.beginUpdate();
14483         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14484         this.list.alignTo(this.inputEl(), this.listAlign);
14485         this.list.alignTo(this.inputEl(), this.listAlign);
14486         //this.list.endUpdate();
14487     },
14488
14489     // private
14490     onEmptyResults : function(){
14491         
14492         if(this.tickable && this.editable){
14493             this.hasFocus = false;
14494             this.restrictHeight();
14495             return;
14496         }
14497         
14498         this.collapse();
14499     },
14500
14501     /**
14502      * Returns true if the dropdown list is expanded, else false.
14503      */
14504     isExpanded : function(){
14505         return this.list.isVisible();
14506     },
14507
14508     /**
14509      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14510      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14511      * @param {String} value The data value of the item to select
14512      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14513      * selected item if it is not currently in view (defaults to true)
14514      * @return {Boolean} True if the value matched an item in the list, else false
14515      */
14516     selectByValue : function(v, scrollIntoView){
14517         if(v !== undefined && v !== null){
14518             var r = this.findRecord(this.valueField || this.displayField, v);
14519             if(r){
14520                 this.select(this.store.indexOf(r), scrollIntoView);
14521                 return true;
14522             }
14523         }
14524         return false;
14525     },
14526
14527     /**
14528      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14529      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14530      * @param {Number} index The zero-based index of the list item to select
14531      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14532      * selected item if it is not currently in view (defaults to true)
14533      */
14534     select : function(index, scrollIntoView){
14535         this.selectedIndex = index;
14536         this.view.select(index);
14537         if(scrollIntoView !== false){
14538             var el = this.view.getNode(index);
14539             /*
14540              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14541              */
14542             if(el){
14543                 this.list.scrollChildIntoView(el, false);
14544             }
14545         }
14546     },
14547
14548     // private
14549     selectNext : function(){
14550         var ct = this.store.getCount();
14551         if(ct > 0){
14552             if(this.selectedIndex == -1){
14553                 this.select(0);
14554             }else if(this.selectedIndex < ct-1){
14555                 this.select(this.selectedIndex+1);
14556             }
14557         }
14558     },
14559
14560     // private
14561     selectPrev : function(){
14562         var ct = this.store.getCount();
14563         if(ct > 0){
14564             if(this.selectedIndex == -1){
14565                 this.select(0);
14566             }else if(this.selectedIndex != 0){
14567                 this.select(this.selectedIndex-1);
14568             }
14569         }
14570     },
14571
14572     // private
14573     onKeyUp : function(e){
14574         if(this.editable !== false && !e.isSpecialKey()){
14575             this.lastKey = e.getKey();
14576             this.dqTask.delay(this.queryDelay);
14577         }
14578     },
14579
14580     // private
14581     validateBlur : function(){
14582         return !this.list || !this.list.isVisible();   
14583     },
14584
14585     // private
14586     initQuery : function(){
14587         
14588         var v = this.getRawValue();
14589         
14590         if(this.tickable && this.editable){
14591             v = this.tickableInputEl().getValue();
14592         }
14593         
14594         this.doQuery(v);
14595     },
14596
14597     // private
14598     doForce : function(){
14599         if(this.inputEl().dom.value.length > 0){
14600             this.inputEl().dom.value =
14601                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14602              
14603         }
14604     },
14605
14606     /**
14607      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14608      * query allowing the query action to be canceled if needed.
14609      * @param {String} query The SQL query to execute
14610      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14611      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14612      * saved in the current store (defaults to false)
14613      */
14614     doQuery : function(q, forceAll){
14615         
14616         if(q === undefined || q === null){
14617             q = '';
14618         }
14619         var qe = {
14620             query: q,
14621             forceAll: forceAll,
14622             combo: this,
14623             cancel:false
14624         };
14625         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14626             return false;
14627         }
14628         q = qe.query;
14629         
14630         forceAll = qe.forceAll;
14631         if(forceAll === true || (q.length >= this.minChars)){
14632             
14633             this.hasQuery = true;
14634             
14635             if(this.lastQuery != q || this.alwaysQuery){
14636                 this.lastQuery = q;
14637                 if(this.mode == 'local'){
14638                     this.selectedIndex = -1;
14639                     if(forceAll){
14640                         this.store.clearFilter();
14641                     }else{
14642                         
14643                         if(this.specialFilter){
14644                             this.fireEvent('specialfilter', this);
14645                             this.onLoad();
14646                             return;
14647                         }
14648                         
14649                         this.store.filter(this.displayField, q);
14650                     }
14651                     
14652                     this.store.fireEvent("datachanged", this.store);
14653                     
14654                     this.onLoad();
14655                     
14656                     
14657                 }else{
14658                     
14659                     this.store.baseParams[this.queryParam] = q;
14660                     
14661                     var options = {params : this.getParams(q)};
14662                     
14663                     if(this.loadNext){
14664                         options.add = true;
14665                         options.params.start = this.page * this.pageSize;
14666                     }
14667                     
14668                     this.store.load(options);
14669                     
14670                     /*
14671                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14672                      *  we should expand the list on onLoad
14673                      *  so command out it
14674                      */
14675 //                    this.expand();
14676                 }
14677             }else{
14678                 this.selectedIndex = -1;
14679                 this.onLoad();   
14680             }
14681         }
14682         
14683         this.loadNext = false;
14684     },
14685     
14686     // private
14687     getParams : function(q){
14688         var p = {};
14689         //p[this.queryParam] = q;
14690         
14691         if(this.pageSize){
14692             p.start = 0;
14693             p.limit = this.pageSize;
14694         }
14695         return p;
14696     },
14697
14698     /**
14699      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14700      */
14701     collapse : function(){
14702         if(!this.isExpanded()){
14703             return;
14704         }
14705         
14706         this.list.hide();
14707         
14708         this.hasFocus = false;
14709         
14710         if(this.tickable){
14711             this.okBtn.hide();
14712             this.cancelBtn.hide();
14713             this.trigger.show();
14714             
14715             if(this.editable){
14716                 this.tickableInputEl().dom.value = '';
14717                 this.tickableInputEl().blur();
14718             }
14719             
14720         }
14721         
14722         Roo.get(document).un('mousedown', this.collapseIf, this);
14723         Roo.get(document).un('mousewheel', this.collapseIf, this);
14724         if (!this.editable) {
14725             Roo.get(document).un('keydown', this.listKeyPress, this);
14726         }
14727         this.fireEvent('collapse', this);
14728         
14729         this.validate();
14730     },
14731
14732     // private
14733     collapseIf : function(e){
14734         var in_combo  = e.within(this.el);
14735         var in_list =  e.within(this.list);
14736         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14737         
14738         if (in_combo || in_list || is_list) {
14739             //e.stopPropagation();
14740             return;
14741         }
14742         
14743         if(this.tickable){
14744             this.onTickableFooterButtonClick(e, false, false);
14745         }
14746
14747         this.collapse();
14748         
14749     },
14750
14751     /**
14752      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14753      */
14754     expand : function(){
14755        
14756         if(this.isExpanded() || !this.hasFocus){
14757             return;
14758         }
14759         
14760         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14761         this.list.setWidth(lw);
14762         
14763         Roo.log('expand');
14764         
14765         this.list.show();
14766         
14767         this.restrictHeight();
14768         
14769         if(this.tickable){
14770             
14771             this.tickItems = Roo.apply([], this.item);
14772             
14773             this.okBtn.show();
14774             this.cancelBtn.show();
14775             this.trigger.hide();
14776             
14777             if(this.editable){
14778                 this.tickableInputEl().focus();
14779             }
14780             
14781         }
14782         
14783         Roo.get(document).on('mousedown', this.collapseIf, this);
14784         Roo.get(document).on('mousewheel', this.collapseIf, this);
14785         if (!this.editable) {
14786             Roo.get(document).on('keydown', this.listKeyPress, this);
14787         }
14788         
14789         this.fireEvent('expand', this);
14790     },
14791
14792     // private
14793     // Implements the default empty TriggerField.onTriggerClick function
14794     onTriggerClick : function(e)
14795     {
14796         Roo.log('trigger click');
14797         
14798         if(this.disabled || !this.triggerList){
14799             return;
14800         }
14801         
14802         this.page = 0;
14803         this.loadNext = false;
14804         
14805         if(this.isExpanded()){
14806             this.collapse();
14807             if (!this.blockFocus) {
14808                 this.inputEl().focus();
14809             }
14810             
14811         }else {
14812             this.hasFocus = true;
14813             if(this.triggerAction == 'all') {
14814                 this.doQuery(this.allQuery, true);
14815             } else {
14816                 this.doQuery(this.getRawValue());
14817             }
14818             if (!this.blockFocus) {
14819                 this.inputEl().focus();
14820             }
14821         }
14822     },
14823     
14824     onTickableTriggerClick : function(e)
14825     {
14826         if(this.disabled){
14827             return;
14828         }
14829         
14830         this.page = 0;
14831         this.loadNext = false;
14832         this.hasFocus = true;
14833         
14834         if(this.triggerAction == 'all') {
14835             this.doQuery(this.allQuery, true);
14836         } else {
14837             this.doQuery(this.getRawValue());
14838         }
14839     },
14840     
14841     onSearchFieldClick : function(e)
14842     {
14843         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14844             this.onTickableFooterButtonClick(e, false, false);
14845             return;
14846         }
14847         
14848         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14849             return;
14850         }
14851         
14852         this.page = 0;
14853         this.loadNext = false;
14854         this.hasFocus = true;
14855         
14856         if(this.triggerAction == 'all') {
14857             this.doQuery(this.allQuery, true);
14858         } else {
14859             this.doQuery(this.getRawValue());
14860         }
14861     },
14862     
14863     listKeyPress : function(e)
14864     {
14865         //Roo.log('listkeypress');
14866         // scroll to first matching element based on key pres..
14867         if (e.isSpecialKey()) {
14868             return false;
14869         }
14870         var k = String.fromCharCode(e.getKey()).toUpperCase();
14871         //Roo.log(k);
14872         var match  = false;
14873         var csel = this.view.getSelectedNodes();
14874         var cselitem = false;
14875         if (csel.length) {
14876             var ix = this.view.indexOf(csel[0]);
14877             cselitem  = this.store.getAt(ix);
14878             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14879                 cselitem = false;
14880             }
14881             
14882         }
14883         
14884         this.store.each(function(v) { 
14885             if (cselitem) {
14886                 // start at existing selection.
14887                 if (cselitem.id == v.id) {
14888                     cselitem = false;
14889                 }
14890                 return true;
14891             }
14892                 
14893             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14894                 match = this.store.indexOf(v);
14895                 return false;
14896             }
14897             return true;
14898         }, this);
14899         
14900         if (match === false) {
14901             return true; // no more action?
14902         }
14903         // scroll to?
14904         this.view.select(match);
14905         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14906         sn.scrollIntoView(sn.dom.parentNode, false);
14907     },
14908     
14909     onViewScroll : function(e, t){
14910         
14911         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){
14912             return;
14913         }
14914         
14915         this.hasQuery = true;
14916         
14917         this.loading = this.list.select('.loading', true).first();
14918         
14919         if(this.loading === null){
14920             this.list.createChild({
14921                 tag: 'div',
14922                 cls: 'loading roo-select2-more-results roo-select2-active',
14923                 html: 'Loading more results...'
14924             });
14925             
14926             this.loading = this.list.select('.loading', true).first();
14927             
14928             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14929             
14930             this.loading.hide();
14931         }
14932         
14933         this.loading.show();
14934         
14935         var _combo = this;
14936         
14937         this.page++;
14938         this.loadNext = true;
14939         
14940         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14941         
14942         return;
14943     },
14944     
14945     addItem : function(o)
14946     {   
14947         var dv = ''; // display value
14948         
14949         if (this.displayField) {
14950             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14951         } else {
14952             // this is an error condition!!!
14953             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14954         }
14955         
14956         if(!dv.length){
14957             return;
14958         }
14959         
14960         var choice = this.choices.createChild({
14961             tag: 'li',
14962             cls: 'roo-select2-search-choice',
14963             cn: [
14964                 {
14965                     tag: 'div',
14966                     html: dv
14967                 },
14968                 {
14969                     tag: 'a',
14970                     href: '#',
14971                     cls: 'roo-select2-search-choice-close fa fa-times',
14972                     tabindex: '-1'
14973                 }
14974             ]
14975             
14976         }, this.searchField);
14977         
14978         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14979         
14980         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14981         
14982         this.item.push(o);
14983         
14984         this.lastData = o;
14985         
14986         this.syncValue();
14987         
14988         this.inputEl().dom.value = '';
14989         
14990         this.validate();
14991     },
14992     
14993     onRemoveItem : function(e, _self, o)
14994     {
14995         e.preventDefault();
14996         
14997         this.lastItem = Roo.apply([], this.item);
14998         
14999         var index = this.item.indexOf(o.data) * 1;
15000         
15001         if( index < 0){
15002             Roo.log('not this item?!');
15003             return;
15004         }
15005         
15006         this.item.splice(index, 1);
15007         o.item.remove();
15008         
15009         this.syncValue();
15010         
15011         this.fireEvent('remove', this, e);
15012         
15013         this.validate();
15014         
15015     },
15016     
15017     syncValue : function()
15018     {
15019         if(!this.item.length){
15020             this.clearValue();
15021             return;
15022         }
15023             
15024         var value = [];
15025         var _this = this;
15026         Roo.each(this.item, function(i){
15027             if(_this.valueField){
15028                 value.push(i[_this.valueField]);
15029                 return;
15030             }
15031
15032             value.push(i);
15033         });
15034
15035         this.value = value.join(',');
15036
15037         if(this.hiddenField){
15038             this.hiddenField.dom.value = this.value;
15039         }
15040         
15041         this.store.fireEvent("datachanged", this.store);
15042         
15043         this.validate();
15044     },
15045     
15046     clearItem : function()
15047     {
15048         if(!this.multiple){
15049             return;
15050         }
15051         
15052         this.item = [];
15053         
15054         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15055            c.remove();
15056         });
15057         
15058         this.syncValue();
15059         
15060         this.validate();
15061         
15062         if(this.tickable && !Roo.isTouch){
15063             this.view.refresh();
15064         }
15065     },
15066     
15067     inputEl: function ()
15068     {
15069         if(Roo.isIOS && this.useNativeIOS){
15070             return this.el.select('select.roo-ios-select', true).first();
15071         }
15072         
15073         if(Roo.isTouch && this.mobileTouchView){
15074             return this.el.select('input.form-control',true).first();
15075         }
15076         
15077         if(this.tickable){
15078             return this.searchField;
15079         }
15080         
15081         return this.el.select('input.form-control',true).first();
15082     },
15083     
15084     onTickableFooterButtonClick : function(e, btn, el)
15085     {
15086         e.preventDefault();
15087         
15088         this.lastItem = Roo.apply([], this.item);
15089         
15090         if(btn && btn.name == 'cancel'){
15091             this.tickItems = Roo.apply([], this.item);
15092             this.collapse();
15093             return;
15094         }
15095         
15096         this.clearItem();
15097         
15098         var _this = this;
15099         
15100         Roo.each(this.tickItems, function(o){
15101             _this.addItem(o);
15102         });
15103         
15104         this.collapse();
15105         
15106     },
15107     
15108     validate : function()
15109     {
15110         if(this.getVisibilityEl().hasClass('hidden')){
15111             return true;
15112         }
15113         
15114         var v = this.getRawValue();
15115         
15116         if(this.multiple){
15117             v = this.getValue();
15118         }
15119         
15120         if(this.disabled || this.allowBlank || v.length){
15121             this.markValid();
15122             return true;
15123         }
15124         
15125         this.markInvalid();
15126         return false;
15127     },
15128     
15129     tickableInputEl : function()
15130     {
15131         if(!this.tickable || !this.editable){
15132             return this.inputEl();
15133         }
15134         
15135         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15136     },
15137     
15138     
15139     getAutoCreateTouchView : function()
15140     {
15141         var id = Roo.id();
15142         
15143         var cfg = {
15144             cls: 'form-group' //input-group
15145         };
15146         
15147         var input =  {
15148             tag: 'input',
15149             id : id,
15150             type : this.inputType,
15151             cls : 'form-control x-combo-noedit',
15152             autocomplete: 'new-password',
15153             placeholder : this.placeholder || '',
15154             readonly : true
15155         };
15156         
15157         if (this.name) {
15158             input.name = this.name;
15159         }
15160         
15161         if (this.size) {
15162             input.cls += ' input-' + this.size;
15163         }
15164         
15165         if (this.disabled) {
15166             input.disabled = true;
15167         }
15168         
15169         var inputblock = {
15170             cls : '',
15171             cn : [
15172                 input
15173             ]
15174         };
15175         
15176         if(this.before){
15177             inputblock.cls += ' input-group';
15178             
15179             inputblock.cn.unshift({
15180                 tag :'span',
15181                 cls : 'input-group-addon input-group-prepend input-group-text',
15182                 html : this.before
15183             });
15184         }
15185         
15186         if(this.removable && !this.multiple){
15187             inputblock.cls += ' roo-removable';
15188             
15189             inputblock.cn.push({
15190                 tag: 'button',
15191                 html : 'x',
15192                 cls : 'roo-combo-removable-btn close'
15193             });
15194         }
15195
15196         if(this.hasFeedback && !this.allowBlank){
15197             
15198             inputblock.cls += ' has-feedback';
15199             
15200             inputblock.cn.push({
15201                 tag: 'span',
15202                 cls: 'glyphicon form-control-feedback'
15203             });
15204             
15205         }
15206         
15207         if (this.after) {
15208             
15209             inputblock.cls += (this.before) ? '' : ' input-group';
15210             
15211             inputblock.cn.push({
15212                 tag :'span',
15213                 cls : 'input-group-addon input-group-append input-group-text',
15214                 html : this.after
15215             });
15216         }
15217
15218         
15219         var ibwrap = inputblock;
15220         
15221         if(this.multiple){
15222             ibwrap = {
15223                 tag: 'ul',
15224                 cls: 'roo-select2-choices',
15225                 cn:[
15226                     {
15227                         tag: 'li',
15228                         cls: 'roo-select2-search-field',
15229                         cn: [
15230
15231                             inputblock
15232                         ]
15233                     }
15234                 ]
15235             };
15236         
15237             
15238         }
15239         
15240         var combobox = {
15241             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15242             cn: [
15243                 {
15244                     tag: 'input',
15245                     type : 'hidden',
15246                     cls: 'form-hidden-field'
15247                 },
15248                 ibwrap
15249             ]
15250         };
15251         
15252         if(!this.multiple && this.showToggleBtn){
15253             
15254             var caret = {
15255                         tag: 'span',
15256                         cls: 'caret'
15257             };
15258             
15259             if (this.caret != false) {
15260                 caret = {
15261                      tag: 'i',
15262                      cls: 'fa fa-' + this.caret
15263                 };
15264                 
15265             }
15266             
15267             combobox.cn.push({
15268                 tag :'span',
15269                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15270                 cn : [
15271                     caret,
15272                     {
15273                         tag: 'span',
15274                         cls: 'combobox-clear',
15275                         cn  : [
15276                             {
15277                                 tag : 'i',
15278                                 cls: 'icon-remove'
15279                             }
15280                         ]
15281                     }
15282                 ]
15283
15284             })
15285         }
15286         
15287         if(this.multiple){
15288             combobox.cls += ' roo-select2-container-multi';
15289         }
15290         
15291         var align = this.labelAlign || this.parentLabelAlign();
15292         
15293         if (align ==='left' && this.fieldLabel.length) {
15294
15295             cfg.cn = [
15296                 {
15297                    tag : 'i',
15298                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15299                    tooltip : 'This field is required'
15300                 },
15301                 {
15302                     tag: 'label',
15303                     cls : 'control-label col-form-label',
15304                     html : this.fieldLabel
15305
15306                 },
15307                 {
15308                     cls : '', 
15309                     cn: [
15310                         combobox
15311                     ]
15312                 }
15313             ];
15314             
15315             var labelCfg = cfg.cn[1];
15316             var contentCfg = cfg.cn[2];
15317             
15318
15319             if(this.indicatorpos == 'right'){
15320                 cfg.cn = [
15321                     {
15322                         tag: 'label',
15323                         'for' :  id,
15324                         cls : 'control-label col-form-label',
15325                         cn : [
15326                             {
15327                                 tag : 'span',
15328                                 html : this.fieldLabel
15329                             },
15330                             {
15331                                 tag : 'i',
15332                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15333                                 tooltip : 'This field is required'
15334                             }
15335                         ]
15336                     },
15337                     {
15338                         cls : "",
15339                         cn: [
15340                             combobox
15341                         ]
15342                     }
15343
15344                 ];
15345                 
15346                 labelCfg = cfg.cn[0];
15347                 contentCfg = cfg.cn[1];
15348             }
15349             
15350            
15351             
15352             if(this.labelWidth > 12){
15353                 labelCfg.style = "width: " + this.labelWidth + 'px';
15354             }
15355             
15356             if(this.labelWidth < 13 && this.labelmd == 0){
15357                 this.labelmd = this.labelWidth;
15358             }
15359             
15360             if(this.labellg > 0){
15361                 labelCfg.cls += ' col-lg-' + this.labellg;
15362                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15363             }
15364             
15365             if(this.labelmd > 0){
15366                 labelCfg.cls += ' col-md-' + this.labelmd;
15367                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15368             }
15369             
15370             if(this.labelsm > 0){
15371                 labelCfg.cls += ' col-sm-' + this.labelsm;
15372                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15373             }
15374             
15375             if(this.labelxs > 0){
15376                 labelCfg.cls += ' col-xs-' + this.labelxs;
15377                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15378             }
15379                 
15380                 
15381         } else if ( this.fieldLabel.length) {
15382             cfg.cn = [
15383                 {
15384                    tag : 'i',
15385                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15386                    tooltip : 'This field is required'
15387                 },
15388                 {
15389                     tag: 'label',
15390                     cls : 'control-label',
15391                     html : this.fieldLabel
15392
15393                 },
15394                 {
15395                     cls : '', 
15396                     cn: [
15397                         combobox
15398                     ]
15399                 }
15400             ];
15401             
15402             if(this.indicatorpos == 'right'){
15403                 cfg.cn = [
15404                     {
15405                         tag: 'label',
15406                         cls : 'control-label',
15407                         html : this.fieldLabel,
15408                         cn : [
15409                             {
15410                                tag : 'i',
15411                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15412                                tooltip : 'This field is required'
15413                             }
15414                         ]
15415                     },
15416                     {
15417                         cls : '', 
15418                         cn: [
15419                             combobox
15420                         ]
15421                     }
15422                 ];
15423             }
15424         } else {
15425             cfg.cn = combobox;    
15426         }
15427         
15428         
15429         var settings = this;
15430         
15431         ['xs','sm','md','lg'].map(function(size){
15432             if (settings[size]) {
15433                 cfg.cls += ' col-' + size + '-' + settings[size];
15434             }
15435         });
15436         
15437         return cfg;
15438     },
15439     
15440     initTouchView : function()
15441     {
15442         this.renderTouchView();
15443         
15444         this.touchViewEl.on('scroll', function(){
15445             this.el.dom.scrollTop = 0;
15446         }, this);
15447         
15448         this.originalValue = this.getValue();
15449         
15450         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15451         
15452         this.inputEl().on("click", this.showTouchView, this);
15453         if (this.triggerEl) {
15454             this.triggerEl.on("click", this.showTouchView, this);
15455         }
15456         
15457         
15458         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15459         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15460         
15461         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15462         
15463         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15464         this.store.on('load', this.onTouchViewLoad, this);
15465         this.store.on('loadexception', this.onTouchViewLoadException, this);
15466         
15467         if(this.hiddenName){
15468             
15469             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15470             
15471             this.hiddenField.dom.value =
15472                 this.hiddenValue !== undefined ? this.hiddenValue :
15473                 this.value !== undefined ? this.value : '';
15474         
15475             this.el.dom.removeAttribute('name');
15476             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15477         }
15478         
15479         if(this.multiple){
15480             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15481             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15482         }
15483         
15484         if(this.removable && !this.multiple){
15485             var close = this.closeTriggerEl();
15486             if(close){
15487                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15488                 close.on('click', this.removeBtnClick, this, close);
15489             }
15490         }
15491         /*
15492          * fix the bug in Safari iOS8
15493          */
15494         this.inputEl().on("focus", function(e){
15495             document.activeElement.blur();
15496         }, this);
15497         
15498         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15499         
15500         return;
15501         
15502         
15503     },
15504     
15505     renderTouchView : function()
15506     {
15507         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15508         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15509         
15510         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15511         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15512         
15513         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15514         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15515         this.touchViewBodyEl.setStyle('overflow', 'auto');
15516         
15517         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15518         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15519         
15520         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15521         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15522         
15523     },
15524     
15525     showTouchView : function()
15526     {
15527         if(this.disabled){
15528             return;
15529         }
15530         
15531         this.touchViewHeaderEl.hide();
15532
15533         if(this.modalTitle.length){
15534             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15535             this.touchViewHeaderEl.show();
15536         }
15537
15538         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15539         this.touchViewEl.show();
15540
15541         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15542         
15543         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15544         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15545
15546         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15547
15548         if(this.modalTitle.length){
15549             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15550         }
15551         
15552         this.touchViewBodyEl.setHeight(bodyHeight);
15553
15554         if(this.animate){
15555             var _this = this;
15556             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15557         }else{
15558             this.touchViewEl.addClass('in');
15559         }
15560         
15561         if(this._touchViewMask){
15562             Roo.get(document.body).addClass("x-body-masked");
15563             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15564             this._touchViewMask.setStyle('z-index', 10000);
15565             this._touchViewMask.addClass('show');
15566         }
15567         
15568         this.doTouchViewQuery();
15569         
15570     },
15571     
15572     hideTouchView : function()
15573     {
15574         this.touchViewEl.removeClass('in');
15575
15576         if(this.animate){
15577             var _this = this;
15578             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15579         }else{
15580             this.touchViewEl.setStyle('display', 'none');
15581         }
15582         
15583         if(this._touchViewMask){
15584             this._touchViewMask.removeClass('show');
15585             Roo.get(document.body).removeClass("x-body-masked");
15586         }
15587     },
15588     
15589     setTouchViewValue : function()
15590     {
15591         if(this.multiple){
15592             this.clearItem();
15593         
15594             var _this = this;
15595
15596             Roo.each(this.tickItems, function(o){
15597                 this.addItem(o);
15598             }, this);
15599         }
15600         
15601         this.hideTouchView();
15602     },
15603     
15604     doTouchViewQuery : function()
15605     {
15606         var qe = {
15607             query: '',
15608             forceAll: true,
15609             combo: this,
15610             cancel:false
15611         };
15612         
15613         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15614             return false;
15615         }
15616         
15617         if(!this.alwaysQuery || this.mode == 'local'){
15618             this.onTouchViewLoad();
15619             return;
15620         }
15621         
15622         this.store.load();
15623     },
15624     
15625     onTouchViewBeforeLoad : function(combo,opts)
15626     {
15627         return;
15628     },
15629
15630     // private
15631     onTouchViewLoad : function()
15632     {
15633         if(this.store.getCount() < 1){
15634             this.onTouchViewEmptyResults();
15635             return;
15636         }
15637         
15638         this.clearTouchView();
15639         
15640         var rawValue = this.getRawValue();
15641         
15642         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15643         
15644         this.tickItems = [];
15645         
15646         this.store.data.each(function(d, rowIndex){
15647             var row = this.touchViewListGroup.createChild(template);
15648             
15649             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15650                 row.addClass(d.data.cls);
15651             }
15652             
15653             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15654                 var cfg = {
15655                     data : d.data,
15656                     html : d.data[this.displayField]
15657                 };
15658                 
15659                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15660                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15661                 }
15662             }
15663             row.removeClass('selected');
15664             if(!this.multiple && this.valueField &&
15665                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15666             {
15667                 // radio buttons..
15668                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15669                 row.addClass('selected');
15670             }
15671             
15672             if(this.multiple && this.valueField &&
15673                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15674             {
15675                 
15676                 // checkboxes...
15677                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15678                 this.tickItems.push(d.data);
15679             }
15680             
15681             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15682             
15683         }, this);
15684         
15685         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15686         
15687         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15688
15689         if(this.modalTitle.length){
15690             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15691         }
15692
15693         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15694         
15695         if(this.mobile_restrict_height && listHeight < bodyHeight){
15696             this.touchViewBodyEl.setHeight(listHeight);
15697         }
15698         
15699         var _this = this;
15700         
15701         if(firstChecked && listHeight > bodyHeight){
15702             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15703         }
15704         
15705     },
15706     
15707     onTouchViewLoadException : function()
15708     {
15709         this.hideTouchView();
15710     },
15711     
15712     onTouchViewEmptyResults : function()
15713     {
15714         this.clearTouchView();
15715         
15716         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15717         
15718         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15719         
15720     },
15721     
15722     clearTouchView : function()
15723     {
15724         this.touchViewListGroup.dom.innerHTML = '';
15725     },
15726     
15727     onTouchViewClick : function(e, el, o)
15728     {
15729         e.preventDefault();
15730         
15731         var row = o.row;
15732         var rowIndex = o.rowIndex;
15733         
15734         var r = this.store.getAt(rowIndex);
15735         
15736         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15737             
15738             if(!this.multiple){
15739                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15740                     c.dom.removeAttribute('checked');
15741                 }, this);
15742
15743                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15744
15745                 this.setFromData(r.data);
15746
15747                 var close = this.closeTriggerEl();
15748
15749                 if(close){
15750                     close.show();
15751                 }
15752
15753                 this.hideTouchView();
15754
15755                 this.fireEvent('select', this, r, rowIndex);
15756
15757                 return;
15758             }
15759
15760             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15761                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15762                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15763                 return;
15764             }
15765
15766             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15767             this.addItem(r.data);
15768             this.tickItems.push(r.data);
15769         }
15770     },
15771     
15772     getAutoCreateNativeIOS : function()
15773     {
15774         var cfg = {
15775             cls: 'form-group' //input-group,
15776         };
15777         
15778         var combobox =  {
15779             tag: 'select',
15780             cls : 'roo-ios-select'
15781         };
15782         
15783         if (this.name) {
15784             combobox.name = this.name;
15785         }
15786         
15787         if (this.disabled) {
15788             combobox.disabled = true;
15789         }
15790         
15791         var settings = this;
15792         
15793         ['xs','sm','md','lg'].map(function(size){
15794             if (settings[size]) {
15795                 cfg.cls += ' col-' + size + '-' + settings[size];
15796             }
15797         });
15798         
15799         cfg.cn = combobox;
15800         
15801         return cfg;
15802         
15803     },
15804     
15805     initIOSView : function()
15806     {
15807         this.store.on('load', this.onIOSViewLoad, this);
15808         
15809         return;
15810     },
15811     
15812     onIOSViewLoad : function()
15813     {
15814         if(this.store.getCount() < 1){
15815             return;
15816         }
15817         
15818         this.clearIOSView();
15819         
15820         if(this.allowBlank) {
15821             
15822             var default_text = '-- SELECT --';
15823             
15824             if(this.placeholder.length){
15825                 default_text = this.placeholder;
15826             }
15827             
15828             if(this.emptyTitle.length){
15829                 default_text += ' - ' + this.emptyTitle + ' -';
15830             }
15831             
15832             var opt = this.inputEl().createChild({
15833                 tag: 'option',
15834                 value : 0,
15835                 html : default_text
15836             });
15837             
15838             var o = {};
15839             o[this.valueField] = 0;
15840             o[this.displayField] = default_text;
15841             
15842             this.ios_options.push({
15843                 data : o,
15844                 el : opt
15845             });
15846             
15847         }
15848         
15849         this.store.data.each(function(d, rowIndex){
15850             
15851             var html = '';
15852             
15853             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15854                 html = d.data[this.displayField];
15855             }
15856             
15857             var value = '';
15858             
15859             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15860                 value = d.data[this.valueField];
15861             }
15862             
15863             var option = {
15864                 tag: 'option',
15865                 value : value,
15866                 html : html
15867             };
15868             
15869             if(this.value == d.data[this.valueField]){
15870                 option['selected'] = true;
15871             }
15872             
15873             var opt = this.inputEl().createChild(option);
15874             
15875             this.ios_options.push({
15876                 data : d.data,
15877                 el : opt
15878             });
15879             
15880         }, this);
15881         
15882         this.inputEl().on('change', function(){
15883            this.fireEvent('select', this);
15884         }, this);
15885         
15886     },
15887     
15888     clearIOSView: function()
15889     {
15890         this.inputEl().dom.innerHTML = '';
15891         
15892         this.ios_options = [];
15893     },
15894     
15895     setIOSValue: function(v)
15896     {
15897         this.value = v;
15898         
15899         if(!this.ios_options){
15900             return;
15901         }
15902         
15903         Roo.each(this.ios_options, function(opts){
15904            
15905            opts.el.dom.removeAttribute('selected');
15906            
15907            if(opts.data[this.valueField] != v){
15908                return;
15909            }
15910            
15911            opts.el.dom.setAttribute('selected', true);
15912            
15913         }, this);
15914     }
15915
15916     /** 
15917     * @cfg {Boolean} grow 
15918     * @hide 
15919     */
15920     /** 
15921     * @cfg {Number} growMin 
15922     * @hide 
15923     */
15924     /** 
15925     * @cfg {Number} growMax 
15926     * @hide 
15927     */
15928     /**
15929      * @hide
15930      * @method autoSize
15931      */
15932 });
15933
15934 Roo.apply(Roo.bootstrap.ComboBox,  {
15935     
15936     header : {
15937         tag: 'div',
15938         cls: 'modal-header',
15939         cn: [
15940             {
15941                 tag: 'h4',
15942                 cls: 'modal-title'
15943             }
15944         ]
15945     },
15946     
15947     body : {
15948         tag: 'div',
15949         cls: 'modal-body',
15950         cn: [
15951             {
15952                 tag: 'ul',
15953                 cls: 'list-group'
15954             }
15955         ]
15956     },
15957     
15958     listItemRadio : {
15959         tag: 'li',
15960         cls: 'list-group-item',
15961         cn: [
15962             {
15963                 tag: 'span',
15964                 cls: 'roo-combobox-list-group-item-value'
15965             },
15966             {
15967                 tag: 'div',
15968                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15969                 cn: [
15970                     {
15971                         tag: 'input',
15972                         type: 'radio'
15973                     },
15974                     {
15975                         tag: 'label'
15976                     }
15977                 ]
15978             }
15979         ]
15980     },
15981     
15982     listItemCheckbox : {
15983         tag: 'li',
15984         cls: 'list-group-item',
15985         cn: [
15986             {
15987                 tag: 'span',
15988                 cls: 'roo-combobox-list-group-item-value'
15989             },
15990             {
15991                 tag: 'div',
15992                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15993                 cn: [
15994                     {
15995                         tag: 'input',
15996                         type: 'checkbox'
15997                     },
15998                     {
15999                         tag: 'label'
16000                     }
16001                 ]
16002             }
16003         ]
16004     },
16005     
16006     emptyResult : {
16007         tag: 'div',
16008         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16009     },
16010     
16011     footer : {
16012         tag: 'div',
16013         cls: 'modal-footer',
16014         cn: [
16015             {
16016                 tag: 'div',
16017                 cls: 'row',
16018                 cn: [
16019                     {
16020                         tag: 'div',
16021                         cls: 'col-xs-6 text-left',
16022                         cn: {
16023                             tag: 'button',
16024                             cls: 'btn btn-danger roo-touch-view-cancel',
16025                             html: 'Cancel'
16026                         }
16027                     },
16028                     {
16029                         tag: 'div',
16030                         cls: 'col-xs-6 text-right',
16031                         cn: {
16032                             tag: 'button',
16033                             cls: 'btn btn-success roo-touch-view-ok',
16034                             html: 'OK'
16035                         }
16036                     }
16037                 ]
16038             }
16039         ]
16040         
16041     }
16042 });
16043
16044 Roo.apply(Roo.bootstrap.ComboBox,  {
16045     
16046     touchViewTemplate : {
16047         tag: 'div',
16048         cls: 'modal fade roo-combobox-touch-view',
16049         cn: [
16050             {
16051                 tag: 'div',
16052                 cls: 'modal-dialog',
16053                 style : 'position:fixed', // we have to fix position....
16054                 cn: [
16055                     {
16056                         tag: 'div',
16057                         cls: 'modal-content',
16058                         cn: [
16059                             Roo.bootstrap.ComboBox.header,
16060                             Roo.bootstrap.ComboBox.body,
16061                             Roo.bootstrap.ComboBox.footer
16062                         ]
16063                     }
16064                 ]
16065             }
16066         ]
16067     }
16068 });/*
16069  * Based on:
16070  * Ext JS Library 1.1.1
16071  * Copyright(c) 2006-2007, Ext JS, LLC.
16072  *
16073  * Originally Released Under LGPL - original licence link has changed is not relivant.
16074  *
16075  * Fork - LGPL
16076  * <script type="text/javascript">
16077  */
16078
16079 /**
16080  * @class Roo.View
16081  * @extends Roo.util.Observable
16082  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16083  * This class also supports single and multi selection modes. <br>
16084  * Create a data model bound view:
16085  <pre><code>
16086  var store = new Roo.data.Store(...);
16087
16088  var view = new Roo.View({
16089     el : "my-element",
16090     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16091  
16092     singleSelect: true,
16093     selectedClass: "ydataview-selected",
16094     store: store
16095  });
16096
16097  // listen for node click?
16098  view.on("click", function(vw, index, node, e){
16099  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16100  });
16101
16102  // load XML data
16103  dataModel.load("foobar.xml");
16104  </code></pre>
16105  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16106  * <br><br>
16107  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16108  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16109  * 
16110  * Note: old style constructor is still suported (container, template, config)
16111  * 
16112  * @constructor
16113  * Create a new View
16114  * @param {Object} config The config object
16115  * 
16116  */
16117 Roo.View = function(config, depreciated_tpl, depreciated_config){
16118     
16119     this.parent = false;
16120     
16121     if (typeof(depreciated_tpl) == 'undefined') {
16122         // new way.. - universal constructor.
16123         Roo.apply(this, config);
16124         this.el  = Roo.get(this.el);
16125     } else {
16126         // old format..
16127         this.el  = Roo.get(config);
16128         this.tpl = depreciated_tpl;
16129         Roo.apply(this, depreciated_config);
16130     }
16131     this.wrapEl  = this.el.wrap().wrap();
16132     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16133     
16134     
16135     if(typeof(this.tpl) == "string"){
16136         this.tpl = new Roo.Template(this.tpl);
16137     } else {
16138         // support xtype ctors..
16139         this.tpl = new Roo.factory(this.tpl, Roo);
16140     }
16141     
16142     
16143     this.tpl.compile();
16144     
16145     /** @private */
16146     this.addEvents({
16147         /**
16148          * @event beforeclick
16149          * Fires before a click is processed. Returns false to cancel the default action.
16150          * @param {Roo.View} this
16151          * @param {Number} index The index of the target node
16152          * @param {HTMLElement} node The target node
16153          * @param {Roo.EventObject} e The raw event object
16154          */
16155             "beforeclick" : true,
16156         /**
16157          * @event click
16158          * Fires when a template node is clicked.
16159          * @param {Roo.View} this
16160          * @param {Number} index The index of the target node
16161          * @param {HTMLElement} node The target node
16162          * @param {Roo.EventObject} e The raw event object
16163          */
16164             "click" : true,
16165         /**
16166          * @event dblclick
16167          * Fires when a template node is double clicked.
16168          * @param {Roo.View} this
16169          * @param {Number} index The index of the target node
16170          * @param {HTMLElement} node The target node
16171          * @param {Roo.EventObject} e The raw event object
16172          */
16173             "dblclick" : true,
16174         /**
16175          * @event contextmenu
16176          * Fires when a template node is right clicked.
16177          * @param {Roo.View} this
16178          * @param {Number} index The index of the target node
16179          * @param {HTMLElement} node The target node
16180          * @param {Roo.EventObject} e The raw event object
16181          */
16182             "contextmenu" : true,
16183         /**
16184          * @event selectionchange
16185          * Fires when the selected nodes change.
16186          * @param {Roo.View} this
16187          * @param {Array} selections Array of the selected nodes
16188          */
16189             "selectionchange" : true,
16190     
16191         /**
16192          * @event beforeselect
16193          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16194          * @param {Roo.View} this
16195          * @param {HTMLElement} node The node to be selected
16196          * @param {Array} selections Array of currently selected nodes
16197          */
16198             "beforeselect" : true,
16199         /**
16200          * @event preparedata
16201          * Fires on every row to render, to allow you to change the data.
16202          * @param {Roo.View} this
16203          * @param {Object} data to be rendered (change this)
16204          */
16205           "preparedata" : true
16206           
16207           
16208         });
16209
16210
16211
16212     this.el.on({
16213         "click": this.onClick,
16214         "dblclick": this.onDblClick,
16215         "contextmenu": this.onContextMenu,
16216         scope:this
16217     });
16218
16219     this.selections = [];
16220     this.nodes = [];
16221     this.cmp = new Roo.CompositeElementLite([]);
16222     if(this.store){
16223         this.store = Roo.factory(this.store, Roo.data);
16224         this.setStore(this.store, true);
16225     }
16226     
16227     if ( this.footer && this.footer.xtype) {
16228            
16229          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16230         
16231         this.footer.dataSource = this.store;
16232         this.footer.container = fctr;
16233         this.footer = Roo.factory(this.footer, Roo);
16234         fctr.insertFirst(this.el);
16235         
16236         // this is a bit insane - as the paging toolbar seems to detach the el..
16237 //        dom.parentNode.parentNode.parentNode
16238          // they get detached?
16239     }
16240     
16241     
16242     Roo.View.superclass.constructor.call(this);
16243     
16244     
16245 };
16246
16247 Roo.extend(Roo.View, Roo.util.Observable, {
16248     
16249      /**
16250      * @cfg {Roo.data.Store} store Data store to load data from.
16251      */
16252     store : false,
16253     
16254     /**
16255      * @cfg {String|Roo.Element} el The container element.
16256      */
16257     el : '',
16258     
16259     /**
16260      * @cfg {String|Roo.Template} tpl The template used by this View 
16261      */
16262     tpl : false,
16263     /**
16264      * @cfg {String} dataName the named area of the template to use as the data area
16265      *                          Works with domtemplates roo-name="name"
16266      */
16267     dataName: false,
16268     /**
16269      * @cfg {String} selectedClass The css class to add to selected nodes
16270      */
16271     selectedClass : "x-view-selected",
16272      /**
16273      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16274      */
16275     emptyText : "",
16276     
16277     /**
16278      * @cfg {String} text to display on mask (default Loading)
16279      */
16280     mask : false,
16281     /**
16282      * @cfg {Boolean} multiSelect Allow multiple selection
16283      */
16284     multiSelect : false,
16285     /**
16286      * @cfg {Boolean} singleSelect Allow single selection
16287      */
16288     singleSelect:  false,
16289     
16290     /**
16291      * @cfg {Boolean} toggleSelect - selecting 
16292      */
16293     toggleSelect : false,
16294     
16295     /**
16296      * @cfg {Boolean} tickable - selecting 
16297      */
16298     tickable : false,
16299     
16300     /**
16301      * Returns the element this view is bound to.
16302      * @return {Roo.Element}
16303      */
16304     getEl : function(){
16305         return this.wrapEl;
16306     },
16307     
16308     
16309
16310     /**
16311      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16312      */
16313     refresh : function(){
16314         //Roo.log('refresh');
16315         var t = this.tpl;
16316         
16317         // if we are using something like 'domtemplate', then
16318         // the what gets used is:
16319         // t.applySubtemplate(NAME, data, wrapping data..)
16320         // the outer template then get' applied with
16321         //     the store 'extra data'
16322         // and the body get's added to the
16323         //      roo-name="data" node?
16324         //      <span class='roo-tpl-{name}'></span> ?????
16325         
16326         
16327         
16328         this.clearSelections();
16329         this.el.update("");
16330         var html = [];
16331         var records = this.store.getRange();
16332         if(records.length < 1) {
16333             
16334             // is this valid??  = should it render a template??
16335             
16336             this.el.update(this.emptyText);
16337             return;
16338         }
16339         var el = this.el;
16340         if (this.dataName) {
16341             this.el.update(t.apply(this.store.meta)); //????
16342             el = this.el.child('.roo-tpl-' + this.dataName);
16343         }
16344         
16345         for(var i = 0, len = records.length; i < len; i++){
16346             var data = this.prepareData(records[i].data, i, records[i]);
16347             this.fireEvent("preparedata", this, data, i, records[i]);
16348             
16349             var d = Roo.apply({}, data);
16350             
16351             if(this.tickable){
16352                 Roo.apply(d, {'roo-id' : Roo.id()});
16353                 
16354                 var _this = this;
16355             
16356                 Roo.each(this.parent.item, function(item){
16357                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16358                         return;
16359                     }
16360                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16361                 });
16362             }
16363             
16364             html[html.length] = Roo.util.Format.trim(
16365                 this.dataName ?
16366                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16367                     t.apply(d)
16368             );
16369         }
16370         
16371         
16372         
16373         el.update(html.join(""));
16374         this.nodes = el.dom.childNodes;
16375         this.updateIndexes(0);
16376     },
16377     
16378
16379     /**
16380      * Function to override to reformat the data that is sent to
16381      * the template for each node.
16382      * DEPRICATED - use the preparedata event handler.
16383      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16384      * a JSON object for an UpdateManager bound view).
16385      */
16386     prepareData : function(data, index, record)
16387     {
16388         this.fireEvent("preparedata", this, data, index, record);
16389         return data;
16390     },
16391
16392     onUpdate : function(ds, record){
16393         // Roo.log('on update');   
16394         this.clearSelections();
16395         var index = this.store.indexOf(record);
16396         var n = this.nodes[index];
16397         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16398         n.parentNode.removeChild(n);
16399         this.updateIndexes(index, index);
16400     },
16401
16402     
16403     
16404 // --------- FIXME     
16405     onAdd : function(ds, records, index)
16406     {
16407         //Roo.log(['on Add', ds, records, index] );        
16408         this.clearSelections();
16409         if(this.nodes.length == 0){
16410             this.refresh();
16411             return;
16412         }
16413         var n = this.nodes[index];
16414         for(var i = 0, len = records.length; i < len; i++){
16415             var d = this.prepareData(records[i].data, i, records[i]);
16416             if(n){
16417                 this.tpl.insertBefore(n, d);
16418             }else{
16419                 
16420                 this.tpl.append(this.el, d);
16421             }
16422         }
16423         this.updateIndexes(index);
16424     },
16425
16426     onRemove : function(ds, record, index){
16427        // Roo.log('onRemove');
16428         this.clearSelections();
16429         var el = this.dataName  ?
16430             this.el.child('.roo-tpl-' + this.dataName) :
16431             this.el; 
16432         
16433         el.dom.removeChild(this.nodes[index]);
16434         this.updateIndexes(index);
16435     },
16436
16437     /**
16438      * Refresh an individual node.
16439      * @param {Number} index
16440      */
16441     refreshNode : function(index){
16442         this.onUpdate(this.store, this.store.getAt(index));
16443     },
16444
16445     updateIndexes : function(startIndex, endIndex){
16446         var ns = this.nodes;
16447         startIndex = startIndex || 0;
16448         endIndex = endIndex || ns.length - 1;
16449         for(var i = startIndex; i <= endIndex; i++){
16450             ns[i].nodeIndex = i;
16451         }
16452     },
16453
16454     /**
16455      * Changes the data store this view uses and refresh the view.
16456      * @param {Store} store
16457      */
16458     setStore : function(store, initial){
16459         if(!initial && this.store){
16460             this.store.un("datachanged", this.refresh);
16461             this.store.un("add", this.onAdd);
16462             this.store.un("remove", this.onRemove);
16463             this.store.un("update", this.onUpdate);
16464             this.store.un("clear", this.refresh);
16465             this.store.un("beforeload", this.onBeforeLoad);
16466             this.store.un("load", this.onLoad);
16467             this.store.un("loadexception", this.onLoad);
16468         }
16469         if(store){
16470           
16471             store.on("datachanged", this.refresh, this);
16472             store.on("add", this.onAdd, this);
16473             store.on("remove", this.onRemove, this);
16474             store.on("update", this.onUpdate, this);
16475             store.on("clear", this.refresh, this);
16476             store.on("beforeload", this.onBeforeLoad, this);
16477             store.on("load", this.onLoad, this);
16478             store.on("loadexception", this.onLoad, this);
16479         }
16480         
16481         if(store){
16482             this.refresh();
16483         }
16484     },
16485     /**
16486      * onbeforeLoad - masks the loading area.
16487      *
16488      */
16489     onBeforeLoad : function(store,opts)
16490     {
16491          //Roo.log('onBeforeLoad');   
16492         if (!opts.add) {
16493             this.el.update("");
16494         }
16495         this.el.mask(this.mask ? this.mask : "Loading" ); 
16496     },
16497     onLoad : function ()
16498     {
16499         this.el.unmask();
16500     },
16501     
16502
16503     /**
16504      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16505      * @param {HTMLElement} node
16506      * @return {HTMLElement} The template node
16507      */
16508     findItemFromChild : function(node){
16509         var el = this.dataName  ?
16510             this.el.child('.roo-tpl-' + this.dataName,true) :
16511             this.el.dom; 
16512         
16513         if(!node || node.parentNode == el){
16514                     return node;
16515             }
16516             var p = node.parentNode;
16517             while(p && p != el){
16518             if(p.parentNode == el){
16519                 return p;
16520             }
16521             p = p.parentNode;
16522         }
16523             return null;
16524     },
16525
16526     /** @ignore */
16527     onClick : function(e){
16528         var item = this.findItemFromChild(e.getTarget());
16529         if(item){
16530             var index = this.indexOf(item);
16531             if(this.onItemClick(item, index, e) !== false){
16532                 this.fireEvent("click", this, index, item, e);
16533             }
16534         }else{
16535             this.clearSelections();
16536         }
16537     },
16538
16539     /** @ignore */
16540     onContextMenu : function(e){
16541         var item = this.findItemFromChild(e.getTarget());
16542         if(item){
16543             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16544         }
16545     },
16546
16547     /** @ignore */
16548     onDblClick : function(e){
16549         var item = this.findItemFromChild(e.getTarget());
16550         if(item){
16551             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16552         }
16553     },
16554
16555     onItemClick : function(item, index, e)
16556     {
16557         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16558             return false;
16559         }
16560         if (this.toggleSelect) {
16561             var m = this.isSelected(item) ? 'unselect' : 'select';
16562             //Roo.log(m);
16563             var _t = this;
16564             _t[m](item, true, false);
16565             return true;
16566         }
16567         if(this.multiSelect || this.singleSelect){
16568             if(this.multiSelect && e.shiftKey && this.lastSelection){
16569                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16570             }else{
16571                 this.select(item, this.multiSelect && e.ctrlKey);
16572                 this.lastSelection = item;
16573             }
16574             
16575             if(!this.tickable){
16576                 e.preventDefault();
16577             }
16578             
16579         }
16580         return true;
16581     },
16582
16583     /**
16584      * Get the number of selected nodes.
16585      * @return {Number}
16586      */
16587     getSelectionCount : function(){
16588         return this.selections.length;
16589     },
16590
16591     /**
16592      * Get the currently selected nodes.
16593      * @return {Array} An array of HTMLElements
16594      */
16595     getSelectedNodes : function(){
16596         return this.selections;
16597     },
16598
16599     /**
16600      * Get the indexes of the selected nodes.
16601      * @return {Array}
16602      */
16603     getSelectedIndexes : function(){
16604         var indexes = [], s = this.selections;
16605         for(var i = 0, len = s.length; i < len; i++){
16606             indexes.push(s[i].nodeIndex);
16607         }
16608         return indexes;
16609     },
16610
16611     /**
16612      * Clear all selections
16613      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16614      */
16615     clearSelections : function(suppressEvent){
16616         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16617             this.cmp.elements = this.selections;
16618             this.cmp.removeClass(this.selectedClass);
16619             this.selections = [];
16620             if(!suppressEvent){
16621                 this.fireEvent("selectionchange", this, this.selections);
16622             }
16623         }
16624     },
16625
16626     /**
16627      * Returns true if the passed node is selected
16628      * @param {HTMLElement/Number} node The node or node index
16629      * @return {Boolean}
16630      */
16631     isSelected : function(node){
16632         var s = this.selections;
16633         if(s.length < 1){
16634             return false;
16635         }
16636         node = this.getNode(node);
16637         return s.indexOf(node) !== -1;
16638     },
16639
16640     /**
16641      * Selects nodes.
16642      * @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
16643      * @param {Boolean} keepExisting (optional) true to keep existing selections
16644      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16645      */
16646     select : function(nodeInfo, keepExisting, suppressEvent){
16647         if(nodeInfo instanceof Array){
16648             if(!keepExisting){
16649                 this.clearSelections(true);
16650             }
16651             for(var i = 0, len = nodeInfo.length; i < len; i++){
16652                 this.select(nodeInfo[i], true, true);
16653             }
16654             return;
16655         } 
16656         var node = this.getNode(nodeInfo);
16657         if(!node || this.isSelected(node)){
16658             return; // already selected.
16659         }
16660         if(!keepExisting){
16661             this.clearSelections(true);
16662         }
16663         
16664         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16665             Roo.fly(node).addClass(this.selectedClass);
16666             this.selections.push(node);
16667             if(!suppressEvent){
16668                 this.fireEvent("selectionchange", this, this.selections);
16669             }
16670         }
16671         
16672         
16673     },
16674       /**
16675      * Unselects nodes.
16676      * @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
16677      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16678      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16679      */
16680     unselect : function(nodeInfo, keepExisting, suppressEvent)
16681     {
16682         if(nodeInfo instanceof Array){
16683             Roo.each(this.selections, function(s) {
16684                 this.unselect(s, nodeInfo);
16685             }, this);
16686             return;
16687         }
16688         var node = this.getNode(nodeInfo);
16689         if(!node || !this.isSelected(node)){
16690             //Roo.log("not selected");
16691             return; // not selected.
16692         }
16693         // fireevent???
16694         var ns = [];
16695         Roo.each(this.selections, function(s) {
16696             if (s == node ) {
16697                 Roo.fly(node).removeClass(this.selectedClass);
16698
16699                 return;
16700             }
16701             ns.push(s);
16702         },this);
16703         
16704         this.selections= ns;
16705         this.fireEvent("selectionchange", this, this.selections);
16706     },
16707
16708     /**
16709      * Gets a template node.
16710      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16711      * @return {HTMLElement} The node or null if it wasn't found
16712      */
16713     getNode : function(nodeInfo){
16714         if(typeof nodeInfo == "string"){
16715             return document.getElementById(nodeInfo);
16716         }else if(typeof nodeInfo == "number"){
16717             return this.nodes[nodeInfo];
16718         }
16719         return nodeInfo;
16720     },
16721
16722     /**
16723      * Gets a range template nodes.
16724      * @param {Number} startIndex
16725      * @param {Number} endIndex
16726      * @return {Array} An array of nodes
16727      */
16728     getNodes : function(start, end){
16729         var ns = this.nodes;
16730         start = start || 0;
16731         end = typeof end == "undefined" ? ns.length - 1 : end;
16732         var nodes = [];
16733         if(start <= end){
16734             for(var i = start; i <= end; i++){
16735                 nodes.push(ns[i]);
16736             }
16737         } else{
16738             for(var i = start; i >= end; i--){
16739                 nodes.push(ns[i]);
16740             }
16741         }
16742         return nodes;
16743     },
16744
16745     /**
16746      * Finds the index of the passed node
16747      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16748      * @return {Number} The index of the node or -1
16749      */
16750     indexOf : function(node){
16751         node = this.getNode(node);
16752         if(typeof node.nodeIndex == "number"){
16753             return node.nodeIndex;
16754         }
16755         var ns = this.nodes;
16756         for(var i = 0, len = ns.length; i < len; i++){
16757             if(ns[i] == node){
16758                 return i;
16759             }
16760         }
16761         return -1;
16762     }
16763 });
16764 /*
16765  * - LGPL
16766  *
16767  * based on jquery fullcalendar
16768  * 
16769  */
16770
16771 Roo.bootstrap = Roo.bootstrap || {};
16772 /**
16773  * @class Roo.bootstrap.Calendar
16774  * @extends Roo.bootstrap.Component
16775  * Bootstrap Calendar class
16776  * @cfg {Boolean} loadMask (true|false) default false
16777  * @cfg {Object} header generate the user specific header of the calendar, default false
16778
16779  * @constructor
16780  * Create a new Container
16781  * @param {Object} config The config object
16782  */
16783
16784
16785
16786 Roo.bootstrap.Calendar = function(config){
16787     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16788      this.addEvents({
16789         /**
16790              * @event select
16791              * Fires when a date is selected
16792              * @param {DatePicker} this
16793              * @param {Date} date The selected date
16794              */
16795         'select': true,
16796         /**
16797              * @event monthchange
16798              * Fires when the displayed month changes 
16799              * @param {DatePicker} this
16800              * @param {Date} date The selected month
16801              */
16802         'monthchange': true,
16803         /**
16804              * @event evententer
16805              * Fires when mouse over an event
16806              * @param {Calendar} this
16807              * @param {event} Event
16808              */
16809         'evententer': true,
16810         /**
16811              * @event eventleave
16812              * Fires when the mouse leaves an
16813              * @param {Calendar} this
16814              * @param {event}
16815              */
16816         'eventleave': true,
16817         /**
16818              * @event eventclick
16819              * Fires when the mouse click an
16820              * @param {Calendar} this
16821              * @param {event}
16822              */
16823         'eventclick': true
16824         
16825     });
16826
16827 };
16828
16829 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16830     
16831      /**
16832      * @cfg {Number} startDay
16833      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16834      */
16835     startDay : 0,
16836     
16837     loadMask : false,
16838     
16839     header : false,
16840       
16841     getAutoCreate : function(){
16842         
16843         
16844         var fc_button = function(name, corner, style, content ) {
16845             return Roo.apply({},{
16846                 tag : 'span',
16847                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16848                          (corner.length ?
16849                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16850                             ''
16851                         ),
16852                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16853                 unselectable: 'on'
16854             });
16855         };
16856         
16857         var header = {};
16858         
16859         if(!this.header){
16860             header = {
16861                 tag : 'table',
16862                 cls : 'fc-header',
16863                 style : 'width:100%',
16864                 cn : [
16865                     {
16866                         tag: 'tr',
16867                         cn : [
16868                             {
16869                                 tag : 'td',
16870                                 cls : 'fc-header-left',
16871                                 cn : [
16872                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16873                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16874                                     { tag: 'span', cls: 'fc-header-space' },
16875                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16876
16877
16878                                 ]
16879                             },
16880
16881                             {
16882                                 tag : 'td',
16883                                 cls : 'fc-header-center',
16884                                 cn : [
16885                                     {
16886                                         tag: 'span',
16887                                         cls: 'fc-header-title',
16888                                         cn : {
16889                                             tag: 'H2',
16890                                             html : 'month / year'
16891                                         }
16892                                     }
16893
16894                                 ]
16895                             },
16896                             {
16897                                 tag : 'td',
16898                                 cls : 'fc-header-right',
16899                                 cn : [
16900                               /*      fc_button('month', 'left', '', 'month' ),
16901                                     fc_button('week', '', '', 'week' ),
16902                                     fc_button('day', 'right', '', 'day' )
16903                                 */    
16904
16905                                 ]
16906                             }
16907
16908                         ]
16909                     }
16910                 ]
16911             };
16912         }
16913         
16914         header = this.header;
16915         
16916        
16917         var cal_heads = function() {
16918             var ret = [];
16919             // fixme - handle this.
16920             
16921             for (var i =0; i < Date.dayNames.length; i++) {
16922                 var d = Date.dayNames[i];
16923                 ret.push({
16924                     tag: 'th',
16925                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16926                     html : d.substring(0,3)
16927                 });
16928                 
16929             }
16930             ret[0].cls += ' fc-first';
16931             ret[6].cls += ' fc-last';
16932             return ret;
16933         };
16934         var cal_cell = function(n) {
16935             return  {
16936                 tag: 'td',
16937                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16938                 cn : [
16939                     {
16940                         cn : [
16941                             {
16942                                 cls: 'fc-day-number',
16943                                 html: 'D'
16944                             },
16945                             {
16946                                 cls: 'fc-day-content',
16947                              
16948                                 cn : [
16949                                      {
16950                                         style: 'position: relative;' // height: 17px;
16951                                     }
16952                                 ]
16953                             }
16954                             
16955                             
16956                         ]
16957                     }
16958                 ]
16959                 
16960             }
16961         };
16962         var cal_rows = function() {
16963             
16964             var ret = [];
16965             for (var r = 0; r < 6; r++) {
16966                 var row= {
16967                     tag : 'tr',
16968                     cls : 'fc-week',
16969                     cn : []
16970                 };
16971                 
16972                 for (var i =0; i < Date.dayNames.length; i++) {
16973                     var d = Date.dayNames[i];
16974                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16975
16976                 }
16977                 row.cn[0].cls+=' fc-first';
16978                 row.cn[0].cn[0].style = 'min-height:90px';
16979                 row.cn[6].cls+=' fc-last';
16980                 ret.push(row);
16981                 
16982             }
16983             ret[0].cls += ' fc-first';
16984             ret[4].cls += ' fc-prev-last';
16985             ret[5].cls += ' fc-last';
16986             return ret;
16987             
16988         };
16989         
16990         var cal_table = {
16991             tag: 'table',
16992             cls: 'fc-border-separate',
16993             style : 'width:100%',
16994             cellspacing  : 0,
16995             cn : [
16996                 { 
16997                     tag: 'thead',
16998                     cn : [
16999                         { 
17000                             tag: 'tr',
17001                             cls : 'fc-first fc-last',
17002                             cn : cal_heads()
17003                         }
17004                     ]
17005                 },
17006                 { 
17007                     tag: 'tbody',
17008                     cn : cal_rows()
17009                 }
17010                   
17011             ]
17012         };
17013          
17014          var cfg = {
17015             cls : 'fc fc-ltr',
17016             cn : [
17017                 header,
17018                 {
17019                     cls : 'fc-content',
17020                     style : "position: relative;",
17021                     cn : [
17022                         {
17023                             cls : 'fc-view fc-view-month fc-grid',
17024                             style : 'position: relative',
17025                             unselectable : 'on',
17026                             cn : [
17027                                 {
17028                                     cls : 'fc-event-container',
17029                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17030                                 },
17031                                 cal_table
17032                             ]
17033                         }
17034                     ]
17035     
17036                 }
17037            ] 
17038             
17039         };
17040         
17041          
17042         
17043         return cfg;
17044     },
17045     
17046     
17047     initEvents : function()
17048     {
17049         if(!this.store){
17050             throw "can not find store for calendar";
17051         }
17052         
17053         var mark = {
17054             tag: "div",
17055             cls:"x-dlg-mask",
17056             style: "text-align:center",
17057             cn: [
17058                 {
17059                     tag: "div",
17060                     style: "background-color:white;width:50%;margin:250 auto",
17061                     cn: [
17062                         {
17063                             tag: "img",
17064                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17065                         },
17066                         {
17067                             tag: "span",
17068                             html: "Loading"
17069                         }
17070                         
17071                     ]
17072                 }
17073             ]
17074         };
17075         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17076         
17077         var size = this.el.select('.fc-content', true).first().getSize();
17078         this.maskEl.setSize(size.width, size.height);
17079         this.maskEl.enableDisplayMode("block");
17080         if(!this.loadMask){
17081             this.maskEl.hide();
17082         }
17083         
17084         this.store = Roo.factory(this.store, Roo.data);
17085         this.store.on('load', this.onLoad, this);
17086         this.store.on('beforeload', this.onBeforeLoad, this);
17087         
17088         this.resize();
17089         
17090         this.cells = this.el.select('.fc-day',true);
17091         //Roo.log(this.cells);
17092         this.textNodes = this.el.query('.fc-day-number');
17093         this.cells.addClassOnOver('fc-state-hover');
17094         
17095         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17096         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17097         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17098         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17099         
17100         this.on('monthchange', this.onMonthChange, this);
17101         
17102         this.update(new Date().clearTime());
17103     },
17104     
17105     resize : function() {
17106         var sz  = this.el.getSize();
17107         
17108         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17109         this.el.select('.fc-day-content div',true).setHeight(34);
17110     },
17111     
17112     
17113     // private
17114     showPrevMonth : function(e){
17115         this.update(this.activeDate.add("mo", -1));
17116     },
17117     showToday : function(e){
17118         this.update(new Date().clearTime());
17119     },
17120     // private
17121     showNextMonth : function(e){
17122         this.update(this.activeDate.add("mo", 1));
17123     },
17124
17125     // private
17126     showPrevYear : function(){
17127         this.update(this.activeDate.add("y", -1));
17128     },
17129
17130     // private
17131     showNextYear : function(){
17132         this.update(this.activeDate.add("y", 1));
17133     },
17134
17135     
17136    // private
17137     update : function(date)
17138     {
17139         var vd = this.activeDate;
17140         this.activeDate = date;
17141 //        if(vd && this.el){
17142 //            var t = date.getTime();
17143 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17144 //                Roo.log('using add remove');
17145 //                
17146 //                this.fireEvent('monthchange', this, date);
17147 //                
17148 //                this.cells.removeClass("fc-state-highlight");
17149 //                this.cells.each(function(c){
17150 //                   if(c.dateValue == t){
17151 //                       c.addClass("fc-state-highlight");
17152 //                       setTimeout(function(){
17153 //                            try{c.dom.firstChild.focus();}catch(e){}
17154 //                       }, 50);
17155 //                       return false;
17156 //                   }
17157 //                   return true;
17158 //                });
17159 //                return;
17160 //            }
17161 //        }
17162         
17163         var days = date.getDaysInMonth();
17164         
17165         var firstOfMonth = date.getFirstDateOfMonth();
17166         var startingPos = firstOfMonth.getDay()-this.startDay;
17167         
17168         if(startingPos < this.startDay){
17169             startingPos += 7;
17170         }
17171         
17172         var pm = date.add(Date.MONTH, -1);
17173         var prevStart = pm.getDaysInMonth()-startingPos;
17174 //        
17175         this.cells = this.el.select('.fc-day',true);
17176         this.textNodes = this.el.query('.fc-day-number');
17177         this.cells.addClassOnOver('fc-state-hover');
17178         
17179         var cells = this.cells.elements;
17180         var textEls = this.textNodes;
17181         
17182         Roo.each(cells, function(cell){
17183             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17184         });
17185         
17186         days += startingPos;
17187
17188         // convert everything to numbers so it's fast
17189         var day = 86400000;
17190         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17191         //Roo.log(d);
17192         //Roo.log(pm);
17193         //Roo.log(prevStart);
17194         
17195         var today = new Date().clearTime().getTime();
17196         var sel = date.clearTime().getTime();
17197         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17198         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17199         var ddMatch = this.disabledDatesRE;
17200         var ddText = this.disabledDatesText;
17201         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17202         var ddaysText = this.disabledDaysText;
17203         var format = this.format;
17204         
17205         var setCellClass = function(cal, cell){
17206             cell.row = 0;
17207             cell.events = [];
17208             cell.more = [];
17209             //Roo.log('set Cell Class');
17210             cell.title = "";
17211             var t = d.getTime();
17212             
17213             //Roo.log(d);
17214             
17215             cell.dateValue = t;
17216             if(t == today){
17217                 cell.className += " fc-today";
17218                 cell.className += " fc-state-highlight";
17219                 cell.title = cal.todayText;
17220             }
17221             if(t == sel){
17222                 // disable highlight in other month..
17223                 //cell.className += " fc-state-highlight";
17224                 
17225             }
17226             // disabling
17227             if(t < min) {
17228                 cell.className = " fc-state-disabled";
17229                 cell.title = cal.minText;
17230                 return;
17231             }
17232             if(t > max) {
17233                 cell.className = " fc-state-disabled";
17234                 cell.title = cal.maxText;
17235                 return;
17236             }
17237             if(ddays){
17238                 if(ddays.indexOf(d.getDay()) != -1){
17239                     cell.title = ddaysText;
17240                     cell.className = " fc-state-disabled";
17241                 }
17242             }
17243             if(ddMatch && format){
17244                 var fvalue = d.dateFormat(format);
17245                 if(ddMatch.test(fvalue)){
17246                     cell.title = ddText.replace("%0", fvalue);
17247                     cell.className = " fc-state-disabled";
17248                 }
17249             }
17250             
17251             if (!cell.initialClassName) {
17252                 cell.initialClassName = cell.dom.className;
17253             }
17254             
17255             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17256         };
17257
17258         var i = 0;
17259         
17260         for(; i < startingPos; i++) {
17261             textEls[i].innerHTML = (++prevStart);
17262             d.setDate(d.getDate()+1);
17263             
17264             cells[i].className = "fc-past fc-other-month";
17265             setCellClass(this, cells[i]);
17266         }
17267         
17268         var intDay = 0;
17269         
17270         for(; i < days; i++){
17271             intDay = i - startingPos + 1;
17272             textEls[i].innerHTML = (intDay);
17273             d.setDate(d.getDate()+1);
17274             
17275             cells[i].className = ''; // "x-date-active";
17276             setCellClass(this, cells[i]);
17277         }
17278         var extraDays = 0;
17279         
17280         for(; i < 42; i++) {
17281             textEls[i].innerHTML = (++extraDays);
17282             d.setDate(d.getDate()+1);
17283             
17284             cells[i].className = "fc-future fc-other-month";
17285             setCellClass(this, cells[i]);
17286         }
17287         
17288         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17289         
17290         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17291         
17292         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17293         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17294         
17295         if(totalRows != 6){
17296             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17297             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17298         }
17299         
17300         this.fireEvent('monthchange', this, date);
17301         
17302         
17303         /*
17304         if(!this.internalRender){
17305             var main = this.el.dom.firstChild;
17306             var w = main.offsetWidth;
17307             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17308             Roo.fly(main).setWidth(w);
17309             this.internalRender = true;
17310             // opera does not respect the auto grow header center column
17311             // then, after it gets a width opera refuses to recalculate
17312             // without a second pass
17313             if(Roo.isOpera && !this.secondPass){
17314                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17315                 this.secondPass = true;
17316                 this.update.defer(10, this, [date]);
17317             }
17318         }
17319         */
17320         
17321     },
17322     
17323     findCell : function(dt) {
17324         dt = dt.clearTime().getTime();
17325         var ret = false;
17326         this.cells.each(function(c){
17327             //Roo.log("check " +c.dateValue + '?=' + dt);
17328             if(c.dateValue == dt){
17329                 ret = c;
17330                 return false;
17331             }
17332             return true;
17333         });
17334         
17335         return ret;
17336     },
17337     
17338     findCells : function(ev) {
17339         var s = ev.start.clone().clearTime().getTime();
17340        // Roo.log(s);
17341         var e= ev.end.clone().clearTime().getTime();
17342        // Roo.log(e);
17343         var ret = [];
17344         this.cells.each(function(c){
17345              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17346             
17347             if(c.dateValue > e){
17348                 return ;
17349             }
17350             if(c.dateValue < s){
17351                 return ;
17352             }
17353             ret.push(c);
17354         });
17355         
17356         return ret;    
17357     },
17358     
17359 //    findBestRow: function(cells)
17360 //    {
17361 //        var ret = 0;
17362 //        
17363 //        for (var i =0 ; i < cells.length;i++) {
17364 //            ret  = Math.max(cells[i].rows || 0,ret);
17365 //        }
17366 //        return ret;
17367 //        
17368 //    },
17369     
17370     
17371     addItem : function(ev)
17372     {
17373         // look for vertical location slot in
17374         var cells = this.findCells(ev);
17375         
17376 //        ev.row = this.findBestRow(cells);
17377         
17378         // work out the location.
17379         
17380         var crow = false;
17381         var rows = [];
17382         for(var i =0; i < cells.length; i++) {
17383             
17384             cells[i].row = cells[0].row;
17385             
17386             if(i == 0){
17387                 cells[i].row = cells[i].row + 1;
17388             }
17389             
17390             if (!crow) {
17391                 crow = {
17392                     start : cells[i],
17393                     end :  cells[i]
17394                 };
17395                 continue;
17396             }
17397             if (crow.start.getY() == cells[i].getY()) {
17398                 // on same row.
17399                 crow.end = cells[i];
17400                 continue;
17401             }
17402             // different row.
17403             rows.push(crow);
17404             crow = {
17405                 start: cells[i],
17406                 end : cells[i]
17407             };
17408             
17409         }
17410         
17411         rows.push(crow);
17412         ev.els = [];
17413         ev.rows = rows;
17414         ev.cells = cells;
17415         
17416         cells[0].events.push(ev);
17417         
17418         this.calevents.push(ev);
17419     },
17420     
17421     clearEvents: function() {
17422         
17423         if(!this.calevents){
17424             return;
17425         }
17426         
17427         Roo.each(this.cells.elements, function(c){
17428             c.row = 0;
17429             c.events = [];
17430             c.more = [];
17431         });
17432         
17433         Roo.each(this.calevents, function(e) {
17434             Roo.each(e.els, function(el) {
17435                 el.un('mouseenter' ,this.onEventEnter, this);
17436                 el.un('mouseleave' ,this.onEventLeave, this);
17437                 el.remove();
17438             },this);
17439         },this);
17440         
17441         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17442             e.remove();
17443         });
17444         
17445     },
17446     
17447     renderEvents: function()
17448     {   
17449         var _this = this;
17450         
17451         this.cells.each(function(c) {
17452             
17453             if(c.row < 5){
17454                 return;
17455             }
17456             
17457             var ev = c.events;
17458             
17459             var r = 4;
17460             if(c.row != c.events.length){
17461                 r = 4 - (4 - (c.row - c.events.length));
17462             }
17463             
17464             c.events = ev.slice(0, r);
17465             c.more = ev.slice(r);
17466             
17467             if(c.more.length && c.more.length == 1){
17468                 c.events.push(c.more.pop());
17469             }
17470             
17471             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17472             
17473         });
17474             
17475         this.cells.each(function(c) {
17476             
17477             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17478             
17479             
17480             for (var e = 0; e < c.events.length; e++){
17481                 var ev = c.events[e];
17482                 var rows = ev.rows;
17483                 
17484                 for(var i = 0; i < rows.length; i++) {
17485                 
17486                     // how many rows should it span..
17487
17488                     var  cfg = {
17489                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17490                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17491
17492                         unselectable : "on",
17493                         cn : [
17494                             {
17495                                 cls: 'fc-event-inner',
17496                                 cn : [
17497     //                                {
17498     //                                  tag:'span',
17499     //                                  cls: 'fc-event-time',
17500     //                                  html : cells.length > 1 ? '' : ev.time
17501     //                                },
17502                                     {
17503                                       tag:'span',
17504                                       cls: 'fc-event-title',
17505                                       html : String.format('{0}', ev.title)
17506                                     }
17507
17508
17509                                 ]
17510                             },
17511                             {
17512                                 cls: 'ui-resizable-handle ui-resizable-e',
17513                                 html : '&nbsp;&nbsp;&nbsp'
17514                             }
17515
17516                         ]
17517                     };
17518
17519                     if (i == 0) {
17520                         cfg.cls += ' fc-event-start';
17521                     }
17522                     if ((i+1) == rows.length) {
17523                         cfg.cls += ' fc-event-end';
17524                     }
17525
17526                     var ctr = _this.el.select('.fc-event-container',true).first();
17527                     var cg = ctr.createChild(cfg);
17528
17529                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17530                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17531
17532                     var r = (c.more.length) ? 1 : 0;
17533                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17534                     cg.setWidth(ebox.right - sbox.x -2);
17535
17536                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17537                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17538                     cg.on('click', _this.onEventClick, _this, ev);
17539
17540                     ev.els.push(cg);
17541                     
17542                 }
17543                 
17544             }
17545             
17546             
17547             if(c.more.length){
17548                 var  cfg = {
17549                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17550                     style : 'position: absolute',
17551                     unselectable : "on",
17552                     cn : [
17553                         {
17554                             cls: 'fc-event-inner',
17555                             cn : [
17556                                 {
17557                                   tag:'span',
17558                                   cls: 'fc-event-title',
17559                                   html : 'More'
17560                                 }
17561
17562
17563                             ]
17564                         },
17565                         {
17566                             cls: 'ui-resizable-handle ui-resizable-e',
17567                             html : '&nbsp;&nbsp;&nbsp'
17568                         }
17569
17570                     ]
17571                 };
17572
17573                 var ctr = _this.el.select('.fc-event-container',true).first();
17574                 var cg = ctr.createChild(cfg);
17575
17576                 var sbox = c.select('.fc-day-content',true).first().getBox();
17577                 var ebox = c.select('.fc-day-content',true).first().getBox();
17578                 //Roo.log(cg);
17579                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17580                 cg.setWidth(ebox.right - sbox.x -2);
17581
17582                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17583                 
17584             }
17585             
17586         });
17587         
17588         
17589         
17590     },
17591     
17592     onEventEnter: function (e, el,event,d) {
17593         this.fireEvent('evententer', this, el, event);
17594     },
17595     
17596     onEventLeave: function (e, el,event,d) {
17597         this.fireEvent('eventleave', this, el, event);
17598     },
17599     
17600     onEventClick: function (e, el,event,d) {
17601         this.fireEvent('eventclick', this, el, event);
17602     },
17603     
17604     onMonthChange: function () {
17605         this.store.load();
17606     },
17607     
17608     onMoreEventClick: function(e, el, more)
17609     {
17610         var _this = this;
17611         
17612         this.calpopover.placement = 'right';
17613         this.calpopover.setTitle('More');
17614         
17615         this.calpopover.setContent('');
17616         
17617         var ctr = this.calpopover.el.select('.popover-content', true).first();
17618         
17619         Roo.each(more, function(m){
17620             var cfg = {
17621                 cls : 'fc-event-hori fc-event-draggable',
17622                 html : m.title
17623             };
17624             var cg = ctr.createChild(cfg);
17625             
17626             cg.on('click', _this.onEventClick, _this, m);
17627         });
17628         
17629         this.calpopover.show(el);
17630         
17631         
17632     },
17633     
17634     onLoad: function () 
17635     {   
17636         this.calevents = [];
17637         var cal = this;
17638         
17639         if(this.store.getCount() > 0){
17640             this.store.data.each(function(d){
17641                cal.addItem({
17642                     id : d.data.id,
17643                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17644                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17645                     time : d.data.start_time,
17646                     title : d.data.title,
17647                     description : d.data.description,
17648                     venue : d.data.venue
17649                 });
17650             });
17651         }
17652         
17653         this.renderEvents();
17654         
17655         if(this.calevents.length && this.loadMask){
17656             this.maskEl.hide();
17657         }
17658     },
17659     
17660     onBeforeLoad: function()
17661     {
17662         this.clearEvents();
17663         if(this.loadMask){
17664             this.maskEl.show();
17665         }
17666     }
17667 });
17668
17669  
17670  /*
17671  * - LGPL
17672  *
17673  * element
17674  * 
17675  */
17676
17677 /**
17678  * @class Roo.bootstrap.Popover
17679  * @extends Roo.bootstrap.Component
17680  * Bootstrap Popover class
17681  * @cfg {String} html contents of the popover   (or false to use children..)
17682  * @cfg {String} title of popover (or false to hide)
17683  * @cfg {String} placement how it is placed
17684  * @cfg {String} trigger click || hover (or false to trigger manually)
17685  * @cfg {String} over what (parent or false to trigger manually.)
17686  * @cfg {Number} delay - delay before showing
17687  
17688  * @constructor
17689  * Create a new Popover
17690  * @param {Object} config The config object
17691  */
17692
17693 Roo.bootstrap.Popover = function(config){
17694     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17695     
17696     this.addEvents({
17697         // raw events
17698          /**
17699          * @event show
17700          * After the popover show
17701          * 
17702          * @param {Roo.bootstrap.Popover} this
17703          */
17704         "show" : true,
17705         /**
17706          * @event hide
17707          * After the popover hide
17708          * 
17709          * @param {Roo.bootstrap.Popover} this
17710          */
17711         "hide" : true
17712     });
17713 };
17714
17715 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17716     
17717     title: 'Fill in a title',
17718     html: false,
17719     
17720     placement : 'right',
17721     trigger : 'hover', // hover
17722     
17723     delay : 0,
17724     
17725     over: 'parent',
17726     
17727     can_build_overlaid : false,
17728     
17729     getChildContainer : function()
17730     {
17731         return this.el.select('.popover-content',true).first();
17732     },
17733     
17734     getAutoCreate : function(){
17735          
17736         var cfg = {
17737            cls : 'popover roo-dynamic',
17738            style: 'display:block',
17739            cn : [
17740                 {
17741                     cls : 'arrow'
17742                 },
17743                 {
17744                     cls : 'popover-inner',
17745                     cn : [
17746                         {
17747                             tag: 'h3',
17748                             cls: 'popover-title popover-header',
17749                             html : this.title
17750                         },
17751                         {
17752                             cls : 'popover-content popover-body',
17753                             html : this.html
17754                         }
17755                     ]
17756                     
17757                 }
17758            ]
17759         };
17760         
17761         return cfg;
17762     },
17763     setTitle: function(str)
17764     {
17765         this.title = str;
17766         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17767     },
17768     setContent: function(str)
17769     {
17770         this.html = str;
17771         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17772     },
17773     // as it get's added to the bottom of the page.
17774     onRender : function(ct, position)
17775     {
17776         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17777         if(!this.el){
17778             var cfg = Roo.apply({},  this.getAutoCreate());
17779             cfg.id = Roo.id();
17780             
17781             if (this.cls) {
17782                 cfg.cls += ' ' + this.cls;
17783             }
17784             if (this.style) {
17785                 cfg.style = this.style;
17786             }
17787             //Roo.log("adding to ");
17788             this.el = Roo.get(document.body).createChild(cfg, position);
17789 //            Roo.log(this.el);
17790         }
17791         this.initEvents();
17792     },
17793     
17794     initEvents : function()
17795     {
17796         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17797         this.el.enableDisplayMode('block');
17798         this.el.hide();
17799         if (this.over === false) {
17800             return; 
17801         }
17802         if (this.triggers === false) {
17803             return;
17804         }
17805         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17806         var triggers = this.trigger ? this.trigger.split(' ') : [];
17807         Roo.each(triggers, function(trigger) {
17808         
17809             if (trigger == 'click') {
17810                 on_el.on('click', this.toggle, this);
17811             } else if (trigger != 'manual') {
17812                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17813                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17814       
17815                 on_el.on(eventIn  ,this.enter, this);
17816                 on_el.on(eventOut, this.leave, this);
17817             }
17818         }, this);
17819         
17820     },
17821     
17822     
17823     // private
17824     timeout : null,
17825     hoverState : null,
17826     
17827     toggle : function () {
17828         this.hoverState == 'in' ? this.leave() : this.enter();
17829     },
17830     
17831     enter : function () {
17832         
17833         clearTimeout(this.timeout);
17834     
17835         this.hoverState = 'in';
17836     
17837         if (!this.delay || !this.delay.show) {
17838             this.show();
17839             return;
17840         }
17841         var _t = this;
17842         this.timeout = setTimeout(function () {
17843             if (_t.hoverState == 'in') {
17844                 _t.show();
17845             }
17846         }, this.delay.show)
17847     },
17848     
17849     leave : function() {
17850         clearTimeout(this.timeout);
17851     
17852         this.hoverState = 'out';
17853     
17854         if (!this.delay || !this.delay.hide) {
17855             this.hide();
17856             return;
17857         }
17858         var _t = this;
17859         this.timeout = setTimeout(function () {
17860             if (_t.hoverState == 'out') {
17861                 _t.hide();
17862             }
17863         }, this.delay.hide)
17864     },
17865     
17866     show : function (on_el)
17867     {
17868         if (!on_el) {
17869             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17870         }
17871         
17872         // set content.
17873         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17874         if (this.html !== false) {
17875             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17876         }
17877         this.el.removeClass([
17878             'fade','top','bottom', 'left', 'right','in',
17879             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17880         ]);
17881         if (!this.title.length) {
17882             this.el.select('.popover-title',true).hide();
17883         }
17884         
17885         var placement = typeof this.placement == 'function' ?
17886             this.placement.call(this, this.el, on_el) :
17887             this.placement;
17888             
17889         var autoToken = /\s?auto?\s?/i;
17890         var autoPlace = autoToken.test(placement);
17891         if (autoPlace) {
17892             placement = placement.replace(autoToken, '') || 'top';
17893         }
17894         
17895         //this.el.detach()
17896         //this.el.setXY([0,0]);
17897         this.el.show();
17898         this.el.dom.style.display='block';
17899         this.el.addClass(placement);
17900         
17901         //this.el.appendTo(on_el);
17902         
17903         var p = this.getPosition();
17904         var box = this.el.getBox();
17905         
17906         if (autoPlace) {
17907             // fixme..
17908         }
17909         var align = Roo.bootstrap.Popover.alignment[placement];
17910         
17911 //        Roo.log(align);
17912         this.el.alignTo(on_el, align[0],align[1]);
17913         //var arrow = this.el.select('.arrow',true).first();
17914         //arrow.set(align[2], 
17915         
17916         this.el.addClass('in');
17917         
17918         
17919         if (this.el.hasClass('fade')) {
17920             // fade it?
17921         }
17922         
17923         this.hoverState = 'in';
17924         
17925         this.fireEvent('show', this);
17926         
17927     },
17928     hide : function()
17929     {
17930         this.el.setXY([0,0]);
17931         this.el.removeClass('in');
17932         this.el.hide();
17933         this.hoverState = null;
17934         
17935         this.fireEvent('hide', this);
17936     }
17937     
17938 });
17939
17940 Roo.bootstrap.Popover.alignment = {
17941     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17942     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17943     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17944     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17945 };
17946
17947  /*
17948  * - LGPL
17949  *
17950  * Progress
17951  * 
17952  */
17953
17954 /**
17955  * @class Roo.bootstrap.Progress
17956  * @extends Roo.bootstrap.Component
17957  * Bootstrap Progress class
17958  * @cfg {Boolean} striped striped of the progress bar
17959  * @cfg {Boolean} active animated of the progress bar
17960  * 
17961  * 
17962  * @constructor
17963  * Create a new Progress
17964  * @param {Object} config The config object
17965  */
17966
17967 Roo.bootstrap.Progress = function(config){
17968     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17969 };
17970
17971 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17972     
17973     striped : false,
17974     active: false,
17975     
17976     getAutoCreate : function(){
17977         var cfg = {
17978             tag: 'div',
17979             cls: 'progress'
17980         };
17981         
17982         
17983         if(this.striped){
17984             cfg.cls += ' progress-striped';
17985         }
17986       
17987         if(this.active){
17988             cfg.cls += ' active';
17989         }
17990         
17991         
17992         return cfg;
17993     }
17994    
17995 });
17996
17997  
17998
17999  /*
18000  * - LGPL
18001  *
18002  * ProgressBar
18003  * 
18004  */
18005
18006 /**
18007  * @class Roo.bootstrap.ProgressBar
18008  * @extends Roo.bootstrap.Component
18009  * Bootstrap ProgressBar class
18010  * @cfg {Number} aria_valuenow aria-value now
18011  * @cfg {Number} aria_valuemin aria-value min
18012  * @cfg {Number} aria_valuemax aria-value max
18013  * @cfg {String} label label for the progress bar
18014  * @cfg {String} panel (success | info | warning | danger )
18015  * @cfg {String} role role of the progress bar
18016  * @cfg {String} sr_only text
18017  * 
18018  * 
18019  * @constructor
18020  * Create a new ProgressBar
18021  * @param {Object} config The config object
18022  */
18023
18024 Roo.bootstrap.ProgressBar = function(config){
18025     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18026 };
18027
18028 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18029     
18030     aria_valuenow : 0,
18031     aria_valuemin : 0,
18032     aria_valuemax : 100,
18033     label : false,
18034     panel : false,
18035     role : false,
18036     sr_only: false,
18037     
18038     getAutoCreate : function()
18039     {
18040         
18041         var cfg = {
18042             tag: 'div',
18043             cls: 'progress-bar',
18044             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18045         };
18046         
18047         if(this.sr_only){
18048             cfg.cn = {
18049                 tag: 'span',
18050                 cls: 'sr-only',
18051                 html: this.sr_only
18052             }
18053         }
18054         
18055         if(this.role){
18056             cfg.role = this.role;
18057         }
18058         
18059         if(this.aria_valuenow){
18060             cfg['aria-valuenow'] = this.aria_valuenow;
18061         }
18062         
18063         if(this.aria_valuemin){
18064             cfg['aria-valuemin'] = this.aria_valuemin;
18065         }
18066         
18067         if(this.aria_valuemax){
18068             cfg['aria-valuemax'] = this.aria_valuemax;
18069         }
18070         
18071         if(this.label && !this.sr_only){
18072             cfg.html = this.label;
18073         }
18074         
18075         if(this.panel){
18076             cfg.cls += ' progress-bar-' + this.panel;
18077         }
18078         
18079         return cfg;
18080     },
18081     
18082     update : function(aria_valuenow)
18083     {
18084         this.aria_valuenow = aria_valuenow;
18085         
18086         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18087     }
18088    
18089 });
18090
18091  
18092
18093  /*
18094  * - LGPL
18095  *
18096  * column
18097  * 
18098  */
18099
18100 /**
18101  * @class Roo.bootstrap.TabGroup
18102  * @extends Roo.bootstrap.Column
18103  * Bootstrap Column class
18104  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18105  * @cfg {Boolean} carousel true to make the group behave like a carousel
18106  * @cfg {Boolean} bullets show bullets for the panels
18107  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18108  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18109  * @cfg {Boolean} showarrow (true|false) show arrow default true
18110  * 
18111  * @constructor
18112  * Create a new TabGroup
18113  * @param {Object} config The config object
18114  */
18115
18116 Roo.bootstrap.TabGroup = function(config){
18117     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18118     if (!this.navId) {
18119         this.navId = Roo.id();
18120     }
18121     this.tabs = [];
18122     Roo.bootstrap.TabGroup.register(this);
18123     
18124 };
18125
18126 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18127     
18128     carousel : false,
18129     transition : false,
18130     bullets : 0,
18131     timer : 0,
18132     autoslide : false,
18133     slideFn : false,
18134     slideOnTouch : false,
18135     showarrow : true,
18136     
18137     getAutoCreate : function()
18138     {
18139         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18140         
18141         cfg.cls += ' tab-content';
18142         
18143         if (this.carousel) {
18144             cfg.cls += ' carousel slide';
18145             
18146             cfg.cn = [{
18147                cls : 'carousel-inner',
18148                cn : []
18149             }];
18150         
18151             if(this.bullets  && !Roo.isTouch){
18152                 
18153                 var bullets = {
18154                     cls : 'carousel-bullets',
18155                     cn : []
18156                 };
18157                
18158                 if(this.bullets_cls){
18159                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18160                 }
18161                 
18162                 bullets.cn.push({
18163                     cls : 'clear'
18164                 });
18165                 
18166                 cfg.cn[0].cn.push(bullets);
18167             }
18168             
18169             if(this.showarrow){
18170                 cfg.cn[0].cn.push({
18171                     tag : 'div',
18172                     class : 'carousel-arrow',
18173                     cn : [
18174                         {
18175                             tag : 'div',
18176                             class : 'carousel-prev',
18177                             cn : [
18178                                 {
18179                                     tag : 'i',
18180                                     class : 'fa fa-chevron-left'
18181                                 }
18182                             ]
18183                         },
18184                         {
18185                             tag : 'div',
18186                             class : 'carousel-next',
18187                             cn : [
18188                                 {
18189                                     tag : 'i',
18190                                     class : 'fa fa-chevron-right'
18191                                 }
18192                             ]
18193                         }
18194                     ]
18195                 });
18196             }
18197             
18198         }
18199         
18200         return cfg;
18201     },
18202     
18203     initEvents:  function()
18204     {
18205 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18206 //            this.el.on("touchstart", this.onTouchStart, this);
18207 //        }
18208         
18209         if(this.autoslide){
18210             var _this = this;
18211             
18212             this.slideFn = window.setInterval(function() {
18213                 _this.showPanelNext();
18214             }, this.timer);
18215         }
18216         
18217         if(this.showarrow){
18218             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18219             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18220         }
18221         
18222         
18223     },
18224     
18225 //    onTouchStart : function(e, el, o)
18226 //    {
18227 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18228 //            return;
18229 //        }
18230 //        
18231 //        this.showPanelNext();
18232 //    },
18233     
18234     
18235     getChildContainer : function()
18236     {
18237         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18238     },
18239     
18240     /**
18241     * register a Navigation item
18242     * @param {Roo.bootstrap.NavItem} the navitem to add
18243     */
18244     register : function(item)
18245     {
18246         this.tabs.push( item);
18247         item.navId = this.navId; // not really needed..
18248         this.addBullet();
18249     
18250     },
18251     
18252     getActivePanel : function()
18253     {
18254         var r = false;
18255         Roo.each(this.tabs, function(t) {
18256             if (t.active) {
18257                 r = t;
18258                 return false;
18259             }
18260             return null;
18261         });
18262         return r;
18263         
18264     },
18265     getPanelByName : function(n)
18266     {
18267         var r = false;
18268         Roo.each(this.tabs, function(t) {
18269             if (t.tabId == n) {
18270                 r = t;
18271                 return false;
18272             }
18273             return null;
18274         });
18275         return r;
18276     },
18277     indexOfPanel : function(p)
18278     {
18279         var r = false;
18280         Roo.each(this.tabs, function(t,i) {
18281             if (t.tabId == p.tabId) {
18282                 r = i;
18283                 return false;
18284             }
18285             return null;
18286         });
18287         return r;
18288     },
18289     /**
18290      * show a specific panel
18291      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18292      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18293      */
18294     showPanel : function (pan)
18295     {
18296         if(this.transition || typeof(pan) == 'undefined'){
18297             Roo.log("waiting for the transitionend");
18298             return;
18299         }
18300         
18301         if (typeof(pan) == 'number') {
18302             pan = this.tabs[pan];
18303         }
18304         
18305         if (typeof(pan) == 'string') {
18306             pan = this.getPanelByName(pan);
18307         }
18308         
18309         var cur = this.getActivePanel();
18310         
18311         if(!pan || !cur){
18312             Roo.log('pan or acitve pan is undefined');
18313             return false;
18314         }
18315         
18316         if (pan.tabId == this.getActivePanel().tabId) {
18317             return true;
18318         }
18319         
18320         if (false === cur.fireEvent('beforedeactivate')) {
18321             return false;
18322         }
18323         
18324         if(this.bullets > 0 && !Roo.isTouch){
18325             this.setActiveBullet(this.indexOfPanel(pan));
18326         }
18327         
18328         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18329             
18330             this.transition = true;
18331             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18332             var lr = dir == 'next' ? 'left' : 'right';
18333             pan.el.addClass(dir); // or prev
18334             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18335             cur.el.addClass(lr); // or right
18336             pan.el.addClass(lr);
18337             
18338             var _this = this;
18339             cur.el.on('transitionend', function() {
18340                 Roo.log("trans end?");
18341                 
18342                 pan.el.removeClass([lr,dir]);
18343                 pan.setActive(true);
18344                 
18345                 cur.el.removeClass([lr]);
18346                 cur.setActive(false);
18347                 
18348                 _this.transition = false;
18349                 
18350             }, this, { single:  true } );
18351             
18352             return true;
18353         }
18354         
18355         cur.setActive(false);
18356         pan.setActive(true);
18357         
18358         return true;
18359         
18360     },
18361     showPanelNext : function()
18362     {
18363         var i = this.indexOfPanel(this.getActivePanel());
18364         
18365         if (i >= this.tabs.length - 1 && !this.autoslide) {
18366             return;
18367         }
18368         
18369         if (i >= this.tabs.length - 1 && this.autoslide) {
18370             i = -1;
18371         }
18372         
18373         this.showPanel(this.tabs[i+1]);
18374     },
18375     
18376     showPanelPrev : function()
18377     {
18378         var i = this.indexOfPanel(this.getActivePanel());
18379         
18380         if (i  < 1 && !this.autoslide) {
18381             return;
18382         }
18383         
18384         if (i < 1 && this.autoslide) {
18385             i = this.tabs.length;
18386         }
18387         
18388         this.showPanel(this.tabs[i-1]);
18389     },
18390     
18391     
18392     addBullet: function()
18393     {
18394         if(!this.bullets || Roo.isTouch){
18395             return;
18396         }
18397         var ctr = this.el.select('.carousel-bullets',true).first();
18398         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18399         var bullet = ctr.createChild({
18400             cls : 'bullet bullet-' + i
18401         },ctr.dom.lastChild);
18402         
18403         
18404         var _this = this;
18405         
18406         bullet.on('click', (function(e, el, o, ii, t){
18407
18408             e.preventDefault();
18409
18410             this.showPanel(ii);
18411
18412             if(this.autoslide && this.slideFn){
18413                 clearInterval(this.slideFn);
18414                 this.slideFn = window.setInterval(function() {
18415                     _this.showPanelNext();
18416                 }, this.timer);
18417             }
18418
18419         }).createDelegate(this, [i, bullet], true));
18420                 
18421         
18422     },
18423      
18424     setActiveBullet : function(i)
18425     {
18426         if(Roo.isTouch){
18427             return;
18428         }
18429         
18430         Roo.each(this.el.select('.bullet', true).elements, function(el){
18431             el.removeClass('selected');
18432         });
18433
18434         var bullet = this.el.select('.bullet-' + i, true).first();
18435         
18436         if(!bullet){
18437             return;
18438         }
18439         
18440         bullet.addClass('selected');
18441     }
18442     
18443     
18444   
18445 });
18446
18447  
18448
18449  
18450  
18451 Roo.apply(Roo.bootstrap.TabGroup, {
18452     
18453     groups: {},
18454      /**
18455     * register a Navigation Group
18456     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18457     */
18458     register : function(navgrp)
18459     {
18460         this.groups[navgrp.navId] = navgrp;
18461         
18462     },
18463     /**
18464     * fetch a Navigation Group based on the navigation ID
18465     * if one does not exist , it will get created.
18466     * @param {string} the navgroup to add
18467     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18468     */
18469     get: function(navId) {
18470         if (typeof(this.groups[navId]) == 'undefined') {
18471             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18472         }
18473         return this.groups[navId] ;
18474     }
18475     
18476     
18477     
18478 });
18479
18480  /*
18481  * - LGPL
18482  *
18483  * TabPanel
18484  * 
18485  */
18486
18487 /**
18488  * @class Roo.bootstrap.TabPanel
18489  * @extends Roo.bootstrap.Component
18490  * Bootstrap TabPanel class
18491  * @cfg {Boolean} active panel active
18492  * @cfg {String} html panel content
18493  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18494  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18495  * @cfg {String} href click to link..
18496  * 
18497  * 
18498  * @constructor
18499  * Create a new TabPanel
18500  * @param {Object} config The config object
18501  */
18502
18503 Roo.bootstrap.TabPanel = function(config){
18504     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18505     this.addEvents({
18506         /**
18507              * @event changed
18508              * Fires when the active status changes
18509              * @param {Roo.bootstrap.TabPanel} this
18510              * @param {Boolean} state the new state
18511             
18512          */
18513         'changed': true,
18514         /**
18515              * @event beforedeactivate
18516              * Fires before a tab is de-activated - can be used to do validation on a form.
18517              * @param {Roo.bootstrap.TabPanel} this
18518              * @return {Boolean} false if there is an error
18519             
18520          */
18521         'beforedeactivate': true
18522      });
18523     
18524     this.tabId = this.tabId || Roo.id();
18525   
18526 };
18527
18528 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18529     
18530     active: false,
18531     html: false,
18532     tabId: false,
18533     navId : false,
18534     href : '',
18535     
18536     getAutoCreate : function(){
18537         var cfg = {
18538             tag: 'div',
18539             // item is needed for carousel - not sure if it has any effect otherwise
18540             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18541             html: this.html || ''
18542         };
18543         
18544         if(this.active){
18545             cfg.cls += ' active';
18546         }
18547         
18548         if(this.tabId){
18549             cfg.tabId = this.tabId;
18550         }
18551         
18552         
18553         return cfg;
18554     },
18555     
18556     initEvents:  function()
18557     {
18558         var p = this.parent();
18559         
18560         this.navId = this.navId || p.navId;
18561         
18562         if (typeof(this.navId) != 'undefined') {
18563             // not really needed.. but just in case.. parent should be a NavGroup.
18564             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18565             
18566             tg.register(this);
18567             
18568             var i = tg.tabs.length - 1;
18569             
18570             if(this.active && tg.bullets > 0 && i < tg.bullets){
18571                 tg.setActiveBullet(i);
18572             }
18573         }
18574         
18575         this.el.on('click', this.onClick, this);
18576         
18577         if(Roo.isTouch){
18578             this.el.on("touchstart", this.onTouchStart, this);
18579             this.el.on("touchmove", this.onTouchMove, this);
18580             this.el.on("touchend", this.onTouchEnd, this);
18581         }
18582         
18583     },
18584     
18585     onRender : function(ct, position)
18586     {
18587         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18588     },
18589     
18590     setActive : function(state)
18591     {
18592         Roo.log("panel - set active " + this.tabId + "=" + state);
18593         
18594         this.active = state;
18595         if (!state) {
18596             this.el.removeClass('active');
18597             
18598         } else  if (!this.el.hasClass('active')) {
18599             this.el.addClass('active');
18600         }
18601         
18602         this.fireEvent('changed', this, state);
18603     },
18604     
18605     onClick : function(e)
18606     {
18607         e.preventDefault();
18608         
18609         if(!this.href.length){
18610             return;
18611         }
18612         
18613         window.location.href = this.href;
18614     },
18615     
18616     startX : 0,
18617     startY : 0,
18618     endX : 0,
18619     endY : 0,
18620     swiping : false,
18621     
18622     onTouchStart : function(e)
18623     {
18624         this.swiping = false;
18625         
18626         this.startX = e.browserEvent.touches[0].clientX;
18627         this.startY = e.browserEvent.touches[0].clientY;
18628     },
18629     
18630     onTouchMove : function(e)
18631     {
18632         this.swiping = true;
18633         
18634         this.endX = e.browserEvent.touches[0].clientX;
18635         this.endY = e.browserEvent.touches[0].clientY;
18636     },
18637     
18638     onTouchEnd : function(e)
18639     {
18640         if(!this.swiping){
18641             this.onClick(e);
18642             return;
18643         }
18644         
18645         var tabGroup = this.parent();
18646         
18647         if(this.endX > this.startX){ // swiping right
18648             tabGroup.showPanelPrev();
18649             return;
18650         }
18651         
18652         if(this.startX > this.endX){ // swiping left
18653             tabGroup.showPanelNext();
18654             return;
18655         }
18656     }
18657     
18658     
18659 });
18660  
18661
18662  
18663
18664  /*
18665  * - LGPL
18666  *
18667  * DateField
18668  * 
18669  */
18670
18671 /**
18672  * @class Roo.bootstrap.DateField
18673  * @extends Roo.bootstrap.Input
18674  * Bootstrap DateField class
18675  * @cfg {Number} weekStart default 0
18676  * @cfg {String} viewMode default empty, (months|years)
18677  * @cfg {String} minViewMode default empty, (months|years)
18678  * @cfg {Number} startDate default -Infinity
18679  * @cfg {Number} endDate default Infinity
18680  * @cfg {Boolean} todayHighlight default false
18681  * @cfg {Boolean} todayBtn default false
18682  * @cfg {Boolean} calendarWeeks default false
18683  * @cfg {Object} daysOfWeekDisabled default empty
18684  * @cfg {Boolean} singleMode default false (true | false)
18685  * 
18686  * @cfg {Boolean} keyboardNavigation default true
18687  * @cfg {String} language default en
18688  * 
18689  * @constructor
18690  * Create a new DateField
18691  * @param {Object} config The config object
18692  */
18693
18694 Roo.bootstrap.DateField = function(config){
18695     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18696      this.addEvents({
18697             /**
18698              * @event show
18699              * Fires when this field show.
18700              * @param {Roo.bootstrap.DateField} this
18701              * @param {Mixed} date The date value
18702              */
18703             show : true,
18704             /**
18705              * @event show
18706              * Fires when this field hide.
18707              * @param {Roo.bootstrap.DateField} this
18708              * @param {Mixed} date The date value
18709              */
18710             hide : true,
18711             /**
18712              * @event select
18713              * Fires when select a date.
18714              * @param {Roo.bootstrap.DateField} this
18715              * @param {Mixed} date The date value
18716              */
18717             select : true,
18718             /**
18719              * @event beforeselect
18720              * Fires when before select a date.
18721              * @param {Roo.bootstrap.DateField} this
18722              * @param {Mixed} date The date value
18723              */
18724             beforeselect : true
18725         });
18726 };
18727
18728 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18729     
18730     /**
18731      * @cfg {String} format
18732      * The default date format string which can be overriden for localization support.  The format must be
18733      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18734      */
18735     format : "m/d/y",
18736     /**
18737      * @cfg {String} altFormats
18738      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18739      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18740      */
18741     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18742     
18743     weekStart : 0,
18744     
18745     viewMode : '',
18746     
18747     minViewMode : '',
18748     
18749     todayHighlight : false,
18750     
18751     todayBtn: false,
18752     
18753     language: 'en',
18754     
18755     keyboardNavigation: true,
18756     
18757     calendarWeeks: false,
18758     
18759     startDate: -Infinity,
18760     
18761     endDate: Infinity,
18762     
18763     daysOfWeekDisabled: [],
18764     
18765     _events: [],
18766     
18767     singleMode : false,
18768     
18769     UTCDate: function()
18770     {
18771         return new Date(Date.UTC.apply(Date, arguments));
18772     },
18773     
18774     UTCToday: function()
18775     {
18776         var today = new Date();
18777         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18778     },
18779     
18780     getDate: function() {
18781             var d = this.getUTCDate();
18782             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18783     },
18784     
18785     getUTCDate: function() {
18786             return this.date;
18787     },
18788     
18789     setDate: function(d) {
18790             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18791     },
18792     
18793     setUTCDate: function(d) {
18794             this.date = d;
18795             this.setValue(this.formatDate(this.date));
18796     },
18797         
18798     onRender: function(ct, position)
18799     {
18800         
18801         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18802         
18803         this.language = this.language || 'en';
18804         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18805         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18806         
18807         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18808         this.format = this.format || 'm/d/y';
18809         this.isInline = false;
18810         this.isInput = true;
18811         this.component = this.el.select('.add-on', true).first() || false;
18812         this.component = (this.component && this.component.length === 0) ? false : this.component;
18813         this.hasInput = this.component && this.inputEl().length;
18814         
18815         if (typeof(this.minViewMode === 'string')) {
18816             switch (this.minViewMode) {
18817                 case 'months':
18818                     this.minViewMode = 1;
18819                     break;
18820                 case 'years':
18821                     this.minViewMode = 2;
18822                     break;
18823                 default:
18824                     this.minViewMode = 0;
18825                     break;
18826             }
18827         }
18828         
18829         if (typeof(this.viewMode === 'string')) {
18830             switch (this.viewMode) {
18831                 case 'months':
18832                     this.viewMode = 1;
18833                     break;
18834                 case 'years':
18835                     this.viewMode = 2;
18836                     break;
18837                 default:
18838                     this.viewMode = 0;
18839                     break;
18840             }
18841         }
18842                 
18843         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18844         
18845 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18846         
18847         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18848         
18849         this.picker().on('mousedown', this.onMousedown, this);
18850         this.picker().on('click', this.onClick, this);
18851         
18852         this.picker().addClass('datepicker-dropdown');
18853         
18854         this.startViewMode = this.viewMode;
18855         
18856         if(this.singleMode){
18857             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18858                 v.setVisibilityMode(Roo.Element.DISPLAY);
18859                 v.hide();
18860             });
18861             
18862             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18863                 v.setStyle('width', '189px');
18864             });
18865         }
18866         
18867         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18868             if(!this.calendarWeeks){
18869                 v.remove();
18870                 return;
18871             }
18872             
18873             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18874             v.attr('colspan', function(i, val){
18875                 return parseInt(val) + 1;
18876             });
18877         });
18878                         
18879         
18880         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18881         
18882         this.setStartDate(this.startDate);
18883         this.setEndDate(this.endDate);
18884         
18885         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18886         
18887         this.fillDow();
18888         this.fillMonths();
18889         this.update();
18890         this.showMode();
18891         
18892         if(this.isInline) {
18893             this.showPopup();
18894         }
18895     },
18896     
18897     picker : function()
18898     {
18899         return this.pickerEl;
18900 //        return this.el.select('.datepicker', true).first();
18901     },
18902     
18903     fillDow: function()
18904     {
18905         var dowCnt = this.weekStart;
18906         
18907         var dow = {
18908             tag: 'tr',
18909             cn: [
18910                 
18911             ]
18912         };
18913         
18914         if(this.calendarWeeks){
18915             dow.cn.push({
18916                 tag: 'th',
18917                 cls: 'cw',
18918                 html: '&nbsp;'
18919             })
18920         }
18921         
18922         while (dowCnt < this.weekStart + 7) {
18923             dow.cn.push({
18924                 tag: 'th',
18925                 cls: 'dow',
18926                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18927             });
18928         }
18929         
18930         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18931     },
18932     
18933     fillMonths: function()
18934     {    
18935         var i = 0;
18936         var months = this.picker().select('>.datepicker-months td', true).first();
18937         
18938         months.dom.innerHTML = '';
18939         
18940         while (i < 12) {
18941             var month = {
18942                 tag: 'span',
18943                 cls: 'month',
18944                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18945             };
18946             
18947             months.createChild(month);
18948         }
18949         
18950     },
18951     
18952     update: function()
18953     {
18954         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;
18955         
18956         if (this.date < this.startDate) {
18957             this.viewDate = new Date(this.startDate);
18958         } else if (this.date > this.endDate) {
18959             this.viewDate = new Date(this.endDate);
18960         } else {
18961             this.viewDate = new Date(this.date);
18962         }
18963         
18964         this.fill();
18965     },
18966     
18967     fill: function() 
18968     {
18969         var d = new Date(this.viewDate),
18970                 year = d.getUTCFullYear(),
18971                 month = d.getUTCMonth(),
18972                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18973                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18974                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18975                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18976                 currentDate = this.date && this.date.valueOf(),
18977                 today = this.UTCToday();
18978         
18979         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18980         
18981 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18982         
18983 //        this.picker.select('>tfoot th.today').
18984 //                                              .text(dates[this.language].today)
18985 //                                              .toggle(this.todayBtn !== false);
18986     
18987         this.updateNavArrows();
18988         this.fillMonths();
18989                                                 
18990         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18991         
18992         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18993          
18994         prevMonth.setUTCDate(day);
18995         
18996         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18997         
18998         var nextMonth = new Date(prevMonth);
18999         
19000         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19001         
19002         nextMonth = nextMonth.valueOf();
19003         
19004         var fillMonths = false;
19005         
19006         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19007         
19008         while(prevMonth.valueOf() <= nextMonth) {
19009             var clsName = '';
19010             
19011             if (prevMonth.getUTCDay() === this.weekStart) {
19012                 if(fillMonths){
19013                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19014                 }
19015                     
19016                 fillMonths = {
19017                     tag: 'tr',
19018                     cn: []
19019                 };
19020                 
19021                 if(this.calendarWeeks){
19022                     // ISO 8601: First week contains first thursday.
19023                     // ISO also states week starts on Monday, but we can be more abstract here.
19024                     var
19025                     // Start of current week: based on weekstart/current date
19026                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19027                     // Thursday of this week
19028                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19029                     // First Thursday of year, year from thursday
19030                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19031                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19032                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19033                     
19034                     fillMonths.cn.push({
19035                         tag: 'td',
19036                         cls: 'cw',
19037                         html: calWeek
19038                     });
19039                 }
19040             }
19041             
19042             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19043                 clsName += ' old';
19044             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19045                 clsName += ' new';
19046             }
19047             if (this.todayHighlight &&
19048                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19049                 prevMonth.getUTCMonth() == today.getMonth() &&
19050                 prevMonth.getUTCDate() == today.getDate()) {
19051                 clsName += ' today';
19052             }
19053             
19054             if (currentDate && prevMonth.valueOf() === currentDate) {
19055                 clsName += ' active';
19056             }
19057             
19058             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19059                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19060                     clsName += ' disabled';
19061             }
19062             
19063             fillMonths.cn.push({
19064                 tag: 'td',
19065                 cls: 'day ' + clsName,
19066                 html: prevMonth.getDate()
19067             });
19068             
19069             prevMonth.setDate(prevMonth.getDate()+1);
19070         }
19071           
19072         var currentYear = this.date && this.date.getUTCFullYear();
19073         var currentMonth = this.date && this.date.getUTCMonth();
19074         
19075         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19076         
19077         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19078             v.removeClass('active');
19079             
19080             if(currentYear === year && k === currentMonth){
19081                 v.addClass('active');
19082             }
19083             
19084             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19085                 v.addClass('disabled');
19086             }
19087             
19088         });
19089         
19090         
19091         year = parseInt(year/10, 10) * 10;
19092         
19093         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19094         
19095         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19096         
19097         year -= 1;
19098         for (var i = -1; i < 11; i++) {
19099             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19100                 tag: 'span',
19101                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19102                 html: year
19103             });
19104             
19105             year += 1;
19106         }
19107     },
19108     
19109     showMode: function(dir) 
19110     {
19111         if (dir) {
19112             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19113         }
19114         
19115         Roo.each(this.picker().select('>div',true).elements, function(v){
19116             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19117             v.hide();
19118         });
19119         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19120     },
19121     
19122     place: function()
19123     {
19124         if(this.isInline) {
19125             return;
19126         }
19127         
19128         this.picker().removeClass(['bottom', 'top']);
19129         
19130         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19131             /*
19132              * place to the top of element!
19133              *
19134              */
19135             
19136             this.picker().addClass('top');
19137             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19138             
19139             return;
19140         }
19141         
19142         this.picker().addClass('bottom');
19143         
19144         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19145     },
19146     
19147     parseDate : function(value)
19148     {
19149         if(!value || value instanceof Date){
19150             return value;
19151         }
19152         var v = Date.parseDate(value, this.format);
19153         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19154             v = Date.parseDate(value, 'Y-m-d');
19155         }
19156         if(!v && this.altFormats){
19157             if(!this.altFormatsArray){
19158                 this.altFormatsArray = this.altFormats.split("|");
19159             }
19160             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19161                 v = Date.parseDate(value, this.altFormatsArray[i]);
19162             }
19163         }
19164         return v;
19165     },
19166     
19167     formatDate : function(date, fmt)
19168     {   
19169         return (!date || !(date instanceof Date)) ?
19170         date : date.dateFormat(fmt || this.format);
19171     },
19172     
19173     onFocus : function()
19174     {
19175         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19176         this.showPopup();
19177     },
19178     
19179     onBlur : function()
19180     {
19181         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19182         
19183         var d = this.inputEl().getValue();
19184         
19185         this.setValue(d);
19186                 
19187         this.hidePopup();
19188     },
19189     
19190     showPopup : function()
19191     {
19192         this.picker().show();
19193         this.update();
19194         this.place();
19195         
19196         this.fireEvent('showpopup', this, this.date);
19197     },
19198     
19199     hidePopup : function()
19200     {
19201         if(this.isInline) {
19202             return;
19203         }
19204         this.picker().hide();
19205         this.viewMode = this.startViewMode;
19206         this.showMode();
19207         
19208         this.fireEvent('hidepopup', this, this.date);
19209         
19210     },
19211     
19212     onMousedown: function(e)
19213     {
19214         e.stopPropagation();
19215         e.preventDefault();
19216     },
19217     
19218     keyup: function(e)
19219     {
19220         Roo.bootstrap.DateField.superclass.keyup.call(this);
19221         this.update();
19222     },
19223
19224     setValue: function(v)
19225     {
19226         if(this.fireEvent('beforeselect', this, v) !== false){
19227             var d = new Date(this.parseDate(v) ).clearTime();
19228         
19229             if(isNaN(d.getTime())){
19230                 this.date = this.viewDate = '';
19231                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19232                 return;
19233             }
19234
19235             v = this.formatDate(d);
19236
19237             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19238
19239             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19240
19241             this.update();
19242
19243             this.fireEvent('select', this, this.date);
19244         }
19245     },
19246     
19247     getValue: function()
19248     {
19249         return this.formatDate(this.date);
19250     },
19251     
19252     fireKey: function(e)
19253     {
19254         if (!this.picker().isVisible()){
19255             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19256                 this.showPopup();
19257             }
19258             return;
19259         }
19260         
19261         var dateChanged = false,
19262         dir, day, month,
19263         newDate, newViewDate;
19264         
19265         switch(e.keyCode){
19266             case 27: // escape
19267                 this.hidePopup();
19268                 e.preventDefault();
19269                 break;
19270             case 37: // left
19271             case 39: // right
19272                 if (!this.keyboardNavigation) {
19273                     break;
19274                 }
19275                 dir = e.keyCode == 37 ? -1 : 1;
19276                 
19277                 if (e.ctrlKey){
19278                     newDate = this.moveYear(this.date, dir);
19279                     newViewDate = this.moveYear(this.viewDate, dir);
19280                 } else if (e.shiftKey){
19281                     newDate = this.moveMonth(this.date, dir);
19282                     newViewDate = this.moveMonth(this.viewDate, dir);
19283                 } else {
19284                     newDate = new Date(this.date);
19285                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19286                     newViewDate = new Date(this.viewDate);
19287                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19288                 }
19289                 if (this.dateWithinRange(newDate)){
19290                     this.date = newDate;
19291                     this.viewDate = newViewDate;
19292                     this.setValue(this.formatDate(this.date));
19293 //                    this.update();
19294                     e.preventDefault();
19295                     dateChanged = true;
19296                 }
19297                 break;
19298             case 38: // up
19299             case 40: // down
19300                 if (!this.keyboardNavigation) {
19301                     break;
19302                 }
19303                 dir = e.keyCode == 38 ? -1 : 1;
19304                 if (e.ctrlKey){
19305                     newDate = this.moveYear(this.date, dir);
19306                     newViewDate = this.moveYear(this.viewDate, dir);
19307                 } else if (e.shiftKey){
19308                     newDate = this.moveMonth(this.date, dir);
19309                     newViewDate = this.moveMonth(this.viewDate, dir);
19310                 } else {
19311                     newDate = new Date(this.date);
19312                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19313                     newViewDate = new Date(this.viewDate);
19314                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19315                 }
19316                 if (this.dateWithinRange(newDate)){
19317                     this.date = newDate;
19318                     this.viewDate = newViewDate;
19319                     this.setValue(this.formatDate(this.date));
19320 //                    this.update();
19321                     e.preventDefault();
19322                     dateChanged = true;
19323                 }
19324                 break;
19325             case 13: // enter
19326                 this.setValue(this.formatDate(this.date));
19327                 this.hidePopup();
19328                 e.preventDefault();
19329                 break;
19330             case 9: // tab
19331                 this.setValue(this.formatDate(this.date));
19332                 this.hidePopup();
19333                 break;
19334             case 16: // shift
19335             case 17: // ctrl
19336             case 18: // alt
19337                 break;
19338             default :
19339                 this.hidePopup();
19340                 
19341         }
19342     },
19343     
19344     
19345     onClick: function(e) 
19346     {
19347         e.stopPropagation();
19348         e.preventDefault();
19349         
19350         var target = e.getTarget();
19351         
19352         if(target.nodeName.toLowerCase() === 'i'){
19353             target = Roo.get(target).dom.parentNode;
19354         }
19355         
19356         var nodeName = target.nodeName;
19357         var className = target.className;
19358         var html = target.innerHTML;
19359         //Roo.log(nodeName);
19360         
19361         switch(nodeName.toLowerCase()) {
19362             case 'th':
19363                 switch(className) {
19364                     case 'switch':
19365                         this.showMode(1);
19366                         break;
19367                     case 'prev':
19368                     case 'next':
19369                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19370                         switch(this.viewMode){
19371                                 case 0:
19372                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19373                                         break;
19374                                 case 1:
19375                                 case 2:
19376                                         this.viewDate = this.moveYear(this.viewDate, dir);
19377                                         break;
19378                         }
19379                         this.fill();
19380                         break;
19381                     case 'today':
19382                         var date = new Date();
19383                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19384 //                        this.fill()
19385                         this.setValue(this.formatDate(this.date));
19386                         
19387                         this.hidePopup();
19388                         break;
19389                 }
19390                 break;
19391             case 'span':
19392                 if (className.indexOf('disabled') < 0) {
19393                     this.viewDate.setUTCDate(1);
19394                     if (className.indexOf('month') > -1) {
19395                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19396                     } else {
19397                         var year = parseInt(html, 10) || 0;
19398                         this.viewDate.setUTCFullYear(year);
19399                         
19400                     }
19401                     
19402                     if(this.singleMode){
19403                         this.setValue(this.formatDate(this.viewDate));
19404                         this.hidePopup();
19405                         return;
19406                     }
19407                     
19408                     this.showMode(-1);
19409                     this.fill();
19410                 }
19411                 break;
19412                 
19413             case 'td':
19414                 //Roo.log(className);
19415                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19416                     var day = parseInt(html, 10) || 1;
19417                     var year = this.viewDate.getUTCFullYear(),
19418                         month = this.viewDate.getUTCMonth();
19419
19420                     if (className.indexOf('old') > -1) {
19421                         if(month === 0 ){
19422                             month = 11;
19423                             year -= 1;
19424                         }else{
19425                             month -= 1;
19426                         }
19427                     } else if (className.indexOf('new') > -1) {
19428                         if (month == 11) {
19429                             month = 0;
19430                             year += 1;
19431                         } else {
19432                             month += 1;
19433                         }
19434                     }
19435                     //Roo.log([year,month,day]);
19436                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19437                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19438 //                    this.fill();
19439                     //Roo.log(this.formatDate(this.date));
19440                     this.setValue(this.formatDate(this.date));
19441                     this.hidePopup();
19442                 }
19443                 break;
19444         }
19445     },
19446     
19447     setStartDate: function(startDate)
19448     {
19449         this.startDate = startDate || -Infinity;
19450         if (this.startDate !== -Infinity) {
19451             this.startDate = this.parseDate(this.startDate);
19452         }
19453         this.update();
19454         this.updateNavArrows();
19455     },
19456
19457     setEndDate: function(endDate)
19458     {
19459         this.endDate = endDate || Infinity;
19460         if (this.endDate !== Infinity) {
19461             this.endDate = this.parseDate(this.endDate);
19462         }
19463         this.update();
19464         this.updateNavArrows();
19465     },
19466     
19467     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19468     {
19469         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19470         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19471             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19472         }
19473         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19474             return parseInt(d, 10);
19475         });
19476         this.update();
19477         this.updateNavArrows();
19478     },
19479     
19480     updateNavArrows: function() 
19481     {
19482         if(this.singleMode){
19483             return;
19484         }
19485         
19486         var d = new Date(this.viewDate),
19487         year = d.getUTCFullYear(),
19488         month = d.getUTCMonth();
19489         
19490         Roo.each(this.picker().select('.prev', true).elements, function(v){
19491             v.show();
19492             switch (this.viewMode) {
19493                 case 0:
19494
19495                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19496                         v.hide();
19497                     }
19498                     break;
19499                 case 1:
19500                 case 2:
19501                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19502                         v.hide();
19503                     }
19504                     break;
19505             }
19506         });
19507         
19508         Roo.each(this.picker().select('.next', true).elements, function(v){
19509             v.show();
19510             switch (this.viewMode) {
19511                 case 0:
19512
19513                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19514                         v.hide();
19515                     }
19516                     break;
19517                 case 1:
19518                 case 2:
19519                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19520                         v.hide();
19521                     }
19522                     break;
19523             }
19524         })
19525     },
19526     
19527     moveMonth: function(date, dir)
19528     {
19529         if (!dir) {
19530             return date;
19531         }
19532         var new_date = new Date(date.valueOf()),
19533         day = new_date.getUTCDate(),
19534         month = new_date.getUTCMonth(),
19535         mag = Math.abs(dir),
19536         new_month, test;
19537         dir = dir > 0 ? 1 : -1;
19538         if (mag == 1){
19539             test = dir == -1
19540             // If going back one month, make sure month is not current month
19541             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19542             ? function(){
19543                 return new_date.getUTCMonth() == month;
19544             }
19545             // If going forward one month, make sure month is as expected
19546             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19547             : function(){
19548                 return new_date.getUTCMonth() != new_month;
19549             };
19550             new_month = month + dir;
19551             new_date.setUTCMonth(new_month);
19552             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19553             if (new_month < 0 || new_month > 11) {
19554                 new_month = (new_month + 12) % 12;
19555             }
19556         } else {
19557             // For magnitudes >1, move one month at a time...
19558             for (var i=0; i<mag; i++) {
19559                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19560                 new_date = this.moveMonth(new_date, dir);
19561             }
19562             // ...then reset the day, keeping it in the new month
19563             new_month = new_date.getUTCMonth();
19564             new_date.setUTCDate(day);
19565             test = function(){
19566                 return new_month != new_date.getUTCMonth();
19567             };
19568         }
19569         // Common date-resetting loop -- if date is beyond end of month, make it
19570         // end of month
19571         while (test()){
19572             new_date.setUTCDate(--day);
19573             new_date.setUTCMonth(new_month);
19574         }
19575         return new_date;
19576     },
19577
19578     moveYear: function(date, dir)
19579     {
19580         return this.moveMonth(date, dir*12);
19581     },
19582
19583     dateWithinRange: function(date)
19584     {
19585         return date >= this.startDate && date <= this.endDate;
19586     },
19587
19588     
19589     remove: function() 
19590     {
19591         this.picker().remove();
19592     },
19593     
19594     validateValue : function(value)
19595     {
19596         if(this.getVisibilityEl().hasClass('hidden')){
19597             return true;
19598         }
19599         
19600         if(value.length < 1)  {
19601             if(this.allowBlank){
19602                 return true;
19603             }
19604             return false;
19605         }
19606         
19607         if(value.length < this.minLength){
19608             return false;
19609         }
19610         if(value.length > this.maxLength){
19611             return false;
19612         }
19613         if(this.vtype){
19614             var vt = Roo.form.VTypes;
19615             if(!vt[this.vtype](value, this)){
19616                 return false;
19617             }
19618         }
19619         if(typeof this.validator == "function"){
19620             var msg = this.validator(value);
19621             if(msg !== true){
19622                 return false;
19623             }
19624         }
19625         
19626         if(this.regex && !this.regex.test(value)){
19627             return false;
19628         }
19629         
19630         if(typeof(this.parseDate(value)) == 'undefined'){
19631             return false;
19632         }
19633         
19634         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19635             return false;
19636         }      
19637         
19638         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19639             return false;
19640         } 
19641         
19642         
19643         return true;
19644     },
19645     
19646     reset : function()
19647     {
19648         this.date = this.viewDate = '';
19649         
19650         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19651     }
19652    
19653 });
19654
19655 Roo.apply(Roo.bootstrap.DateField,  {
19656     
19657     head : {
19658         tag: 'thead',
19659         cn: [
19660         {
19661             tag: 'tr',
19662             cn: [
19663             {
19664                 tag: 'th',
19665                 cls: 'prev',
19666                 html: '<i class="fa fa-arrow-left"/>'
19667             },
19668             {
19669                 tag: 'th',
19670                 cls: 'switch',
19671                 colspan: '5'
19672             },
19673             {
19674                 tag: 'th',
19675                 cls: 'next',
19676                 html: '<i class="fa fa-arrow-right"/>'
19677             }
19678
19679             ]
19680         }
19681         ]
19682     },
19683     
19684     content : {
19685         tag: 'tbody',
19686         cn: [
19687         {
19688             tag: 'tr',
19689             cn: [
19690             {
19691                 tag: 'td',
19692                 colspan: '7'
19693             }
19694             ]
19695         }
19696         ]
19697     },
19698     
19699     footer : {
19700         tag: 'tfoot',
19701         cn: [
19702         {
19703             tag: 'tr',
19704             cn: [
19705             {
19706                 tag: 'th',
19707                 colspan: '7',
19708                 cls: 'today'
19709             }
19710                     
19711             ]
19712         }
19713         ]
19714     },
19715     
19716     dates:{
19717         en: {
19718             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19719             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19720             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19721             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19722             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19723             today: "Today"
19724         }
19725     },
19726     
19727     modes: [
19728     {
19729         clsName: 'days',
19730         navFnc: 'Month',
19731         navStep: 1
19732     },
19733     {
19734         clsName: 'months',
19735         navFnc: 'FullYear',
19736         navStep: 1
19737     },
19738     {
19739         clsName: 'years',
19740         navFnc: 'FullYear',
19741         navStep: 10
19742     }]
19743 });
19744
19745 Roo.apply(Roo.bootstrap.DateField,  {
19746   
19747     template : {
19748         tag: 'div',
19749         cls: 'datepicker dropdown-menu roo-dynamic',
19750         cn: [
19751         {
19752             tag: 'div',
19753             cls: 'datepicker-days',
19754             cn: [
19755             {
19756                 tag: 'table',
19757                 cls: 'table-condensed',
19758                 cn:[
19759                 Roo.bootstrap.DateField.head,
19760                 {
19761                     tag: 'tbody'
19762                 },
19763                 Roo.bootstrap.DateField.footer
19764                 ]
19765             }
19766             ]
19767         },
19768         {
19769             tag: 'div',
19770             cls: 'datepicker-months',
19771             cn: [
19772             {
19773                 tag: 'table',
19774                 cls: 'table-condensed',
19775                 cn:[
19776                 Roo.bootstrap.DateField.head,
19777                 Roo.bootstrap.DateField.content,
19778                 Roo.bootstrap.DateField.footer
19779                 ]
19780             }
19781             ]
19782         },
19783         {
19784             tag: 'div',
19785             cls: 'datepicker-years',
19786             cn: [
19787             {
19788                 tag: 'table',
19789                 cls: 'table-condensed',
19790                 cn:[
19791                 Roo.bootstrap.DateField.head,
19792                 Roo.bootstrap.DateField.content,
19793                 Roo.bootstrap.DateField.footer
19794                 ]
19795             }
19796             ]
19797         }
19798         ]
19799     }
19800 });
19801
19802  
19803
19804  /*
19805  * - LGPL
19806  *
19807  * TimeField
19808  * 
19809  */
19810
19811 /**
19812  * @class Roo.bootstrap.TimeField
19813  * @extends Roo.bootstrap.Input
19814  * Bootstrap DateField class
19815  * 
19816  * 
19817  * @constructor
19818  * Create a new TimeField
19819  * @param {Object} config The config object
19820  */
19821
19822 Roo.bootstrap.TimeField = function(config){
19823     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19824     this.addEvents({
19825             /**
19826              * @event show
19827              * Fires when this field show.
19828              * @param {Roo.bootstrap.DateField} thisthis
19829              * @param {Mixed} date The date value
19830              */
19831             show : true,
19832             /**
19833              * @event show
19834              * Fires when this field hide.
19835              * @param {Roo.bootstrap.DateField} this
19836              * @param {Mixed} date The date value
19837              */
19838             hide : true,
19839             /**
19840              * @event select
19841              * Fires when select a date.
19842              * @param {Roo.bootstrap.DateField} this
19843              * @param {Mixed} date The date value
19844              */
19845             select : true
19846         });
19847 };
19848
19849 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19850     
19851     /**
19852      * @cfg {String} format
19853      * The default time format string which can be overriden for localization support.  The format must be
19854      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19855      */
19856     format : "H:i",
19857        
19858     onRender: function(ct, position)
19859     {
19860         
19861         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19862                 
19863         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19864         
19865         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19866         
19867         this.pop = this.picker().select('>.datepicker-time',true).first();
19868         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19869         
19870         this.picker().on('mousedown', this.onMousedown, this);
19871         this.picker().on('click', this.onClick, this);
19872         
19873         this.picker().addClass('datepicker-dropdown');
19874     
19875         this.fillTime();
19876         this.update();
19877             
19878         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19879         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19880         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19881         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19882         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19883         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19884
19885     },
19886     
19887     fireKey: function(e){
19888         if (!this.picker().isVisible()){
19889             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19890                 this.show();
19891             }
19892             return;
19893         }
19894
19895         e.preventDefault();
19896         
19897         switch(e.keyCode){
19898             case 27: // escape
19899                 this.hide();
19900                 break;
19901             case 37: // left
19902             case 39: // right
19903                 this.onTogglePeriod();
19904                 break;
19905             case 38: // up
19906                 this.onIncrementMinutes();
19907                 break;
19908             case 40: // down
19909                 this.onDecrementMinutes();
19910                 break;
19911             case 13: // enter
19912             case 9: // tab
19913                 this.setTime();
19914                 break;
19915         }
19916     },
19917     
19918     onClick: function(e) {
19919         e.stopPropagation();
19920         e.preventDefault();
19921     },
19922     
19923     picker : function()
19924     {
19925         return this.el.select('.datepicker', true).first();
19926     },
19927     
19928     fillTime: function()
19929     {    
19930         var time = this.pop.select('tbody', true).first();
19931         
19932         time.dom.innerHTML = '';
19933         
19934         time.createChild({
19935             tag: 'tr',
19936             cn: [
19937                 {
19938                     tag: 'td',
19939                     cn: [
19940                         {
19941                             tag: 'a',
19942                             href: '#',
19943                             cls: 'btn',
19944                             cn: [
19945                                 {
19946                                     tag: 'span',
19947                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19948                                 }
19949                             ]
19950                         } 
19951                     ]
19952                 },
19953                 {
19954                     tag: 'td',
19955                     cls: 'separator'
19956                 },
19957                 {
19958                     tag: 'td',
19959                     cn: [
19960                         {
19961                             tag: 'a',
19962                             href: '#',
19963                             cls: 'btn',
19964                             cn: [
19965                                 {
19966                                     tag: 'span',
19967                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19968                                 }
19969                             ]
19970                         }
19971                     ]
19972                 },
19973                 {
19974                     tag: 'td',
19975                     cls: 'separator'
19976                 }
19977             ]
19978         });
19979         
19980         time.createChild({
19981             tag: 'tr',
19982             cn: [
19983                 {
19984                     tag: 'td',
19985                     cn: [
19986                         {
19987                             tag: 'span',
19988                             cls: 'timepicker-hour',
19989                             html: '00'
19990                         }  
19991                     ]
19992                 },
19993                 {
19994                     tag: 'td',
19995                     cls: 'separator',
19996                     html: ':'
19997                 },
19998                 {
19999                     tag: 'td',
20000                     cn: [
20001                         {
20002                             tag: 'span',
20003                             cls: 'timepicker-minute',
20004                             html: '00'
20005                         }  
20006                     ]
20007                 },
20008                 {
20009                     tag: 'td',
20010                     cls: 'separator'
20011                 },
20012                 {
20013                     tag: 'td',
20014                     cn: [
20015                         {
20016                             tag: 'button',
20017                             type: 'button',
20018                             cls: 'btn btn-primary period',
20019                             html: 'AM'
20020                             
20021                         }
20022                     ]
20023                 }
20024             ]
20025         });
20026         
20027         time.createChild({
20028             tag: 'tr',
20029             cn: [
20030                 {
20031                     tag: 'td',
20032                     cn: [
20033                         {
20034                             tag: 'a',
20035                             href: '#',
20036                             cls: 'btn',
20037                             cn: [
20038                                 {
20039                                     tag: 'span',
20040                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20041                                 }
20042                             ]
20043                         }
20044                     ]
20045                 },
20046                 {
20047                     tag: 'td',
20048                     cls: 'separator'
20049                 },
20050                 {
20051                     tag: 'td',
20052                     cn: [
20053                         {
20054                             tag: 'a',
20055                             href: '#',
20056                             cls: 'btn',
20057                             cn: [
20058                                 {
20059                                     tag: 'span',
20060                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20061                                 }
20062                             ]
20063                         }
20064                     ]
20065                 },
20066                 {
20067                     tag: 'td',
20068                     cls: 'separator'
20069                 }
20070             ]
20071         });
20072         
20073     },
20074     
20075     update: function()
20076     {
20077         
20078         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20079         
20080         this.fill();
20081     },
20082     
20083     fill: function() 
20084     {
20085         var hours = this.time.getHours();
20086         var minutes = this.time.getMinutes();
20087         var period = 'AM';
20088         
20089         if(hours > 11){
20090             period = 'PM';
20091         }
20092         
20093         if(hours == 0){
20094             hours = 12;
20095         }
20096         
20097         
20098         if(hours > 12){
20099             hours = hours - 12;
20100         }
20101         
20102         if(hours < 10){
20103             hours = '0' + hours;
20104         }
20105         
20106         if(minutes < 10){
20107             minutes = '0' + minutes;
20108         }
20109         
20110         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20111         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20112         this.pop.select('button', true).first().dom.innerHTML = period;
20113         
20114     },
20115     
20116     place: function()
20117     {   
20118         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20119         
20120         var cls = ['bottom'];
20121         
20122         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20123             cls.pop();
20124             cls.push('top');
20125         }
20126         
20127         cls.push('right');
20128         
20129         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20130             cls.pop();
20131             cls.push('left');
20132         }
20133         
20134         this.picker().addClass(cls.join('-'));
20135         
20136         var _this = this;
20137         
20138         Roo.each(cls, function(c){
20139             if(c == 'bottom'){
20140                 _this.picker().setTop(_this.inputEl().getHeight());
20141                 return;
20142             }
20143             if(c == 'top'){
20144                 _this.picker().setTop(0 - _this.picker().getHeight());
20145                 return;
20146             }
20147             
20148             if(c == 'left'){
20149                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20150                 return;
20151             }
20152             if(c == 'right'){
20153                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20154                 return;
20155             }
20156         });
20157         
20158     },
20159   
20160     onFocus : function()
20161     {
20162         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20163         this.show();
20164     },
20165     
20166     onBlur : function()
20167     {
20168         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20169         this.hide();
20170     },
20171     
20172     show : function()
20173     {
20174         this.picker().show();
20175         this.pop.show();
20176         this.update();
20177         this.place();
20178         
20179         this.fireEvent('show', this, this.date);
20180     },
20181     
20182     hide : function()
20183     {
20184         this.picker().hide();
20185         this.pop.hide();
20186         
20187         this.fireEvent('hide', this, this.date);
20188     },
20189     
20190     setTime : function()
20191     {
20192         this.hide();
20193         this.setValue(this.time.format(this.format));
20194         
20195         this.fireEvent('select', this, this.date);
20196         
20197         
20198     },
20199     
20200     onMousedown: function(e){
20201         e.stopPropagation();
20202         e.preventDefault();
20203     },
20204     
20205     onIncrementHours: function()
20206     {
20207         Roo.log('onIncrementHours');
20208         this.time = this.time.add(Date.HOUR, 1);
20209         this.update();
20210         
20211     },
20212     
20213     onDecrementHours: function()
20214     {
20215         Roo.log('onDecrementHours');
20216         this.time = this.time.add(Date.HOUR, -1);
20217         this.update();
20218     },
20219     
20220     onIncrementMinutes: function()
20221     {
20222         Roo.log('onIncrementMinutes');
20223         this.time = this.time.add(Date.MINUTE, 1);
20224         this.update();
20225     },
20226     
20227     onDecrementMinutes: function()
20228     {
20229         Roo.log('onDecrementMinutes');
20230         this.time = this.time.add(Date.MINUTE, -1);
20231         this.update();
20232     },
20233     
20234     onTogglePeriod: function()
20235     {
20236         Roo.log('onTogglePeriod');
20237         this.time = this.time.add(Date.HOUR, 12);
20238         this.update();
20239     }
20240     
20241    
20242 });
20243
20244 Roo.apply(Roo.bootstrap.TimeField,  {
20245     
20246     content : {
20247         tag: 'tbody',
20248         cn: [
20249             {
20250                 tag: 'tr',
20251                 cn: [
20252                 {
20253                     tag: 'td',
20254                     colspan: '7'
20255                 }
20256                 ]
20257             }
20258         ]
20259     },
20260     
20261     footer : {
20262         tag: 'tfoot',
20263         cn: [
20264             {
20265                 tag: 'tr',
20266                 cn: [
20267                 {
20268                     tag: 'th',
20269                     colspan: '7',
20270                     cls: '',
20271                     cn: [
20272                         {
20273                             tag: 'button',
20274                             cls: 'btn btn-info ok',
20275                             html: 'OK'
20276                         }
20277                     ]
20278                 }
20279
20280                 ]
20281             }
20282         ]
20283     }
20284 });
20285
20286 Roo.apply(Roo.bootstrap.TimeField,  {
20287   
20288     template : {
20289         tag: 'div',
20290         cls: 'datepicker dropdown-menu',
20291         cn: [
20292             {
20293                 tag: 'div',
20294                 cls: 'datepicker-time',
20295                 cn: [
20296                 {
20297                     tag: 'table',
20298                     cls: 'table-condensed',
20299                     cn:[
20300                     Roo.bootstrap.TimeField.content,
20301                     Roo.bootstrap.TimeField.footer
20302                     ]
20303                 }
20304                 ]
20305             }
20306         ]
20307     }
20308 });
20309
20310  
20311
20312  /*
20313  * - LGPL
20314  *
20315  * MonthField
20316  * 
20317  */
20318
20319 /**
20320  * @class Roo.bootstrap.MonthField
20321  * @extends Roo.bootstrap.Input
20322  * Bootstrap MonthField class
20323  * 
20324  * @cfg {String} language default en
20325  * 
20326  * @constructor
20327  * Create a new MonthField
20328  * @param {Object} config The config object
20329  */
20330
20331 Roo.bootstrap.MonthField = function(config){
20332     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20333     
20334     this.addEvents({
20335         /**
20336          * @event show
20337          * Fires when this field show.
20338          * @param {Roo.bootstrap.MonthField} this
20339          * @param {Mixed} date The date value
20340          */
20341         show : true,
20342         /**
20343          * @event show
20344          * Fires when this field hide.
20345          * @param {Roo.bootstrap.MonthField} this
20346          * @param {Mixed} date The date value
20347          */
20348         hide : true,
20349         /**
20350          * @event select
20351          * Fires when select a date.
20352          * @param {Roo.bootstrap.MonthField} this
20353          * @param {String} oldvalue The old value
20354          * @param {String} newvalue The new value
20355          */
20356         select : true
20357     });
20358 };
20359
20360 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20361     
20362     onRender: function(ct, position)
20363     {
20364         
20365         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20366         
20367         this.language = this.language || 'en';
20368         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20369         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20370         
20371         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20372         this.isInline = false;
20373         this.isInput = true;
20374         this.component = this.el.select('.add-on', true).first() || false;
20375         this.component = (this.component && this.component.length === 0) ? false : this.component;
20376         this.hasInput = this.component && this.inputEL().length;
20377         
20378         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20379         
20380         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20381         
20382         this.picker().on('mousedown', this.onMousedown, this);
20383         this.picker().on('click', this.onClick, this);
20384         
20385         this.picker().addClass('datepicker-dropdown');
20386         
20387         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20388             v.setStyle('width', '189px');
20389         });
20390         
20391         this.fillMonths();
20392         
20393         this.update();
20394         
20395         if(this.isInline) {
20396             this.show();
20397         }
20398         
20399     },
20400     
20401     setValue: function(v, suppressEvent)
20402     {   
20403         var o = this.getValue();
20404         
20405         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20406         
20407         this.update();
20408
20409         if(suppressEvent !== true){
20410             this.fireEvent('select', this, o, v);
20411         }
20412         
20413     },
20414     
20415     getValue: function()
20416     {
20417         return this.value;
20418     },
20419     
20420     onClick: function(e) 
20421     {
20422         e.stopPropagation();
20423         e.preventDefault();
20424         
20425         var target = e.getTarget();
20426         
20427         if(target.nodeName.toLowerCase() === 'i'){
20428             target = Roo.get(target).dom.parentNode;
20429         }
20430         
20431         var nodeName = target.nodeName;
20432         var className = target.className;
20433         var html = target.innerHTML;
20434         
20435         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20436             return;
20437         }
20438         
20439         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20440         
20441         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20442         
20443         this.hide();
20444                         
20445     },
20446     
20447     picker : function()
20448     {
20449         return this.pickerEl;
20450     },
20451     
20452     fillMonths: function()
20453     {    
20454         var i = 0;
20455         var months = this.picker().select('>.datepicker-months td', true).first();
20456         
20457         months.dom.innerHTML = '';
20458         
20459         while (i < 12) {
20460             var month = {
20461                 tag: 'span',
20462                 cls: 'month',
20463                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20464             };
20465             
20466             months.createChild(month);
20467         }
20468         
20469     },
20470     
20471     update: function()
20472     {
20473         var _this = this;
20474         
20475         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20476             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20477         }
20478         
20479         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20480             e.removeClass('active');
20481             
20482             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20483                 e.addClass('active');
20484             }
20485         })
20486     },
20487     
20488     place: function()
20489     {
20490         if(this.isInline) {
20491             return;
20492         }
20493         
20494         this.picker().removeClass(['bottom', 'top']);
20495         
20496         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20497             /*
20498              * place to the top of element!
20499              *
20500              */
20501             
20502             this.picker().addClass('top');
20503             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20504             
20505             return;
20506         }
20507         
20508         this.picker().addClass('bottom');
20509         
20510         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20511     },
20512     
20513     onFocus : function()
20514     {
20515         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20516         this.show();
20517     },
20518     
20519     onBlur : function()
20520     {
20521         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20522         
20523         var d = this.inputEl().getValue();
20524         
20525         this.setValue(d);
20526                 
20527         this.hide();
20528     },
20529     
20530     show : function()
20531     {
20532         this.picker().show();
20533         this.picker().select('>.datepicker-months', true).first().show();
20534         this.update();
20535         this.place();
20536         
20537         this.fireEvent('show', this, this.date);
20538     },
20539     
20540     hide : function()
20541     {
20542         if(this.isInline) {
20543             return;
20544         }
20545         this.picker().hide();
20546         this.fireEvent('hide', this, this.date);
20547         
20548     },
20549     
20550     onMousedown: function(e)
20551     {
20552         e.stopPropagation();
20553         e.preventDefault();
20554     },
20555     
20556     keyup: function(e)
20557     {
20558         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20559         this.update();
20560     },
20561
20562     fireKey: function(e)
20563     {
20564         if (!this.picker().isVisible()){
20565             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20566                 this.show();
20567             }
20568             return;
20569         }
20570         
20571         var dir;
20572         
20573         switch(e.keyCode){
20574             case 27: // escape
20575                 this.hide();
20576                 e.preventDefault();
20577                 break;
20578             case 37: // left
20579             case 39: // right
20580                 dir = e.keyCode == 37 ? -1 : 1;
20581                 
20582                 this.vIndex = this.vIndex + dir;
20583                 
20584                 if(this.vIndex < 0){
20585                     this.vIndex = 0;
20586                 }
20587                 
20588                 if(this.vIndex > 11){
20589                     this.vIndex = 11;
20590                 }
20591                 
20592                 if(isNaN(this.vIndex)){
20593                     this.vIndex = 0;
20594                 }
20595                 
20596                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20597                 
20598                 break;
20599             case 38: // up
20600             case 40: // down
20601                 
20602                 dir = e.keyCode == 38 ? -1 : 1;
20603                 
20604                 this.vIndex = this.vIndex + dir * 4;
20605                 
20606                 if(this.vIndex < 0){
20607                     this.vIndex = 0;
20608                 }
20609                 
20610                 if(this.vIndex > 11){
20611                     this.vIndex = 11;
20612                 }
20613                 
20614                 if(isNaN(this.vIndex)){
20615                     this.vIndex = 0;
20616                 }
20617                 
20618                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20619                 break;
20620                 
20621             case 13: // enter
20622                 
20623                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20624                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20625                 }
20626                 
20627                 this.hide();
20628                 e.preventDefault();
20629                 break;
20630             case 9: // tab
20631                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20632                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20633                 }
20634                 this.hide();
20635                 break;
20636             case 16: // shift
20637             case 17: // ctrl
20638             case 18: // alt
20639                 break;
20640             default :
20641                 this.hide();
20642                 
20643         }
20644     },
20645     
20646     remove: function() 
20647     {
20648         this.picker().remove();
20649     }
20650    
20651 });
20652
20653 Roo.apply(Roo.bootstrap.MonthField,  {
20654     
20655     content : {
20656         tag: 'tbody',
20657         cn: [
20658         {
20659             tag: 'tr',
20660             cn: [
20661             {
20662                 tag: 'td',
20663                 colspan: '7'
20664             }
20665             ]
20666         }
20667         ]
20668     },
20669     
20670     dates:{
20671         en: {
20672             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20673             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20674         }
20675     }
20676 });
20677
20678 Roo.apply(Roo.bootstrap.MonthField,  {
20679   
20680     template : {
20681         tag: 'div',
20682         cls: 'datepicker dropdown-menu roo-dynamic',
20683         cn: [
20684             {
20685                 tag: 'div',
20686                 cls: 'datepicker-months',
20687                 cn: [
20688                 {
20689                     tag: 'table',
20690                     cls: 'table-condensed',
20691                     cn:[
20692                         Roo.bootstrap.DateField.content
20693                     ]
20694                 }
20695                 ]
20696             }
20697         ]
20698     }
20699 });
20700
20701  
20702
20703  
20704  /*
20705  * - LGPL
20706  *
20707  * CheckBox
20708  * 
20709  */
20710
20711 /**
20712  * @class Roo.bootstrap.CheckBox
20713  * @extends Roo.bootstrap.Input
20714  * Bootstrap CheckBox class
20715  * 
20716  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20717  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20718  * @cfg {String} boxLabel The text that appears beside the checkbox
20719  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20720  * @cfg {Boolean} checked initnal the element
20721  * @cfg {Boolean} inline inline the element (default false)
20722  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20723  * @cfg {String} tooltip label tooltip
20724  * 
20725  * @constructor
20726  * Create a new CheckBox
20727  * @param {Object} config The config object
20728  */
20729
20730 Roo.bootstrap.CheckBox = function(config){
20731     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20732    
20733     this.addEvents({
20734         /**
20735         * @event check
20736         * Fires when the element is checked or unchecked.
20737         * @param {Roo.bootstrap.CheckBox} this This input
20738         * @param {Boolean} checked The new checked value
20739         */
20740        check : true,
20741        /**
20742         * @event click
20743         * Fires when the element is click.
20744         * @param {Roo.bootstrap.CheckBox} this This input
20745         */
20746        click : true
20747     });
20748     
20749 };
20750
20751 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20752   
20753     inputType: 'checkbox',
20754     inputValue: 1,
20755     valueOff: 0,
20756     boxLabel: false,
20757     checked: false,
20758     weight : false,
20759     inline: false,
20760     tooltip : '',
20761     
20762     getAutoCreate : function()
20763     {
20764         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20765         
20766         var id = Roo.id();
20767         
20768         var cfg = {};
20769         
20770         cfg.cls = 'form-group ' + this.inputType; //input-group
20771         
20772         if(this.inline){
20773             cfg.cls += ' ' + this.inputType + '-inline';
20774         }
20775         
20776         var input =  {
20777             tag: 'input',
20778             id : id,
20779             type : this.inputType,
20780             value : this.inputValue,
20781             cls : 'roo-' + this.inputType, //'form-box',
20782             placeholder : this.placeholder || ''
20783             
20784         };
20785         
20786         if(this.inputType != 'radio'){
20787             var hidden =  {
20788                 tag: 'input',
20789                 type : 'hidden',
20790                 cls : 'roo-hidden-value',
20791                 value : this.checked ? this.inputValue : this.valueOff
20792             };
20793         }
20794         
20795             
20796         if (this.weight) { // Validity check?
20797             cfg.cls += " " + this.inputType + "-" + this.weight;
20798         }
20799         
20800         if (this.disabled) {
20801             input.disabled=true;
20802         }
20803         
20804         if(this.checked){
20805             input.checked = this.checked;
20806         }
20807         
20808         if (this.name) {
20809             
20810             input.name = this.name;
20811             
20812             if(this.inputType != 'radio'){
20813                 hidden.name = this.name;
20814                 input.name = '_hidden_' + this.name;
20815             }
20816         }
20817         
20818         if (this.size) {
20819             input.cls += ' input-' + this.size;
20820         }
20821         
20822         var settings=this;
20823         
20824         ['xs','sm','md','lg'].map(function(size){
20825             if (settings[size]) {
20826                 cfg.cls += ' col-' + size + '-' + settings[size];
20827             }
20828         });
20829         
20830         var inputblock = input;
20831          
20832         if (this.before || this.after) {
20833             
20834             inputblock = {
20835                 cls : 'input-group',
20836                 cn :  [] 
20837             };
20838             
20839             if (this.before) {
20840                 inputblock.cn.push({
20841                     tag :'span',
20842                     cls : 'input-group-addon',
20843                     html : this.before
20844                 });
20845             }
20846             
20847             inputblock.cn.push(input);
20848             
20849             if(this.inputType != 'radio'){
20850                 inputblock.cn.push(hidden);
20851             }
20852             
20853             if (this.after) {
20854                 inputblock.cn.push({
20855                     tag :'span',
20856                     cls : 'input-group-addon',
20857                     html : this.after
20858                 });
20859             }
20860             
20861         }
20862         
20863         if (align ==='left' && this.fieldLabel.length) {
20864 //                Roo.log("left and has label");
20865             cfg.cn = [
20866                 {
20867                     tag: 'label',
20868                     'for' :  id,
20869                     cls : 'control-label',
20870                     html : this.fieldLabel
20871                 },
20872                 {
20873                     cls : "", 
20874                     cn: [
20875                         inputblock
20876                     ]
20877                 }
20878             ];
20879             
20880             if(this.labelWidth > 12){
20881                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20882             }
20883             
20884             if(this.labelWidth < 13 && this.labelmd == 0){
20885                 this.labelmd = this.labelWidth;
20886             }
20887             
20888             if(this.labellg > 0){
20889                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20890                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20891             }
20892             
20893             if(this.labelmd > 0){
20894                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20895                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20896             }
20897             
20898             if(this.labelsm > 0){
20899                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20900                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20901             }
20902             
20903             if(this.labelxs > 0){
20904                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20905                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20906             }
20907             
20908         } else if ( this.fieldLabel.length) {
20909 //                Roo.log(" label");
20910                 cfg.cn = [
20911                    
20912                     {
20913                         tag: this.boxLabel ? 'span' : 'label',
20914                         'for': id,
20915                         cls: 'control-label box-input-label',
20916                         //cls : 'input-group-addon',
20917                         html : this.fieldLabel
20918                     },
20919                     
20920                     inputblock
20921                     
20922                 ];
20923
20924         } else {
20925             
20926 //                Roo.log(" no label && no align");
20927                 cfg.cn = [  inputblock ] ;
20928                 
20929                 
20930         }
20931         
20932         if(this.boxLabel){
20933              var boxLabelCfg = {
20934                 tag: 'label',
20935                 //'for': id, // box label is handled by onclick - so no for...
20936                 cls: 'box-label',
20937                 html: this.boxLabel
20938             };
20939             
20940             if(this.tooltip){
20941                 boxLabelCfg.tooltip = this.tooltip;
20942             }
20943              
20944             cfg.cn.push(boxLabelCfg);
20945         }
20946         
20947         if(this.inputType != 'radio'){
20948             cfg.cn.push(hidden);
20949         }
20950         
20951         return cfg;
20952         
20953     },
20954     
20955     /**
20956      * return the real input element.
20957      */
20958     inputEl: function ()
20959     {
20960         return this.el.select('input.roo-' + this.inputType,true).first();
20961     },
20962     hiddenEl: function ()
20963     {
20964         return this.el.select('input.roo-hidden-value',true).first();
20965     },
20966     
20967     labelEl: function()
20968     {
20969         return this.el.select('label.control-label',true).first();
20970     },
20971     /* depricated... */
20972     
20973     label: function()
20974     {
20975         return this.labelEl();
20976     },
20977     
20978     boxLabelEl: function()
20979     {
20980         return this.el.select('label.box-label',true).first();
20981     },
20982     
20983     initEvents : function()
20984     {
20985 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20986         
20987         this.inputEl().on('click', this.onClick,  this);
20988         
20989         if (this.boxLabel) { 
20990             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20991         }
20992         
20993         this.startValue = this.getValue();
20994         
20995         if(this.groupId){
20996             Roo.bootstrap.CheckBox.register(this);
20997         }
20998     },
20999     
21000     onClick : function(e)
21001     {   
21002         if(this.fireEvent('click', this, e) !== false){
21003             this.setChecked(!this.checked);
21004         }
21005         
21006     },
21007     
21008     setChecked : function(state,suppressEvent)
21009     {
21010         this.startValue = this.getValue();
21011
21012         if(this.inputType == 'radio'){
21013             
21014             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21015                 e.dom.checked = false;
21016             });
21017             
21018             this.inputEl().dom.checked = true;
21019             
21020             this.inputEl().dom.value = this.inputValue;
21021             
21022             if(suppressEvent !== true){
21023                 this.fireEvent('check', this, true);
21024             }
21025             
21026             this.validate();
21027             
21028             return;
21029         }
21030         
21031         this.checked = state;
21032         
21033         this.inputEl().dom.checked = state;
21034         
21035         
21036         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21037         
21038         if(suppressEvent !== true){
21039             this.fireEvent('check', this, state);
21040         }
21041         
21042         this.validate();
21043     },
21044     
21045     getValue : function()
21046     {
21047         if(this.inputType == 'radio'){
21048             return this.getGroupValue();
21049         }
21050         
21051         return this.hiddenEl().dom.value;
21052         
21053     },
21054     
21055     getGroupValue : function()
21056     {
21057         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21058             return '';
21059         }
21060         
21061         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21062     },
21063     
21064     setValue : function(v,suppressEvent)
21065     {
21066         if(this.inputType == 'radio'){
21067             this.setGroupValue(v, suppressEvent);
21068             return;
21069         }
21070         
21071         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21072         
21073         this.validate();
21074     },
21075     
21076     setGroupValue : function(v, suppressEvent)
21077     {
21078         this.startValue = this.getValue();
21079         
21080         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21081             e.dom.checked = false;
21082             
21083             if(e.dom.value == v){
21084                 e.dom.checked = true;
21085             }
21086         });
21087         
21088         if(suppressEvent !== true){
21089             this.fireEvent('check', this, true);
21090         }
21091
21092         this.validate();
21093         
21094         return;
21095     },
21096     
21097     validate : function()
21098     {
21099         if(this.getVisibilityEl().hasClass('hidden')){
21100             return true;
21101         }
21102         
21103         if(
21104                 this.disabled || 
21105                 (this.inputType == 'radio' && this.validateRadio()) ||
21106                 (this.inputType == 'checkbox' && this.validateCheckbox())
21107         ){
21108             this.markValid();
21109             return true;
21110         }
21111         
21112         this.markInvalid();
21113         return false;
21114     },
21115     
21116     validateRadio : function()
21117     {
21118         if(this.getVisibilityEl().hasClass('hidden')){
21119             return true;
21120         }
21121         
21122         if(this.allowBlank){
21123             return true;
21124         }
21125         
21126         var valid = false;
21127         
21128         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21129             if(!e.dom.checked){
21130                 return;
21131             }
21132             
21133             valid = true;
21134             
21135             return false;
21136         });
21137         
21138         return valid;
21139     },
21140     
21141     validateCheckbox : function()
21142     {
21143         if(!this.groupId){
21144             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21145             //return (this.getValue() == this.inputValue) ? true : false;
21146         }
21147         
21148         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21149         
21150         if(!group){
21151             return false;
21152         }
21153         
21154         var r = false;
21155         
21156         for(var i in group){
21157             if(group[i].el.isVisible(true)){
21158                 r = false;
21159                 break;
21160             }
21161             
21162             r = true;
21163         }
21164         
21165         for(var i in group){
21166             if(r){
21167                 break;
21168             }
21169             
21170             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21171         }
21172         
21173         return r;
21174     },
21175     
21176     /**
21177      * Mark this field as valid
21178      */
21179     markValid : function()
21180     {
21181         var _this = this;
21182         
21183         this.fireEvent('valid', this);
21184         
21185         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21186         
21187         if(this.groupId){
21188             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21189         }
21190         
21191         if(label){
21192             label.markValid();
21193         }
21194
21195         if(this.inputType == 'radio'){
21196             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21197                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21198                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21199             });
21200             
21201             return;
21202         }
21203
21204         if(!this.groupId){
21205             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21206             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21207             return;
21208         }
21209         
21210         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21211         
21212         if(!group){
21213             return;
21214         }
21215         
21216         for(var i in group){
21217             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21218             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21219         }
21220     },
21221     
21222      /**
21223      * Mark this field as invalid
21224      * @param {String} msg The validation message
21225      */
21226     markInvalid : function(msg)
21227     {
21228         if(this.allowBlank){
21229             return;
21230         }
21231         
21232         var _this = this;
21233         
21234         this.fireEvent('invalid', this, msg);
21235         
21236         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21237         
21238         if(this.groupId){
21239             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21240         }
21241         
21242         if(label){
21243             label.markInvalid();
21244         }
21245             
21246         if(this.inputType == 'radio'){
21247             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21248                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21249                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21250             });
21251             
21252             return;
21253         }
21254         
21255         if(!this.groupId){
21256             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21257             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21258             return;
21259         }
21260         
21261         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21262         
21263         if(!group){
21264             return;
21265         }
21266         
21267         for(var i in group){
21268             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21269             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21270         }
21271         
21272     },
21273     
21274     clearInvalid : function()
21275     {
21276         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21277         
21278         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21279         
21280         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21281         
21282         if (label && label.iconEl) {
21283             label.iconEl.removeClass(label.validClass);
21284             label.iconEl.removeClass(label.invalidClass);
21285         }
21286     },
21287     
21288     disable : function()
21289     {
21290         if(this.inputType != 'radio'){
21291             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21292             return;
21293         }
21294         
21295         var _this = this;
21296         
21297         if(this.rendered){
21298             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21299                 _this.getActionEl().addClass(this.disabledClass);
21300                 e.dom.disabled = true;
21301             });
21302         }
21303         
21304         this.disabled = true;
21305         this.fireEvent("disable", this);
21306         return this;
21307     },
21308
21309     enable : function()
21310     {
21311         if(this.inputType != 'radio'){
21312             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21313             return;
21314         }
21315         
21316         var _this = this;
21317         
21318         if(this.rendered){
21319             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21320                 _this.getActionEl().removeClass(this.disabledClass);
21321                 e.dom.disabled = false;
21322             });
21323         }
21324         
21325         this.disabled = false;
21326         this.fireEvent("enable", this);
21327         return this;
21328     },
21329     
21330     setBoxLabel : function(v)
21331     {
21332         this.boxLabel = v;
21333         
21334         if(this.rendered){
21335             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21336         }
21337     }
21338
21339 });
21340
21341 Roo.apply(Roo.bootstrap.CheckBox, {
21342     
21343     groups: {},
21344     
21345      /**
21346     * register a CheckBox Group
21347     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21348     */
21349     register : function(checkbox)
21350     {
21351         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21352             this.groups[checkbox.groupId] = {};
21353         }
21354         
21355         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21356             return;
21357         }
21358         
21359         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21360         
21361     },
21362     /**
21363     * fetch a CheckBox Group based on the group ID
21364     * @param {string} the group ID
21365     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21366     */
21367     get: function(groupId) {
21368         if (typeof(this.groups[groupId]) == 'undefined') {
21369             return false;
21370         }
21371         
21372         return this.groups[groupId] ;
21373     }
21374     
21375     
21376 });
21377 /*
21378  * - LGPL
21379  *
21380  * RadioItem
21381  * 
21382  */
21383
21384 /**
21385  * @class Roo.bootstrap.Radio
21386  * @extends Roo.bootstrap.Component
21387  * Bootstrap Radio class
21388  * @cfg {String} boxLabel - the label associated
21389  * @cfg {String} value - the value of radio
21390  * 
21391  * @constructor
21392  * Create a new Radio
21393  * @param {Object} config The config object
21394  */
21395 Roo.bootstrap.Radio = function(config){
21396     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21397     
21398 };
21399
21400 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21401     
21402     boxLabel : '',
21403     
21404     value : '',
21405     
21406     getAutoCreate : function()
21407     {
21408         var cfg = {
21409             tag : 'div',
21410             cls : 'form-group radio',
21411             cn : [
21412                 {
21413                     tag : 'label',
21414                     cls : 'box-label',
21415                     html : this.boxLabel
21416                 }
21417             ]
21418         };
21419         
21420         return cfg;
21421     },
21422     
21423     initEvents : function() 
21424     {
21425         this.parent().register(this);
21426         
21427         this.el.on('click', this.onClick, this);
21428         
21429     },
21430     
21431     onClick : function(e)
21432     {
21433         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21434             this.setChecked(true);
21435         }
21436     },
21437     
21438     setChecked : function(state, suppressEvent)
21439     {
21440         this.parent().setValue(this.value, suppressEvent);
21441         
21442     },
21443     
21444     setBoxLabel : function(v)
21445     {
21446         this.boxLabel = v;
21447         
21448         if(this.rendered){
21449             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21450         }
21451     }
21452     
21453 });
21454  
21455
21456  /*
21457  * - LGPL
21458  *
21459  * Input
21460  * 
21461  */
21462
21463 /**
21464  * @class Roo.bootstrap.SecurePass
21465  * @extends Roo.bootstrap.Input
21466  * Bootstrap SecurePass class
21467  *
21468  * 
21469  * @constructor
21470  * Create a new SecurePass
21471  * @param {Object} config The config object
21472  */
21473  
21474 Roo.bootstrap.SecurePass = function (config) {
21475     // these go here, so the translation tool can replace them..
21476     this.errors = {
21477         PwdEmpty: "Please type a password, and then retype it to confirm.",
21478         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21479         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21480         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21481         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21482         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21483         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21484         TooWeak: "Your password is Too Weak."
21485     },
21486     this.meterLabel = "Password strength:";
21487     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21488     this.meterClass = [
21489         "roo-password-meter-tooweak", 
21490         "roo-password-meter-weak", 
21491         "roo-password-meter-medium", 
21492         "roo-password-meter-strong", 
21493         "roo-password-meter-grey"
21494     ];
21495     
21496     this.errors = {};
21497     
21498     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21499 }
21500
21501 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21502     /**
21503      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21504      * {
21505      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21506      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21507      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21508      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21509      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21510      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21511      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21512      * })
21513      */
21514     // private
21515     
21516     meterWidth: 300,
21517     errorMsg :'',    
21518     errors: false,
21519     imageRoot: '/',
21520     /**
21521      * @cfg {String/Object} Label for the strength meter (defaults to
21522      * 'Password strength:')
21523      */
21524     // private
21525     meterLabel: '',
21526     /**
21527      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21528      * ['Weak', 'Medium', 'Strong'])
21529      */
21530     // private    
21531     pwdStrengths: false,    
21532     // private
21533     strength: 0,
21534     // private
21535     _lastPwd: null,
21536     // private
21537     kCapitalLetter: 0,
21538     kSmallLetter: 1,
21539     kDigit: 2,
21540     kPunctuation: 3,
21541     
21542     insecure: false,
21543     // private
21544     initEvents: function ()
21545     {
21546         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21547
21548         if (this.el.is('input[type=password]') && Roo.isSafari) {
21549             this.el.on('keydown', this.SafariOnKeyDown, this);
21550         }
21551
21552         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21553     },
21554     // private
21555     onRender: function (ct, position)
21556     {
21557         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21558         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21559         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21560
21561         this.trigger.createChild({
21562                    cn: [
21563                     {
21564                     //id: 'PwdMeter',
21565                     tag: 'div',
21566                     cls: 'roo-password-meter-grey col-xs-12',
21567                     style: {
21568                         //width: 0,
21569                         //width: this.meterWidth + 'px'                                                
21570                         }
21571                     },
21572                     {                            
21573                          cls: 'roo-password-meter-text'                          
21574                     }
21575                 ]            
21576         });
21577
21578          
21579         if (this.hideTrigger) {
21580             this.trigger.setDisplayed(false);
21581         }
21582         this.setSize(this.width || '', this.height || '');
21583     },
21584     // private
21585     onDestroy: function ()
21586     {
21587         if (this.trigger) {
21588             this.trigger.removeAllListeners();
21589             this.trigger.remove();
21590         }
21591         if (this.wrap) {
21592             this.wrap.remove();
21593         }
21594         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21595     },
21596     // private
21597     checkStrength: function ()
21598     {
21599         var pwd = this.inputEl().getValue();
21600         if (pwd == this._lastPwd) {
21601             return;
21602         }
21603
21604         var strength;
21605         if (this.ClientSideStrongPassword(pwd)) {
21606             strength = 3;
21607         } else if (this.ClientSideMediumPassword(pwd)) {
21608             strength = 2;
21609         } else if (this.ClientSideWeakPassword(pwd)) {
21610             strength = 1;
21611         } else {
21612             strength = 0;
21613         }
21614         
21615         Roo.log('strength1: ' + strength);
21616         
21617         //var pm = this.trigger.child('div/div/div').dom;
21618         var pm = this.trigger.child('div/div');
21619         pm.removeClass(this.meterClass);
21620         pm.addClass(this.meterClass[strength]);
21621                 
21622         
21623         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21624                 
21625         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21626         
21627         this._lastPwd = pwd;
21628     },
21629     reset: function ()
21630     {
21631         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21632         
21633         this._lastPwd = '';
21634         
21635         var pm = this.trigger.child('div/div');
21636         pm.removeClass(this.meterClass);
21637         pm.addClass('roo-password-meter-grey');        
21638         
21639         
21640         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21641         
21642         pt.innerHTML = '';
21643         this.inputEl().dom.type='password';
21644     },
21645     // private
21646     validateValue: function (value)
21647     {
21648         
21649         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21650             return false;
21651         }
21652         if (value.length == 0) {
21653             if (this.allowBlank) {
21654                 this.clearInvalid();
21655                 return true;
21656             }
21657
21658             this.markInvalid(this.errors.PwdEmpty);
21659             this.errorMsg = this.errors.PwdEmpty;
21660             return false;
21661         }
21662         
21663         if(this.insecure){
21664             return true;
21665         }
21666         
21667         if ('[\x21-\x7e]*'.match(value)) {
21668             this.markInvalid(this.errors.PwdBadChar);
21669             this.errorMsg = this.errors.PwdBadChar;
21670             return false;
21671         }
21672         if (value.length < 6) {
21673             this.markInvalid(this.errors.PwdShort);
21674             this.errorMsg = this.errors.PwdShort;
21675             return false;
21676         }
21677         if (value.length > 16) {
21678             this.markInvalid(this.errors.PwdLong);
21679             this.errorMsg = this.errors.PwdLong;
21680             return false;
21681         }
21682         var strength;
21683         if (this.ClientSideStrongPassword(value)) {
21684             strength = 3;
21685         } else if (this.ClientSideMediumPassword(value)) {
21686             strength = 2;
21687         } else if (this.ClientSideWeakPassword(value)) {
21688             strength = 1;
21689         } else {
21690             strength = 0;
21691         }
21692
21693         
21694         if (strength < 2) {
21695             //this.markInvalid(this.errors.TooWeak);
21696             this.errorMsg = this.errors.TooWeak;
21697             //return false;
21698         }
21699         
21700         
21701         console.log('strength2: ' + strength);
21702         
21703         //var pm = this.trigger.child('div/div/div').dom;
21704         
21705         var pm = this.trigger.child('div/div');
21706         pm.removeClass(this.meterClass);
21707         pm.addClass(this.meterClass[strength]);
21708                 
21709         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21710                 
21711         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21712         
21713         this.errorMsg = ''; 
21714         return true;
21715     },
21716     // private
21717     CharacterSetChecks: function (type)
21718     {
21719         this.type = type;
21720         this.fResult = false;
21721     },
21722     // private
21723     isctype: function (character, type)
21724     {
21725         switch (type) {  
21726             case this.kCapitalLetter:
21727                 if (character >= 'A' && character <= 'Z') {
21728                     return true;
21729                 }
21730                 break;
21731             
21732             case this.kSmallLetter:
21733                 if (character >= 'a' && character <= 'z') {
21734                     return true;
21735                 }
21736                 break;
21737             
21738             case this.kDigit:
21739                 if (character >= '0' && character <= '9') {
21740                     return true;
21741                 }
21742                 break;
21743             
21744             case this.kPunctuation:
21745                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21746                     return true;
21747                 }
21748                 break;
21749             
21750             default:
21751                 return false;
21752         }
21753
21754     },
21755     // private
21756     IsLongEnough: function (pwd, size)
21757     {
21758         return !(pwd == null || isNaN(size) || pwd.length < size);
21759     },
21760     // private
21761     SpansEnoughCharacterSets: function (word, nb)
21762     {
21763         if (!this.IsLongEnough(word, nb))
21764         {
21765             return false;
21766         }
21767
21768         var characterSetChecks = new Array(
21769             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21770             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21771         );
21772         
21773         for (var index = 0; index < word.length; ++index) {
21774             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21775                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21776                     characterSetChecks[nCharSet].fResult = true;
21777                     break;
21778                 }
21779             }
21780         }
21781
21782         var nCharSets = 0;
21783         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21784             if (characterSetChecks[nCharSet].fResult) {
21785                 ++nCharSets;
21786             }
21787         }
21788
21789         if (nCharSets < nb) {
21790             return false;
21791         }
21792         return true;
21793     },
21794     // private
21795     ClientSideStrongPassword: function (pwd)
21796     {
21797         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21798     },
21799     // private
21800     ClientSideMediumPassword: function (pwd)
21801     {
21802         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21803     },
21804     // private
21805     ClientSideWeakPassword: function (pwd)
21806     {
21807         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21808     }
21809           
21810 })//<script type="text/javascript">
21811
21812 /*
21813  * Based  Ext JS Library 1.1.1
21814  * Copyright(c) 2006-2007, Ext JS, LLC.
21815  * LGPL
21816  *
21817  */
21818  
21819 /**
21820  * @class Roo.HtmlEditorCore
21821  * @extends Roo.Component
21822  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21823  *
21824  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21825  */
21826
21827 Roo.HtmlEditorCore = function(config){
21828     
21829     
21830     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21831     
21832     
21833     this.addEvents({
21834         /**
21835          * @event initialize
21836          * Fires when the editor is fully initialized (including the iframe)
21837          * @param {Roo.HtmlEditorCore} this
21838          */
21839         initialize: true,
21840         /**
21841          * @event activate
21842          * Fires when the editor is first receives the focus. Any insertion must wait
21843          * until after this event.
21844          * @param {Roo.HtmlEditorCore} this
21845          */
21846         activate: true,
21847          /**
21848          * @event beforesync
21849          * Fires before the textarea is updated with content from the editor iframe. Return false
21850          * to cancel the sync.
21851          * @param {Roo.HtmlEditorCore} this
21852          * @param {String} html
21853          */
21854         beforesync: true,
21855          /**
21856          * @event beforepush
21857          * Fires before the iframe editor is updated with content from the textarea. Return false
21858          * to cancel the push.
21859          * @param {Roo.HtmlEditorCore} this
21860          * @param {String} html
21861          */
21862         beforepush: true,
21863          /**
21864          * @event sync
21865          * Fires when the textarea is updated with content from the editor iframe.
21866          * @param {Roo.HtmlEditorCore} this
21867          * @param {String} html
21868          */
21869         sync: true,
21870          /**
21871          * @event push
21872          * Fires when the iframe editor is updated with content from the textarea.
21873          * @param {Roo.HtmlEditorCore} this
21874          * @param {String} html
21875          */
21876         push: true,
21877         
21878         /**
21879          * @event editorevent
21880          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21881          * @param {Roo.HtmlEditorCore} this
21882          */
21883         editorevent: true
21884         
21885     });
21886     
21887     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21888     
21889     // defaults : white / black...
21890     this.applyBlacklists();
21891     
21892     
21893     
21894 };
21895
21896
21897 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21898
21899
21900      /**
21901      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21902      */
21903     
21904     owner : false,
21905     
21906      /**
21907      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21908      *                        Roo.resizable.
21909      */
21910     resizable : false,
21911      /**
21912      * @cfg {Number} height (in pixels)
21913      */   
21914     height: 300,
21915    /**
21916      * @cfg {Number} width (in pixels)
21917      */   
21918     width: 500,
21919     
21920     /**
21921      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21922      * 
21923      */
21924     stylesheets: false,
21925     
21926     // id of frame..
21927     frameId: false,
21928     
21929     // private properties
21930     validationEvent : false,
21931     deferHeight: true,
21932     initialized : false,
21933     activated : false,
21934     sourceEditMode : false,
21935     onFocus : Roo.emptyFn,
21936     iframePad:3,
21937     hideMode:'offsets',
21938     
21939     clearUp: true,
21940     
21941     // blacklist + whitelisted elements..
21942     black: false,
21943     white: false,
21944      
21945     bodyCls : '',
21946
21947     /**
21948      * Protected method that will not generally be called directly. It
21949      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21950      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21951      */
21952     getDocMarkup : function(){
21953         // body styles..
21954         var st = '';
21955         
21956         // inherit styels from page...?? 
21957         if (this.stylesheets === false) {
21958             
21959             Roo.get(document.head).select('style').each(function(node) {
21960                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21961             });
21962             
21963             Roo.get(document.head).select('link').each(function(node) { 
21964                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21965             });
21966             
21967         } else if (!this.stylesheets.length) {
21968                 // simple..
21969                 st = '<style type="text/css">' +
21970                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21971                    '</style>';
21972         } else { 
21973             st = '<style type="text/css">' +
21974                     this.stylesheets +
21975                 '</style>';
21976         }
21977         
21978         st +=  '<style type="text/css">' +
21979             'IMG { cursor: pointer } ' +
21980         '</style>';
21981
21982         var cls = 'roo-htmleditor-body';
21983         
21984         if(this.bodyCls.length){
21985             cls += ' ' + this.bodyCls;
21986         }
21987         
21988         return '<html><head>' + st  +
21989             //<style type="text/css">' +
21990             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21991             //'</style>' +
21992             ' </head><body class="' +  cls + '"></body></html>';
21993     },
21994
21995     // private
21996     onRender : function(ct, position)
21997     {
21998         var _t = this;
21999         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22000         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22001         
22002         
22003         this.el.dom.style.border = '0 none';
22004         this.el.dom.setAttribute('tabIndex', -1);
22005         this.el.addClass('x-hidden hide');
22006         
22007         
22008         
22009         if(Roo.isIE){ // fix IE 1px bogus margin
22010             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22011         }
22012        
22013         
22014         this.frameId = Roo.id();
22015         
22016          
22017         
22018         var iframe = this.owner.wrap.createChild({
22019             tag: 'iframe',
22020             cls: 'form-control', // bootstrap..
22021             id: this.frameId,
22022             name: this.frameId,
22023             frameBorder : 'no',
22024             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22025         }, this.el
22026         );
22027         
22028         
22029         this.iframe = iframe.dom;
22030
22031          this.assignDocWin();
22032         
22033         this.doc.designMode = 'on';
22034        
22035         this.doc.open();
22036         this.doc.write(this.getDocMarkup());
22037         this.doc.close();
22038
22039         
22040         var task = { // must defer to wait for browser to be ready
22041             run : function(){
22042                 //console.log("run task?" + this.doc.readyState);
22043                 this.assignDocWin();
22044                 if(this.doc.body || this.doc.readyState == 'complete'){
22045                     try {
22046                         this.doc.designMode="on";
22047                     } catch (e) {
22048                         return;
22049                     }
22050                     Roo.TaskMgr.stop(task);
22051                     this.initEditor.defer(10, this);
22052                 }
22053             },
22054             interval : 10,
22055             duration: 10000,
22056             scope: this
22057         };
22058         Roo.TaskMgr.start(task);
22059
22060     },
22061
22062     // private
22063     onResize : function(w, h)
22064     {
22065          Roo.log('resize: ' +w + ',' + h );
22066         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22067         if(!this.iframe){
22068             return;
22069         }
22070         if(typeof w == 'number'){
22071             
22072             this.iframe.style.width = w + 'px';
22073         }
22074         if(typeof h == 'number'){
22075             
22076             this.iframe.style.height = h + 'px';
22077             if(this.doc){
22078                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22079             }
22080         }
22081         
22082     },
22083
22084     /**
22085      * Toggles the editor between standard and source edit mode.
22086      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22087      */
22088     toggleSourceEdit : function(sourceEditMode){
22089         
22090         this.sourceEditMode = sourceEditMode === true;
22091         
22092         if(this.sourceEditMode){
22093  
22094             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22095             
22096         }else{
22097             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22098             //this.iframe.className = '';
22099             this.deferFocus();
22100         }
22101         //this.setSize(this.owner.wrap.getSize());
22102         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22103     },
22104
22105     
22106   
22107
22108     /**
22109      * Protected method that will not generally be called directly. If you need/want
22110      * custom HTML cleanup, this is the method you should override.
22111      * @param {String} html The HTML to be cleaned
22112      * return {String} The cleaned HTML
22113      */
22114     cleanHtml : function(html){
22115         html = String(html);
22116         if(html.length > 5){
22117             if(Roo.isSafari){ // strip safari nonsense
22118                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22119             }
22120         }
22121         if(html == '&nbsp;'){
22122             html = '';
22123         }
22124         return html;
22125     },
22126
22127     /**
22128      * HTML Editor -> Textarea
22129      * Protected method that will not generally be called directly. Syncs the contents
22130      * of the editor iframe with the textarea.
22131      */
22132     syncValue : function(){
22133         if(this.initialized){
22134             var bd = (this.doc.body || this.doc.documentElement);
22135             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22136             var html = bd.innerHTML;
22137             if(Roo.isSafari){
22138                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22139                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22140                 if(m && m[1]){
22141                     html = '<div style="'+m[0]+'">' + html + '</div>';
22142                 }
22143             }
22144             html = this.cleanHtml(html);
22145             // fix up the special chars.. normaly like back quotes in word...
22146             // however we do not want to do this with chinese..
22147             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22148                 var cc = b.charCodeAt();
22149                 if (
22150                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22151                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22152                     (cc >= 0xf900 && cc < 0xfb00 )
22153                 ) {
22154                         return b;
22155                 }
22156                 return "&#"+cc+";" 
22157             });
22158             if(this.owner.fireEvent('beforesync', this, html) !== false){
22159                 this.el.dom.value = html;
22160                 this.owner.fireEvent('sync', this, html);
22161             }
22162         }
22163     },
22164
22165     /**
22166      * Protected method that will not generally be called directly. Pushes the value of the textarea
22167      * into the iframe editor.
22168      */
22169     pushValue : function(){
22170         if(this.initialized){
22171             var v = this.el.dom.value.trim();
22172             
22173 //            if(v.length < 1){
22174 //                v = '&#160;';
22175 //            }
22176             
22177             if(this.owner.fireEvent('beforepush', this, v) !== false){
22178                 var d = (this.doc.body || this.doc.documentElement);
22179                 d.innerHTML = v;
22180                 this.cleanUpPaste();
22181                 this.el.dom.value = d.innerHTML;
22182                 this.owner.fireEvent('push', this, v);
22183             }
22184         }
22185     },
22186
22187     // private
22188     deferFocus : function(){
22189         this.focus.defer(10, this);
22190     },
22191
22192     // doc'ed in Field
22193     focus : function(){
22194         if(this.win && !this.sourceEditMode){
22195             this.win.focus();
22196         }else{
22197             this.el.focus();
22198         }
22199     },
22200     
22201     assignDocWin: function()
22202     {
22203         var iframe = this.iframe;
22204         
22205          if(Roo.isIE){
22206             this.doc = iframe.contentWindow.document;
22207             this.win = iframe.contentWindow;
22208         } else {
22209 //            if (!Roo.get(this.frameId)) {
22210 //                return;
22211 //            }
22212 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22213 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22214             
22215             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22216                 return;
22217             }
22218             
22219             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22220             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22221         }
22222     },
22223     
22224     // private
22225     initEditor : function(){
22226         //console.log("INIT EDITOR");
22227         this.assignDocWin();
22228         
22229         
22230         
22231         this.doc.designMode="on";
22232         this.doc.open();
22233         this.doc.write(this.getDocMarkup());
22234         this.doc.close();
22235         
22236         var dbody = (this.doc.body || this.doc.documentElement);
22237         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22238         // this copies styles from the containing element into thsi one..
22239         // not sure why we need all of this..
22240         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22241         
22242         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22243         //ss['background-attachment'] = 'fixed'; // w3c
22244         dbody.bgProperties = 'fixed'; // ie
22245         //Roo.DomHelper.applyStyles(dbody, ss);
22246         Roo.EventManager.on(this.doc, {
22247             //'mousedown': this.onEditorEvent,
22248             'mouseup': this.onEditorEvent,
22249             'dblclick': this.onEditorEvent,
22250             'click': this.onEditorEvent,
22251             'keyup': this.onEditorEvent,
22252             buffer:100,
22253             scope: this
22254         });
22255         if(Roo.isGecko){
22256             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22257         }
22258         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22259             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22260         }
22261         this.initialized = true;
22262
22263         this.owner.fireEvent('initialize', this);
22264         this.pushValue();
22265     },
22266
22267     // private
22268     onDestroy : function(){
22269         
22270         
22271         
22272         if(this.rendered){
22273             
22274             //for (var i =0; i < this.toolbars.length;i++) {
22275             //    // fixme - ask toolbars for heights?
22276             //    this.toolbars[i].onDestroy();
22277            // }
22278             
22279             //this.wrap.dom.innerHTML = '';
22280             //this.wrap.remove();
22281         }
22282     },
22283
22284     // private
22285     onFirstFocus : function(){
22286         
22287         this.assignDocWin();
22288         
22289         
22290         this.activated = true;
22291          
22292     
22293         if(Roo.isGecko){ // prevent silly gecko errors
22294             this.win.focus();
22295             var s = this.win.getSelection();
22296             if(!s.focusNode || s.focusNode.nodeType != 3){
22297                 var r = s.getRangeAt(0);
22298                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22299                 r.collapse(true);
22300                 this.deferFocus();
22301             }
22302             try{
22303                 this.execCmd('useCSS', true);
22304                 this.execCmd('styleWithCSS', false);
22305             }catch(e){}
22306         }
22307         this.owner.fireEvent('activate', this);
22308     },
22309
22310     // private
22311     adjustFont: function(btn){
22312         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22313         //if(Roo.isSafari){ // safari
22314         //    adjust *= 2;
22315        // }
22316         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22317         if(Roo.isSafari){ // safari
22318             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22319             v =  (v < 10) ? 10 : v;
22320             v =  (v > 48) ? 48 : v;
22321             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22322             
22323         }
22324         
22325         
22326         v = Math.max(1, v+adjust);
22327         
22328         this.execCmd('FontSize', v  );
22329     },
22330
22331     onEditorEvent : function(e)
22332     {
22333         this.owner.fireEvent('editorevent', this, e);
22334       //  this.updateToolbar();
22335         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22336     },
22337
22338     insertTag : function(tg)
22339     {
22340         // could be a bit smarter... -> wrap the current selected tRoo..
22341         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22342             
22343             range = this.createRange(this.getSelection());
22344             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22345             wrappingNode.appendChild(range.extractContents());
22346             range.insertNode(wrappingNode);
22347
22348             return;
22349             
22350             
22351             
22352         }
22353         this.execCmd("formatblock",   tg);
22354         
22355     },
22356     
22357     insertText : function(txt)
22358     {
22359         
22360         
22361         var range = this.createRange();
22362         range.deleteContents();
22363                //alert(Sender.getAttribute('label'));
22364                
22365         range.insertNode(this.doc.createTextNode(txt));
22366     } ,
22367     
22368      
22369
22370     /**
22371      * Executes a Midas editor command on the editor document and performs necessary focus and
22372      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22373      * @param {String} cmd The Midas command
22374      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22375      */
22376     relayCmd : function(cmd, value){
22377         this.win.focus();
22378         this.execCmd(cmd, value);
22379         this.owner.fireEvent('editorevent', this);
22380         //this.updateToolbar();
22381         this.owner.deferFocus();
22382     },
22383
22384     /**
22385      * Executes a Midas editor command directly on the editor document.
22386      * For visual commands, you should use {@link #relayCmd} instead.
22387      * <b>This should only be called after the editor is initialized.</b>
22388      * @param {String} cmd The Midas command
22389      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22390      */
22391     execCmd : function(cmd, value){
22392         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22393         this.syncValue();
22394     },
22395  
22396  
22397    
22398     /**
22399      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22400      * to insert tRoo.
22401      * @param {String} text | dom node.. 
22402      */
22403     insertAtCursor : function(text)
22404     {
22405         
22406         if(!this.activated){
22407             return;
22408         }
22409         /*
22410         if(Roo.isIE){
22411             this.win.focus();
22412             var r = this.doc.selection.createRange();
22413             if(r){
22414                 r.collapse(true);
22415                 r.pasteHTML(text);
22416                 this.syncValue();
22417                 this.deferFocus();
22418             
22419             }
22420             return;
22421         }
22422         */
22423         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22424             this.win.focus();
22425             
22426             
22427             // from jquery ui (MIT licenced)
22428             var range, node;
22429             var win = this.win;
22430             
22431             if (win.getSelection && win.getSelection().getRangeAt) {
22432                 range = win.getSelection().getRangeAt(0);
22433                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22434                 range.insertNode(node);
22435             } else if (win.document.selection && win.document.selection.createRange) {
22436                 // no firefox support
22437                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22438                 win.document.selection.createRange().pasteHTML(txt);
22439             } else {
22440                 // no firefox support
22441                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22442                 this.execCmd('InsertHTML', txt);
22443             } 
22444             
22445             this.syncValue();
22446             
22447             this.deferFocus();
22448         }
22449     },
22450  // private
22451     mozKeyPress : function(e){
22452         if(e.ctrlKey){
22453             var c = e.getCharCode(), cmd;
22454           
22455             if(c > 0){
22456                 c = String.fromCharCode(c).toLowerCase();
22457                 switch(c){
22458                     case 'b':
22459                         cmd = 'bold';
22460                         break;
22461                     case 'i':
22462                         cmd = 'italic';
22463                         break;
22464                     
22465                     case 'u':
22466                         cmd = 'underline';
22467                         break;
22468                     
22469                     case 'v':
22470                         this.cleanUpPaste.defer(100, this);
22471                         return;
22472                         
22473                 }
22474                 if(cmd){
22475                     this.win.focus();
22476                     this.execCmd(cmd);
22477                     this.deferFocus();
22478                     e.preventDefault();
22479                 }
22480                 
22481             }
22482         }
22483     },
22484
22485     // private
22486     fixKeys : function(){ // load time branching for fastest keydown performance
22487         if(Roo.isIE){
22488             return function(e){
22489                 var k = e.getKey(), r;
22490                 if(k == e.TAB){
22491                     e.stopEvent();
22492                     r = this.doc.selection.createRange();
22493                     if(r){
22494                         r.collapse(true);
22495                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22496                         this.deferFocus();
22497                     }
22498                     return;
22499                 }
22500                 
22501                 if(k == e.ENTER){
22502                     r = this.doc.selection.createRange();
22503                     if(r){
22504                         var target = r.parentElement();
22505                         if(!target || target.tagName.toLowerCase() != 'li'){
22506                             e.stopEvent();
22507                             r.pasteHTML('<br />');
22508                             r.collapse(false);
22509                             r.select();
22510                         }
22511                     }
22512                 }
22513                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22514                     this.cleanUpPaste.defer(100, this);
22515                     return;
22516                 }
22517                 
22518                 
22519             };
22520         }else if(Roo.isOpera){
22521             return function(e){
22522                 var k = e.getKey();
22523                 if(k == e.TAB){
22524                     e.stopEvent();
22525                     this.win.focus();
22526                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22527                     this.deferFocus();
22528                 }
22529                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22530                     this.cleanUpPaste.defer(100, this);
22531                     return;
22532                 }
22533                 
22534             };
22535         }else if(Roo.isSafari){
22536             return function(e){
22537                 var k = e.getKey();
22538                 
22539                 if(k == e.TAB){
22540                     e.stopEvent();
22541                     this.execCmd('InsertText','\t');
22542                     this.deferFocus();
22543                     return;
22544                 }
22545                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22546                     this.cleanUpPaste.defer(100, this);
22547                     return;
22548                 }
22549                 
22550              };
22551         }
22552     }(),
22553     
22554     getAllAncestors: function()
22555     {
22556         var p = this.getSelectedNode();
22557         var a = [];
22558         if (!p) {
22559             a.push(p); // push blank onto stack..
22560             p = this.getParentElement();
22561         }
22562         
22563         
22564         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22565             a.push(p);
22566             p = p.parentNode;
22567         }
22568         a.push(this.doc.body);
22569         return a;
22570     },
22571     lastSel : false,
22572     lastSelNode : false,
22573     
22574     
22575     getSelection : function() 
22576     {
22577         this.assignDocWin();
22578         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22579     },
22580     
22581     getSelectedNode: function() 
22582     {
22583         // this may only work on Gecko!!!
22584         
22585         // should we cache this!!!!
22586         
22587         
22588         
22589          
22590         var range = this.createRange(this.getSelection()).cloneRange();
22591         
22592         if (Roo.isIE) {
22593             var parent = range.parentElement();
22594             while (true) {
22595                 var testRange = range.duplicate();
22596                 testRange.moveToElementText(parent);
22597                 if (testRange.inRange(range)) {
22598                     break;
22599                 }
22600                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22601                     break;
22602                 }
22603                 parent = parent.parentElement;
22604             }
22605             return parent;
22606         }
22607         
22608         // is ancestor a text element.
22609         var ac =  range.commonAncestorContainer;
22610         if (ac.nodeType == 3) {
22611             ac = ac.parentNode;
22612         }
22613         
22614         var ar = ac.childNodes;
22615          
22616         var nodes = [];
22617         var other_nodes = [];
22618         var has_other_nodes = false;
22619         for (var i=0;i<ar.length;i++) {
22620             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22621                 continue;
22622             }
22623             // fullly contained node.
22624             
22625             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22626                 nodes.push(ar[i]);
22627                 continue;
22628             }
22629             
22630             // probably selected..
22631             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22632                 other_nodes.push(ar[i]);
22633                 continue;
22634             }
22635             // outer..
22636             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22637                 continue;
22638             }
22639             
22640             
22641             has_other_nodes = true;
22642         }
22643         if (!nodes.length && other_nodes.length) {
22644             nodes= other_nodes;
22645         }
22646         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22647             return false;
22648         }
22649         
22650         return nodes[0];
22651     },
22652     createRange: function(sel)
22653     {
22654         // this has strange effects when using with 
22655         // top toolbar - not sure if it's a great idea.
22656         //this.editor.contentWindow.focus();
22657         if (typeof sel != "undefined") {
22658             try {
22659                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22660             } catch(e) {
22661                 return this.doc.createRange();
22662             }
22663         } else {
22664             return this.doc.createRange();
22665         }
22666     },
22667     getParentElement: function()
22668     {
22669         
22670         this.assignDocWin();
22671         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22672         
22673         var range = this.createRange(sel);
22674          
22675         try {
22676             var p = range.commonAncestorContainer;
22677             while (p.nodeType == 3) { // text node
22678                 p = p.parentNode;
22679             }
22680             return p;
22681         } catch (e) {
22682             return null;
22683         }
22684     
22685     },
22686     /***
22687      *
22688      * Range intersection.. the hard stuff...
22689      *  '-1' = before
22690      *  '0' = hits..
22691      *  '1' = after.
22692      *         [ -- selected range --- ]
22693      *   [fail]                        [fail]
22694      *
22695      *    basically..
22696      *      if end is before start or  hits it. fail.
22697      *      if start is after end or hits it fail.
22698      *
22699      *   if either hits (but other is outside. - then it's not 
22700      *   
22701      *    
22702      **/
22703     
22704     
22705     // @see http://www.thismuchiknow.co.uk/?p=64.
22706     rangeIntersectsNode : function(range, node)
22707     {
22708         var nodeRange = node.ownerDocument.createRange();
22709         try {
22710             nodeRange.selectNode(node);
22711         } catch (e) {
22712             nodeRange.selectNodeContents(node);
22713         }
22714     
22715         var rangeStartRange = range.cloneRange();
22716         rangeStartRange.collapse(true);
22717     
22718         var rangeEndRange = range.cloneRange();
22719         rangeEndRange.collapse(false);
22720     
22721         var nodeStartRange = nodeRange.cloneRange();
22722         nodeStartRange.collapse(true);
22723     
22724         var nodeEndRange = nodeRange.cloneRange();
22725         nodeEndRange.collapse(false);
22726     
22727         return rangeStartRange.compareBoundaryPoints(
22728                  Range.START_TO_START, nodeEndRange) == -1 &&
22729                rangeEndRange.compareBoundaryPoints(
22730                  Range.START_TO_START, nodeStartRange) == 1;
22731         
22732          
22733     },
22734     rangeCompareNode : function(range, node)
22735     {
22736         var nodeRange = node.ownerDocument.createRange();
22737         try {
22738             nodeRange.selectNode(node);
22739         } catch (e) {
22740             nodeRange.selectNodeContents(node);
22741         }
22742         
22743         
22744         range.collapse(true);
22745     
22746         nodeRange.collapse(true);
22747      
22748         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22749         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22750          
22751         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22752         
22753         var nodeIsBefore   =  ss == 1;
22754         var nodeIsAfter    = ee == -1;
22755         
22756         if (nodeIsBefore && nodeIsAfter) {
22757             return 0; // outer
22758         }
22759         if (!nodeIsBefore && nodeIsAfter) {
22760             return 1; //right trailed.
22761         }
22762         
22763         if (nodeIsBefore && !nodeIsAfter) {
22764             return 2;  // left trailed.
22765         }
22766         // fully contined.
22767         return 3;
22768     },
22769
22770     // private? - in a new class?
22771     cleanUpPaste :  function()
22772     {
22773         // cleans up the whole document..
22774         Roo.log('cleanuppaste');
22775         
22776         this.cleanUpChildren(this.doc.body);
22777         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22778         if (clean != this.doc.body.innerHTML) {
22779             this.doc.body.innerHTML = clean;
22780         }
22781         
22782     },
22783     
22784     cleanWordChars : function(input) {// change the chars to hex code
22785         var he = Roo.HtmlEditorCore;
22786         
22787         var output = input;
22788         Roo.each(he.swapCodes, function(sw) { 
22789             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22790             
22791             output = output.replace(swapper, sw[1]);
22792         });
22793         
22794         return output;
22795     },
22796     
22797     
22798     cleanUpChildren : function (n)
22799     {
22800         if (!n.childNodes.length) {
22801             return;
22802         }
22803         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22804            this.cleanUpChild(n.childNodes[i]);
22805         }
22806     },
22807     
22808     
22809         
22810     
22811     cleanUpChild : function (node)
22812     {
22813         var ed = this;
22814         //console.log(node);
22815         if (node.nodeName == "#text") {
22816             // clean up silly Windows -- stuff?
22817             return; 
22818         }
22819         if (node.nodeName == "#comment") {
22820             node.parentNode.removeChild(node);
22821             // clean up silly Windows -- stuff?
22822             return; 
22823         }
22824         var lcname = node.tagName.toLowerCase();
22825         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22826         // whitelist of tags..
22827         
22828         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22829             // remove node.
22830             node.parentNode.removeChild(node);
22831             return;
22832             
22833         }
22834         
22835         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22836         
22837         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22838         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22839         
22840         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22841         //    remove_keep_children = true;
22842         //}
22843         
22844         if (remove_keep_children) {
22845             this.cleanUpChildren(node);
22846             // inserts everything just before this node...
22847             while (node.childNodes.length) {
22848                 var cn = node.childNodes[0];
22849                 node.removeChild(cn);
22850                 node.parentNode.insertBefore(cn, node);
22851             }
22852             node.parentNode.removeChild(node);
22853             return;
22854         }
22855         
22856         if (!node.attributes || !node.attributes.length) {
22857             this.cleanUpChildren(node);
22858             return;
22859         }
22860         
22861         function cleanAttr(n,v)
22862         {
22863             
22864             if (v.match(/^\./) || v.match(/^\//)) {
22865                 return;
22866             }
22867             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22868                 return;
22869             }
22870             if (v.match(/^#/)) {
22871                 return;
22872             }
22873 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22874             node.removeAttribute(n);
22875             
22876         }
22877         
22878         var cwhite = this.cwhite;
22879         var cblack = this.cblack;
22880             
22881         function cleanStyle(n,v)
22882         {
22883             if (v.match(/expression/)) { //XSS?? should we even bother..
22884                 node.removeAttribute(n);
22885                 return;
22886             }
22887             
22888             var parts = v.split(/;/);
22889             var clean = [];
22890             
22891             Roo.each(parts, function(p) {
22892                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22893                 if (!p.length) {
22894                     return true;
22895                 }
22896                 var l = p.split(':').shift().replace(/\s+/g,'');
22897                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22898                 
22899                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22900 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22901                     //node.removeAttribute(n);
22902                     return true;
22903                 }
22904                 //Roo.log()
22905                 // only allow 'c whitelisted system attributes'
22906                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22907 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22908                     //node.removeAttribute(n);
22909                     return true;
22910                 }
22911                 
22912                 
22913                  
22914                 
22915                 clean.push(p);
22916                 return true;
22917             });
22918             if (clean.length) { 
22919                 node.setAttribute(n, clean.join(';'));
22920             } else {
22921                 node.removeAttribute(n);
22922             }
22923             
22924         }
22925         
22926         
22927         for (var i = node.attributes.length-1; i > -1 ; i--) {
22928             var a = node.attributes[i];
22929             //console.log(a);
22930             
22931             if (a.name.toLowerCase().substr(0,2)=='on')  {
22932                 node.removeAttribute(a.name);
22933                 continue;
22934             }
22935             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22936                 node.removeAttribute(a.name);
22937                 continue;
22938             }
22939             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22940                 cleanAttr(a.name,a.value); // fixme..
22941                 continue;
22942             }
22943             if (a.name == 'style') {
22944                 cleanStyle(a.name,a.value);
22945                 continue;
22946             }
22947             /// clean up MS crap..
22948             // tecnically this should be a list of valid class'es..
22949             
22950             
22951             if (a.name == 'class') {
22952                 if (a.value.match(/^Mso/)) {
22953                     node.className = '';
22954                 }
22955                 
22956                 if (a.value.match(/^body$/)) {
22957                     node.className = '';
22958                 }
22959                 continue;
22960             }
22961             
22962             // style cleanup!?
22963             // class cleanup?
22964             
22965         }
22966         
22967         
22968         this.cleanUpChildren(node);
22969         
22970         
22971     },
22972     
22973     /**
22974      * Clean up MS wordisms...
22975      */
22976     cleanWord : function(node)
22977     {
22978         
22979         
22980         if (!node) {
22981             this.cleanWord(this.doc.body);
22982             return;
22983         }
22984         if (node.nodeName == "#text") {
22985             // clean up silly Windows -- stuff?
22986             return; 
22987         }
22988         if (node.nodeName == "#comment") {
22989             node.parentNode.removeChild(node);
22990             // clean up silly Windows -- stuff?
22991             return; 
22992         }
22993         
22994         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22995             node.parentNode.removeChild(node);
22996             return;
22997         }
22998         
22999         // remove - but keep children..
23000         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23001             while (node.childNodes.length) {
23002                 var cn = node.childNodes[0];
23003                 node.removeChild(cn);
23004                 node.parentNode.insertBefore(cn, node);
23005             }
23006             node.parentNode.removeChild(node);
23007             this.iterateChildren(node, this.cleanWord);
23008             return;
23009         }
23010         // clean styles
23011         if (node.className.length) {
23012             
23013             var cn = node.className.split(/\W+/);
23014             var cna = [];
23015             Roo.each(cn, function(cls) {
23016                 if (cls.match(/Mso[a-zA-Z]+/)) {
23017                     return;
23018                 }
23019                 cna.push(cls);
23020             });
23021             node.className = cna.length ? cna.join(' ') : '';
23022             if (!cna.length) {
23023                 node.removeAttribute("class");
23024             }
23025         }
23026         
23027         if (node.hasAttribute("lang")) {
23028             node.removeAttribute("lang");
23029         }
23030         
23031         if (node.hasAttribute("style")) {
23032             
23033             var styles = node.getAttribute("style").split(";");
23034             var nstyle = [];
23035             Roo.each(styles, function(s) {
23036                 if (!s.match(/:/)) {
23037                     return;
23038                 }
23039                 var kv = s.split(":");
23040                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23041                     return;
23042                 }
23043                 // what ever is left... we allow.
23044                 nstyle.push(s);
23045             });
23046             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23047             if (!nstyle.length) {
23048                 node.removeAttribute('style');
23049             }
23050         }
23051         this.iterateChildren(node, this.cleanWord);
23052         
23053         
23054         
23055     },
23056     /**
23057      * iterateChildren of a Node, calling fn each time, using this as the scole..
23058      * @param {DomNode} node node to iterate children of.
23059      * @param {Function} fn method of this class to call on each item.
23060      */
23061     iterateChildren : function(node, fn)
23062     {
23063         if (!node.childNodes.length) {
23064                 return;
23065         }
23066         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23067            fn.call(this, node.childNodes[i])
23068         }
23069     },
23070     
23071     
23072     /**
23073      * cleanTableWidths.
23074      *
23075      * Quite often pasting from word etc.. results in tables with column and widths.
23076      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23077      *
23078      */
23079     cleanTableWidths : function(node)
23080     {
23081          
23082          
23083         if (!node) {
23084             this.cleanTableWidths(this.doc.body);
23085             return;
23086         }
23087         
23088         // ignore list...
23089         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23090             return; 
23091         }
23092         Roo.log(node.tagName);
23093         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23094             this.iterateChildren(node, this.cleanTableWidths);
23095             return;
23096         }
23097         if (node.hasAttribute('width')) {
23098             node.removeAttribute('width');
23099         }
23100         
23101          
23102         if (node.hasAttribute("style")) {
23103             // pretty basic...
23104             
23105             var styles = node.getAttribute("style").split(";");
23106             var nstyle = [];
23107             Roo.each(styles, function(s) {
23108                 if (!s.match(/:/)) {
23109                     return;
23110                 }
23111                 var kv = s.split(":");
23112                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23113                     return;
23114                 }
23115                 // what ever is left... we allow.
23116                 nstyle.push(s);
23117             });
23118             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23119             if (!nstyle.length) {
23120                 node.removeAttribute('style');
23121             }
23122         }
23123         
23124         this.iterateChildren(node, this.cleanTableWidths);
23125         
23126         
23127     },
23128     
23129     
23130     
23131     
23132     domToHTML : function(currentElement, depth, nopadtext) {
23133         
23134         depth = depth || 0;
23135         nopadtext = nopadtext || false;
23136     
23137         if (!currentElement) {
23138             return this.domToHTML(this.doc.body);
23139         }
23140         
23141         //Roo.log(currentElement);
23142         var j;
23143         var allText = false;
23144         var nodeName = currentElement.nodeName;
23145         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23146         
23147         if  (nodeName == '#text') {
23148             
23149             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23150         }
23151         
23152         
23153         var ret = '';
23154         if (nodeName != 'BODY') {
23155              
23156             var i = 0;
23157             // Prints the node tagName, such as <A>, <IMG>, etc
23158             if (tagName) {
23159                 var attr = [];
23160                 for(i = 0; i < currentElement.attributes.length;i++) {
23161                     // quoting?
23162                     var aname = currentElement.attributes.item(i).name;
23163                     if (!currentElement.attributes.item(i).value.length) {
23164                         continue;
23165                     }
23166                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23167                 }
23168                 
23169                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23170             } 
23171             else {
23172                 
23173                 // eack
23174             }
23175         } else {
23176             tagName = false;
23177         }
23178         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23179             return ret;
23180         }
23181         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23182             nopadtext = true;
23183         }
23184         
23185         
23186         // Traverse the tree
23187         i = 0;
23188         var currentElementChild = currentElement.childNodes.item(i);
23189         var allText = true;
23190         var innerHTML  = '';
23191         lastnode = '';
23192         while (currentElementChild) {
23193             // Formatting code (indent the tree so it looks nice on the screen)
23194             var nopad = nopadtext;
23195             if (lastnode == 'SPAN') {
23196                 nopad  = true;
23197             }
23198             // text
23199             if  (currentElementChild.nodeName == '#text') {
23200                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23201                 toadd = nopadtext ? toadd : toadd.trim();
23202                 if (!nopad && toadd.length > 80) {
23203                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23204                 }
23205                 innerHTML  += toadd;
23206                 
23207                 i++;
23208                 currentElementChild = currentElement.childNodes.item(i);
23209                 lastNode = '';
23210                 continue;
23211             }
23212             allText = false;
23213             
23214             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23215                 
23216             // Recursively traverse the tree structure of the child node
23217             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23218             lastnode = currentElementChild.nodeName;
23219             i++;
23220             currentElementChild=currentElement.childNodes.item(i);
23221         }
23222         
23223         ret += innerHTML;
23224         
23225         if (!allText) {
23226                 // The remaining code is mostly for formatting the tree
23227             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23228         }
23229         
23230         
23231         if (tagName) {
23232             ret+= "</"+tagName+">";
23233         }
23234         return ret;
23235         
23236     },
23237         
23238     applyBlacklists : function()
23239     {
23240         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23241         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23242         
23243         this.white = [];
23244         this.black = [];
23245         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23246             if (b.indexOf(tag) > -1) {
23247                 return;
23248             }
23249             this.white.push(tag);
23250             
23251         }, this);
23252         
23253         Roo.each(w, function(tag) {
23254             if (b.indexOf(tag) > -1) {
23255                 return;
23256             }
23257             if (this.white.indexOf(tag) > -1) {
23258                 return;
23259             }
23260             this.white.push(tag);
23261             
23262         }, this);
23263         
23264         
23265         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23266             if (w.indexOf(tag) > -1) {
23267                 return;
23268             }
23269             this.black.push(tag);
23270             
23271         }, this);
23272         
23273         Roo.each(b, function(tag) {
23274             if (w.indexOf(tag) > -1) {
23275                 return;
23276             }
23277             if (this.black.indexOf(tag) > -1) {
23278                 return;
23279             }
23280             this.black.push(tag);
23281             
23282         }, this);
23283         
23284         
23285         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23286         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23287         
23288         this.cwhite = [];
23289         this.cblack = [];
23290         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23291             if (b.indexOf(tag) > -1) {
23292                 return;
23293             }
23294             this.cwhite.push(tag);
23295             
23296         }, this);
23297         
23298         Roo.each(w, function(tag) {
23299             if (b.indexOf(tag) > -1) {
23300                 return;
23301             }
23302             if (this.cwhite.indexOf(tag) > -1) {
23303                 return;
23304             }
23305             this.cwhite.push(tag);
23306             
23307         }, this);
23308         
23309         
23310         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23311             if (w.indexOf(tag) > -1) {
23312                 return;
23313             }
23314             this.cblack.push(tag);
23315             
23316         }, this);
23317         
23318         Roo.each(b, function(tag) {
23319             if (w.indexOf(tag) > -1) {
23320                 return;
23321             }
23322             if (this.cblack.indexOf(tag) > -1) {
23323                 return;
23324             }
23325             this.cblack.push(tag);
23326             
23327         }, this);
23328     },
23329     
23330     setStylesheets : function(stylesheets)
23331     {
23332         if(typeof(stylesheets) == 'string'){
23333             Roo.get(this.iframe.contentDocument.head).createChild({
23334                 tag : 'link',
23335                 rel : 'stylesheet',
23336                 type : 'text/css',
23337                 href : stylesheets
23338             });
23339             
23340             return;
23341         }
23342         var _this = this;
23343      
23344         Roo.each(stylesheets, function(s) {
23345             if(!s.length){
23346                 return;
23347             }
23348             
23349             Roo.get(_this.iframe.contentDocument.head).createChild({
23350                 tag : 'link',
23351                 rel : 'stylesheet',
23352                 type : 'text/css',
23353                 href : s
23354             });
23355         });
23356
23357         
23358     },
23359     
23360     removeStylesheets : function()
23361     {
23362         var _this = this;
23363         
23364         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23365             s.remove();
23366         });
23367     },
23368     
23369     setStyle : function(style)
23370     {
23371         Roo.get(this.iframe.contentDocument.head).createChild({
23372             tag : 'style',
23373             type : 'text/css',
23374             html : style
23375         });
23376
23377         return;
23378     }
23379     
23380     // hide stuff that is not compatible
23381     /**
23382      * @event blur
23383      * @hide
23384      */
23385     /**
23386      * @event change
23387      * @hide
23388      */
23389     /**
23390      * @event focus
23391      * @hide
23392      */
23393     /**
23394      * @event specialkey
23395      * @hide
23396      */
23397     /**
23398      * @cfg {String} fieldClass @hide
23399      */
23400     /**
23401      * @cfg {String} focusClass @hide
23402      */
23403     /**
23404      * @cfg {String} autoCreate @hide
23405      */
23406     /**
23407      * @cfg {String} inputType @hide
23408      */
23409     /**
23410      * @cfg {String} invalidClass @hide
23411      */
23412     /**
23413      * @cfg {String} invalidText @hide
23414      */
23415     /**
23416      * @cfg {String} msgFx @hide
23417      */
23418     /**
23419      * @cfg {String} validateOnBlur @hide
23420      */
23421 });
23422
23423 Roo.HtmlEditorCore.white = [
23424         'area', 'br', 'img', 'input', 'hr', 'wbr',
23425         
23426        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23427        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23428        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23429        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23430        'table',   'ul',         'xmp', 
23431        
23432        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23433       'thead',   'tr', 
23434      
23435       'dir', 'menu', 'ol', 'ul', 'dl',
23436        
23437       'embed',  'object'
23438 ];
23439
23440
23441 Roo.HtmlEditorCore.black = [
23442     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23443         'applet', // 
23444         'base',   'basefont', 'bgsound', 'blink',  'body', 
23445         'frame',  'frameset', 'head',    'html',   'ilayer', 
23446         'iframe', 'layer',  'link',     'meta',    'object',   
23447         'script', 'style' ,'title',  'xml' // clean later..
23448 ];
23449 Roo.HtmlEditorCore.clean = [
23450     'script', 'style', 'title', 'xml'
23451 ];
23452 Roo.HtmlEditorCore.remove = [
23453     'font'
23454 ];
23455 // attributes..
23456
23457 Roo.HtmlEditorCore.ablack = [
23458     'on'
23459 ];
23460     
23461 Roo.HtmlEditorCore.aclean = [ 
23462     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23463 ];
23464
23465 // protocols..
23466 Roo.HtmlEditorCore.pwhite= [
23467         'http',  'https',  'mailto'
23468 ];
23469
23470 // white listed style attributes.
23471 Roo.HtmlEditorCore.cwhite= [
23472       //  'text-align', /// default is to allow most things..
23473       
23474          
23475 //        'font-size'//??
23476 ];
23477
23478 // black listed style attributes.
23479 Roo.HtmlEditorCore.cblack= [
23480       //  'font-size' -- this can be set by the project 
23481 ];
23482
23483
23484 Roo.HtmlEditorCore.swapCodes   =[ 
23485     [    8211, "--" ], 
23486     [    8212, "--" ], 
23487     [    8216,  "'" ],  
23488     [    8217, "'" ],  
23489     [    8220, '"' ],  
23490     [    8221, '"' ],  
23491     [    8226, "*" ],  
23492     [    8230, "..." ]
23493 ]; 
23494
23495     /*
23496  * - LGPL
23497  *
23498  * HtmlEditor
23499  * 
23500  */
23501
23502 /**
23503  * @class Roo.bootstrap.HtmlEditor
23504  * @extends Roo.bootstrap.TextArea
23505  * Bootstrap HtmlEditor class
23506
23507  * @constructor
23508  * Create a new HtmlEditor
23509  * @param {Object} config The config object
23510  */
23511
23512 Roo.bootstrap.HtmlEditor = function(config){
23513     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23514     if (!this.toolbars) {
23515         this.toolbars = [];
23516     }
23517     
23518     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23519     this.addEvents({
23520             /**
23521              * @event initialize
23522              * Fires when the editor is fully initialized (including the iframe)
23523              * @param {HtmlEditor} this
23524              */
23525             initialize: true,
23526             /**
23527              * @event activate
23528              * Fires when the editor is first receives the focus. Any insertion must wait
23529              * until after this event.
23530              * @param {HtmlEditor} this
23531              */
23532             activate: true,
23533              /**
23534              * @event beforesync
23535              * Fires before the textarea is updated with content from the editor iframe. Return false
23536              * to cancel the sync.
23537              * @param {HtmlEditor} this
23538              * @param {String} html
23539              */
23540             beforesync: true,
23541              /**
23542              * @event beforepush
23543              * Fires before the iframe editor is updated with content from the textarea. Return false
23544              * to cancel the push.
23545              * @param {HtmlEditor} this
23546              * @param {String} html
23547              */
23548             beforepush: true,
23549              /**
23550              * @event sync
23551              * Fires when the textarea is updated with content from the editor iframe.
23552              * @param {HtmlEditor} this
23553              * @param {String} html
23554              */
23555             sync: true,
23556              /**
23557              * @event push
23558              * Fires when the iframe editor is updated with content from the textarea.
23559              * @param {HtmlEditor} this
23560              * @param {String} html
23561              */
23562             push: true,
23563              /**
23564              * @event editmodechange
23565              * Fires when the editor switches edit modes
23566              * @param {HtmlEditor} this
23567              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23568              */
23569             editmodechange: true,
23570             /**
23571              * @event editorevent
23572              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23573              * @param {HtmlEditor} this
23574              */
23575             editorevent: true,
23576             /**
23577              * @event firstfocus
23578              * Fires when on first focus - needed by toolbars..
23579              * @param {HtmlEditor} this
23580              */
23581             firstfocus: true,
23582             /**
23583              * @event autosave
23584              * Auto save the htmlEditor value as a file into Events
23585              * @param {HtmlEditor} this
23586              */
23587             autosave: true,
23588             /**
23589              * @event savedpreview
23590              * preview the saved version of htmlEditor
23591              * @param {HtmlEditor} this
23592              */
23593             savedpreview: true
23594         });
23595 };
23596
23597
23598 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23599     
23600     
23601       /**
23602      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23603      */
23604     toolbars : false,
23605     
23606      /**
23607     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23608     */
23609     btns : [],
23610    
23611      /**
23612      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23613      *                        Roo.resizable.
23614      */
23615     resizable : false,
23616      /**
23617      * @cfg {Number} height (in pixels)
23618      */   
23619     height: 300,
23620    /**
23621      * @cfg {Number} width (in pixels)
23622      */   
23623     width: false,
23624     
23625     /**
23626      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23627      * 
23628      */
23629     stylesheets: false,
23630     
23631     // id of frame..
23632     frameId: false,
23633     
23634     // private properties
23635     validationEvent : false,
23636     deferHeight: true,
23637     initialized : false,
23638     activated : false,
23639     
23640     onFocus : Roo.emptyFn,
23641     iframePad:3,
23642     hideMode:'offsets',
23643     
23644     tbContainer : false,
23645     
23646     bodyCls : '',
23647     
23648     toolbarContainer :function() {
23649         return this.wrap.select('.x-html-editor-tb',true).first();
23650     },
23651
23652     /**
23653      * Protected method that will not generally be called directly. It
23654      * is called when the editor creates its toolbar. Override this method if you need to
23655      * add custom toolbar buttons.
23656      * @param {HtmlEditor} editor
23657      */
23658     createToolbar : function(){
23659         Roo.log('renewing');
23660         Roo.log("create toolbars");
23661         
23662         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23663         this.toolbars[0].render(this.toolbarContainer());
23664         
23665         return;
23666         
23667 //        if (!editor.toolbars || !editor.toolbars.length) {
23668 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23669 //        }
23670 //        
23671 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23672 //            editor.toolbars[i] = Roo.factory(
23673 //                    typeof(editor.toolbars[i]) == 'string' ?
23674 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23675 //                Roo.bootstrap.HtmlEditor);
23676 //            editor.toolbars[i].init(editor);
23677 //        }
23678     },
23679
23680      
23681     // private
23682     onRender : function(ct, position)
23683     {
23684        // Roo.log("Call onRender: " + this.xtype);
23685         var _t = this;
23686         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23687       
23688         this.wrap = this.inputEl().wrap({
23689             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23690         });
23691         
23692         this.editorcore.onRender(ct, position);
23693          
23694         if (this.resizable) {
23695             this.resizeEl = new Roo.Resizable(this.wrap, {
23696                 pinned : true,
23697                 wrap: true,
23698                 dynamic : true,
23699                 minHeight : this.height,
23700                 height: this.height,
23701                 handles : this.resizable,
23702                 width: this.width,
23703                 listeners : {
23704                     resize : function(r, w, h) {
23705                         _t.onResize(w,h); // -something
23706                     }
23707                 }
23708             });
23709             
23710         }
23711         this.createToolbar(this);
23712        
23713         
23714         if(!this.width && this.resizable){
23715             this.setSize(this.wrap.getSize());
23716         }
23717         if (this.resizeEl) {
23718             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23719             // should trigger onReize..
23720         }
23721         
23722     },
23723
23724     // private
23725     onResize : function(w, h)
23726     {
23727         Roo.log('resize: ' +w + ',' + h );
23728         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23729         var ew = false;
23730         var eh = false;
23731         
23732         if(this.inputEl() ){
23733             if(typeof w == 'number'){
23734                 var aw = w - this.wrap.getFrameWidth('lr');
23735                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23736                 ew = aw;
23737             }
23738             if(typeof h == 'number'){
23739                  var tbh = -11;  // fixme it needs to tool bar size!
23740                 for (var i =0; i < this.toolbars.length;i++) {
23741                     // fixme - ask toolbars for heights?
23742                     tbh += this.toolbars[i].el.getHeight();
23743                     //if (this.toolbars[i].footer) {
23744                     //    tbh += this.toolbars[i].footer.el.getHeight();
23745                     //}
23746                 }
23747               
23748                 
23749                 
23750                 
23751                 
23752                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23753                 ah -= 5; // knock a few pixes off for look..
23754                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23755                 var eh = ah;
23756             }
23757         }
23758         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23759         this.editorcore.onResize(ew,eh);
23760         
23761     },
23762
23763     /**
23764      * Toggles the editor between standard and source edit mode.
23765      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23766      */
23767     toggleSourceEdit : function(sourceEditMode)
23768     {
23769         this.editorcore.toggleSourceEdit(sourceEditMode);
23770         
23771         if(this.editorcore.sourceEditMode){
23772             Roo.log('editor - showing textarea');
23773             
23774 //            Roo.log('in');
23775 //            Roo.log(this.syncValue());
23776             this.syncValue();
23777             this.inputEl().removeClass(['hide', 'x-hidden']);
23778             this.inputEl().dom.removeAttribute('tabIndex');
23779             this.inputEl().focus();
23780         }else{
23781             Roo.log('editor - hiding textarea');
23782 //            Roo.log('out')
23783 //            Roo.log(this.pushValue()); 
23784             this.pushValue();
23785             
23786             this.inputEl().addClass(['hide', 'x-hidden']);
23787             this.inputEl().dom.setAttribute('tabIndex', -1);
23788             //this.deferFocus();
23789         }
23790          
23791         if(this.resizable){
23792             this.setSize(this.wrap.getSize());
23793         }
23794         
23795         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23796     },
23797  
23798     // private (for BoxComponent)
23799     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23800
23801     // private (for BoxComponent)
23802     getResizeEl : function(){
23803         return this.wrap;
23804     },
23805
23806     // private (for BoxComponent)
23807     getPositionEl : function(){
23808         return this.wrap;
23809     },
23810
23811     // private
23812     initEvents : function(){
23813         this.originalValue = this.getValue();
23814     },
23815
23816 //    /**
23817 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23818 //     * @method
23819 //     */
23820 //    markInvalid : Roo.emptyFn,
23821 //    /**
23822 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23823 //     * @method
23824 //     */
23825 //    clearInvalid : Roo.emptyFn,
23826
23827     setValue : function(v){
23828         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23829         this.editorcore.pushValue();
23830     },
23831
23832      
23833     // private
23834     deferFocus : function(){
23835         this.focus.defer(10, this);
23836     },
23837
23838     // doc'ed in Field
23839     focus : function(){
23840         this.editorcore.focus();
23841         
23842     },
23843       
23844
23845     // private
23846     onDestroy : function(){
23847         
23848         
23849         
23850         if(this.rendered){
23851             
23852             for (var i =0; i < this.toolbars.length;i++) {
23853                 // fixme - ask toolbars for heights?
23854                 this.toolbars[i].onDestroy();
23855             }
23856             
23857             this.wrap.dom.innerHTML = '';
23858             this.wrap.remove();
23859         }
23860     },
23861
23862     // private
23863     onFirstFocus : function(){
23864         //Roo.log("onFirstFocus");
23865         this.editorcore.onFirstFocus();
23866          for (var i =0; i < this.toolbars.length;i++) {
23867             this.toolbars[i].onFirstFocus();
23868         }
23869         
23870     },
23871     
23872     // private
23873     syncValue : function()
23874     {   
23875         this.editorcore.syncValue();
23876     },
23877     
23878     pushValue : function()
23879     {   
23880         this.editorcore.pushValue();
23881     }
23882      
23883     
23884     // hide stuff that is not compatible
23885     /**
23886      * @event blur
23887      * @hide
23888      */
23889     /**
23890      * @event change
23891      * @hide
23892      */
23893     /**
23894      * @event focus
23895      * @hide
23896      */
23897     /**
23898      * @event specialkey
23899      * @hide
23900      */
23901     /**
23902      * @cfg {String} fieldClass @hide
23903      */
23904     /**
23905      * @cfg {String} focusClass @hide
23906      */
23907     /**
23908      * @cfg {String} autoCreate @hide
23909      */
23910     /**
23911      * @cfg {String} inputType @hide
23912      */
23913     /**
23914      * @cfg {String} invalidClass @hide
23915      */
23916     /**
23917      * @cfg {String} invalidText @hide
23918      */
23919     /**
23920      * @cfg {String} msgFx @hide
23921      */
23922     /**
23923      * @cfg {String} validateOnBlur @hide
23924      */
23925 });
23926  
23927     
23928    
23929    
23930    
23931       
23932 Roo.namespace('Roo.bootstrap.htmleditor');
23933 /**
23934  * @class Roo.bootstrap.HtmlEditorToolbar1
23935  * Basic Toolbar
23936  * 
23937  * Usage:
23938  *
23939  new Roo.bootstrap.HtmlEditor({
23940     ....
23941     toolbars : [
23942         new Roo.bootstrap.HtmlEditorToolbar1({
23943             disable : { fonts: 1 , format: 1, ..., ... , ...],
23944             btns : [ .... ]
23945         })
23946     }
23947      
23948  * 
23949  * @cfg {Object} disable List of elements to disable..
23950  * @cfg {Array} btns List of additional buttons.
23951  * 
23952  * 
23953  * NEEDS Extra CSS? 
23954  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23955  */
23956  
23957 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23958 {
23959     
23960     Roo.apply(this, config);
23961     
23962     // default disabled, based on 'good practice'..
23963     this.disable = this.disable || {};
23964     Roo.applyIf(this.disable, {
23965         fontSize : true,
23966         colors : true,
23967         specialElements : true
23968     });
23969     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23970     
23971     this.editor = config.editor;
23972     this.editorcore = config.editor.editorcore;
23973     
23974     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23975     
23976     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23977     // dont call parent... till later.
23978 }
23979 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23980      
23981     bar : true,
23982     
23983     editor : false,
23984     editorcore : false,
23985     
23986     
23987     formats : [
23988         "p" ,  
23989         "h1","h2","h3","h4","h5","h6", 
23990         "pre", "code", 
23991         "abbr", "acronym", "address", "cite", "samp", "var",
23992         'div','span'
23993     ],
23994     
23995     onRender : function(ct, position)
23996     {
23997        // Roo.log("Call onRender: " + this.xtype);
23998         
23999        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24000        Roo.log(this.el);
24001        this.el.dom.style.marginBottom = '0';
24002        var _this = this;
24003        var editorcore = this.editorcore;
24004        var editor= this.editor;
24005        
24006        var children = [];
24007        var btn = function(id,cmd , toggle, handler, html){
24008        
24009             var  event = toggle ? 'toggle' : 'click';
24010        
24011             var a = {
24012                 size : 'sm',
24013                 xtype: 'Button',
24014                 xns: Roo.bootstrap,
24015                 //glyphicon : id,
24016                 fa: id,
24017                 cmd : id || cmd,
24018                 enableToggle:toggle !== false,
24019                 html : html || '',
24020                 pressed : toggle ? false : null,
24021                 listeners : {}
24022             };
24023             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24024                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24025             };
24026             children.push(a);
24027             return a;
24028        }
24029        
24030     //    var cb_box = function...
24031         
24032         var style = {
24033                 xtype: 'Button',
24034                 size : 'sm',
24035                 xns: Roo.bootstrap,
24036                 fa : 'font',
24037                 //html : 'submit'
24038                 menu : {
24039                     xtype: 'Menu',
24040                     xns: Roo.bootstrap,
24041                     items:  []
24042                 }
24043         };
24044         Roo.each(this.formats, function(f) {
24045             style.menu.items.push({
24046                 xtype :'MenuItem',
24047                 xns: Roo.bootstrap,
24048                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24049                 tagname : f,
24050                 listeners : {
24051                     click : function()
24052                     {
24053                         editorcore.insertTag(this.tagname);
24054                         editor.focus();
24055                     }
24056                 }
24057                 
24058             });
24059         });
24060         children.push(style);   
24061         
24062         btn('bold',false,true);
24063         btn('italic',false,true);
24064         btn('align-left', 'justifyleft',true);
24065         btn('align-center', 'justifycenter',true);
24066         btn('align-right' , 'justifyright',true);
24067         btn('link', false, false, function(btn) {
24068             //Roo.log("create link?");
24069             var url = prompt(this.createLinkText, this.defaultLinkValue);
24070             if(url && url != 'http:/'+'/'){
24071                 this.editorcore.relayCmd('createlink', url);
24072             }
24073         }),
24074         btn('list','insertunorderedlist',true);
24075         btn('pencil', false,true, function(btn){
24076                 Roo.log(this);
24077                 this.toggleSourceEdit(btn.pressed);
24078         });
24079         
24080         if (this.editor.btns.length > 0) {
24081             for (var i = 0; i<this.editor.btns.length; i++) {
24082                 children.push(this.editor.btns[i]);
24083             }
24084         }
24085         
24086         /*
24087         var cog = {
24088                 xtype: 'Button',
24089                 size : 'sm',
24090                 xns: Roo.bootstrap,
24091                 glyphicon : 'cog',
24092                 //html : 'submit'
24093                 menu : {
24094                     xtype: 'Menu',
24095                     xns: Roo.bootstrap,
24096                     items:  []
24097                 }
24098         };
24099         
24100         cog.menu.items.push({
24101             xtype :'MenuItem',
24102             xns: Roo.bootstrap,
24103             html : Clean styles,
24104             tagname : f,
24105             listeners : {
24106                 click : function()
24107                 {
24108                     editorcore.insertTag(this.tagname);
24109                     editor.focus();
24110                 }
24111             }
24112             
24113         });
24114        */
24115         
24116          
24117        this.xtype = 'NavSimplebar';
24118         
24119         for(var i=0;i< children.length;i++) {
24120             
24121             this.buttons.add(this.addxtypeChild(children[i]));
24122             
24123         }
24124         
24125         editor.on('editorevent', this.updateToolbar, this);
24126     },
24127     onBtnClick : function(id)
24128     {
24129        this.editorcore.relayCmd(id);
24130        this.editorcore.focus();
24131     },
24132     
24133     /**
24134      * Protected method that will not generally be called directly. It triggers
24135      * a toolbar update by reading the markup state of the current selection in the editor.
24136      */
24137     updateToolbar: function(){
24138
24139         if(!this.editorcore.activated){
24140             this.editor.onFirstFocus(); // is this neeed?
24141             return;
24142         }
24143
24144         var btns = this.buttons; 
24145         var doc = this.editorcore.doc;
24146         btns.get('bold').setActive(doc.queryCommandState('bold'));
24147         btns.get('italic').setActive(doc.queryCommandState('italic'));
24148         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24149         
24150         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24151         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24152         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24153         
24154         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24155         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24156          /*
24157         
24158         var ans = this.editorcore.getAllAncestors();
24159         if (this.formatCombo) {
24160             
24161             
24162             var store = this.formatCombo.store;
24163             this.formatCombo.setValue("");
24164             for (var i =0; i < ans.length;i++) {
24165                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24166                     // select it..
24167                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24168                     break;
24169                 }
24170             }
24171         }
24172         
24173         
24174         
24175         // hides menus... - so this cant be on a menu...
24176         Roo.bootstrap.MenuMgr.hideAll();
24177         */
24178         Roo.bootstrap.MenuMgr.hideAll();
24179         //this.editorsyncValue();
24180     },
24181     onFirstFocus: function() {
24182         this.buttons.each(function(item){
24183            item.enable();
24184         });
24185     },
24186     toggleSourceEdit : function(sourceEditMode){
24187         
24188           
24189         if(sourceEditMode){
24190             Roo.log("disabling buttons");
24191            this.buttons.each( function(item){
24192                 if(item.cmd != 'pencil'){
24193                     item.disable();
24194                 }
24195             });
24196           
24197         }else{
24198             Roo.log("enabling buttons");
24199             if(this.editorcore.initialized){
24200                 this.buttons.each( function(item){
24201                     item.enable();
24202                 });
24203             }
24204             
24205         }
24206         Roo.log("calling toggole on editor");
24207         // tell the editor that it's been pressed..
24208         this.editor.toggleSourceEdit(sourceEditMode);
24209        
24210     }
24211 });
24212
24213
24214
24215
24216
24217 /**
24218  * @class Roo.bootstrap.Table.AbstractSelectionModel
24219  * @extends Roo.util.Observable
24220  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24221  * implemented by descendant classes.  This class should not be directly instantiated.
24222  * @constructor
24223  */
24224 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24225     this.locked = false;
24226     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24227 };
24228
24229
24230 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24231     /** @ignore Called by the grid automatically. Do not call directly. */
24232     init : function(grid){
24233         this.grid = grid;
24234         this.initEvents();
24235     },
24236
24237     /**
24238      * Locks the selections.
24239      */
24240     lock : function(){
24241         this.locked = true;
24242     },
24243
24244     /**
24245      * Unlocks the selections.
24246      */
24247     unlock : function(){
24248         this.locked = false;
24249     },
24250
24251     /**
24252      * Returns true if the selections are locked.
24253      * @return {Boolean}
24254      */
24255     isLocked : function(){
24256         return this.locked;
24257     }
24258 });
24259 /**
24260  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24261  * @class Roo.bootstrap.Table.RowSelectionModel
24262  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24263  * It supports multiple selections and keyboard selection/navigation. 
24264  * @constructor
24265  * @param {Object} config
24266  */
24267
24268 Roo.bootstrap.Table.RowSelectionModel = function(config){
24269     Roo.apply(this, config);
24270     this.selections = new Roo.util.MixedCollection(false, function(o){
24271         return o.id;
24272     });
24273
24274     this.last = false;
24275     this.lastActive = false;
24276
24277     this.addEvents({
24278         /**
24279              * @event selectionchange
24280              * Fires when the selection changes
24281              * @param {SelectionModel} this
24282              */
24283             "selectionchange" : true,
24284         /**
24285              * @event afterselectionchange
24286              * Fires after the selection changes (eg. by key press or clicking)
24287              * @param {SelectionModel} this
24288              */
24289             "afterselectionchange" : true,
24290         /**
24291              * @event beforerowselect
24292              * Fires when a row is selected being selected, return false to cancel.
24293              * @param {SelectionModel} this
24294              * @param {Number} rowIndex The selected index
24295              * @param {Boolean} keepExisting False if other selections will be cleared
24296              */
24297             "beforerowselect" : true,
24298         /**
24299              * @event rowselect
24300              * Fires when a row is selected.
24301              * @param {SelectionModel} this
24302              * @param {Number} rowIndex The selected index
24303              * @param {Roo.data.Record} r The record
24304              */
24305             "rowselect" : true,
24306         /**
24307              * @event rowdeselect
24308              * Fires when a row is deselected.
24309              * @param {SelectionModel} this
24310              * @param {Number} rowIndex The selected index
24311              */
24312         "rowdeselect" : true
24313     });
24314     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24315     this.locked = false;
24316  };
24317
24318 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24319     /**
24320      * @cfg {Boolean} singleSelect
24321      * True to allow selection of only one row at a time (defaults to false)
24322      */
24323     singleSelect : false,
24324
24325     // private
24326     initEvents : function()
24327     {
24328
24329         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24330         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24331         //}else{ // allow click to work like normal
24332          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24333         //}
24334         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24335         this.grid.on("rowclick", this.handleMouseDown, this);
24336         
24337         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24338             "up" : function(e){
24339                 if(!e.shiftKey){
24340                     this.selectPrevious(e.shiftKey);
24341                 }else if(this.last !== false && this.lastActive !== false){
24342                     var last = this.last;
24343                     this.selectRange(this.last,  this.lastActive-1);
24344                     this.grid.getView().focusRow(this.lastActive);
24345                     if(last !== false){
24346                         this.last = last;
24347                     }
24348                 }else{
24349                     this.selectFirstRow();
24350                 }
24351                 this.fireEvent("afterselectionchange", this);
24352             },
24353             "down" : function(e){
24354                 if(!e.shiftKey){
24355                     this.selectNext(e.shiftKey);
24356                 }else if(this.last !== false && this.lastActive !== false){
24357                     var last = this.last;
24358                     this.selectRange(this.last,  this.lastActive+1);
24359                     this.grid.getView().focusRow(this.lastActive);
24360                     if(last !== false){
24361                         this.last = last;
24362                     }
24363                 }else{
24364                     this.selectFirstRow();
24365                 }
24366                 this.fireEvent("afterselectionchange", this);
24367             },
24368             scope: this
24369         });
24370         this.grid.store.on('load', function(){
24371             this.selections.clear();
24372         },this);
24373         /*
24374         var view = this.grid.view;
24375         view.on("refresh", this.onRefresh, this);
24376         view.on("rowupdated", this.onRowUpdated, this);
24377         view.on("rowremoved", this.onRemove, this);
24378         */
24379     },
24380
24381     // private
24382     onRefresh : function()
24383     {
24384         var ds = this.grid.store, i, v = this.grid.view;
24385         var s = this.selections;
24386         s.each(function(r){
24387             if((i = ds.indexOfId(r.id)) != -1){
24388                 v.onRowSelect(i);
24389             }else{
24390                 s.remove(r);
24391             }
24392         });
24393     },
24394
24395     // private
24396     onRemove : function(v, index, r){
24397         this.selections.remove(r);
24398     },
24399
24400     // private
24401     onRowUpdated : function(v, index, r){
24402         if(this.isSelected(r)){
24403             v.onRowSelect(index);
24404         }
24405     },
24406
24407     /**
24408      * Select records.
24409      * @param {Array} records The records to select
24410      * @param {Boolean} keepExisting (optional) True to keep existing selections
24411      */
24412     selectRecords : function(records, keepExisting)
24413     {
24414         if(!keepExisting){
24415             this.clearSelections();
24416         }
24417             var ds = this.grid.store;
24418         for(var i = 0, len = records.length; i < len; i++){
24419             this.selectRow(ds.indexOf(records[i]), true);
24420         }
24421     },
24422
24423     /**
24424      * Gets the number of selected rows.
24425      * @return {Number}
24426      */
24427     getCount : function(){
24428         return this.selections.length;
24429     },
24430
24431     /**
24432      * Selects the first row in the grid.
24433      */
24434     selectFirstRow : function(){
24435         this.selectRow(0);
24436     },
24437
24438     /**
24439      * Select the last row.
24440      * @param {Boolean} keepExisting (optional) True to keep existing selections
24441      */
24442     selectLastRow : function(keepExisting){
24443         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24444         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24445     },
24446
24447     /**
24448      * Selects the row immediately following the last selected row.
24449      * @param {Boolean} keepExisting (optional) True to keep existing selections
24450      */
24451     selectNext : function(keepExisting)
24452     {
24453             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24454             this.selectRow(this.last+1, keepExisting);
24455             this.grid.getView().focusRow(this.last);
24456         }
24457     },
24458
24459     /**
24460      * Selects the row that precedes the last selected row.
24461      * @param {Boolean} keepExisting (optional) True to keep existing selections
24462      */
24463     selectPrevious : function(keepExisting){
24464         if(this.last){
24465             this.selectRow(this.last-1, keepExisting);
24466             this.grid.getView().focusRow(this.last);
24467         }
24468     },
24469
24470     /**
24471      * Returns the selected records
24472      * @return {Array} Array of selected records
24473      */
24474     getSelections : function(){
24475         return [].concat(this.selections.items);
24476     },
24477
24478     /**
24479      * Returns the first selected record.
24480      * @return {Record}
24481      */
24482     getSelected : function(){
24483         return this.selections.itemAt(0);
24484     },
24485
24486
24487     /**
24488      * Clears all selections.
24489      */
24490     clearSelections : function(fast)
24491     {
24492         if(this.locked) {
24493             return;
24494         }
24495         if(fast !== true){
24496                 var ds = this.grid.store;
24497             var s = this.selections;
24498             s.each(function(r){
24499                 this.deselectRow(ds.indexOfId(r.id));
24500             }, this);
24501             s.clear();
24502         }else{
24503             this.selections.clear();
24504         }
24505         this.last = false;
24506     },
24507
24508
24509     /**
24510      * Selects all rows.
24511      */
24512     selectAll : function(){
24513         if(this.locked) {
24514             return;
24515         }
24516         this.selections.clear();
24517         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24518             this.selectRow(i, true);
24519         }
24520     },
24521
24522     /**
24523      * Returns True if there is a selection.
24524      * @return {Boolean}
24525      */
24526     hasSelection : function(){
24527         return this.selections.length > 0;
24528     },
24529
24530     /**
24531      * Returns True if the specified row is selected.
24532      * @param {Number/Record} record The record or index of the record to check
24533      * @return {Boolean}
24534      */
24535     isSelected : function(index){
24536             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24537         return (r && this.selections.key(r.id) ? true : false);
24538     },
24539
24540     /**
24541      * Returns True if the specified record id is selected.
24542      * @param {String} id The id of record to check
24543      * @return {Boolean}
24544      */
24545     isIdSelected : function(id){
24546         return (this.selections.key(id) ? true : false);
24547     },
24548
24549
24550     // private
24551     handleMouseDBClick : function(e, t){
24552         
24553     },
24554     // private
24555     handleMouseDown : function(e, t)
24556     {
24557             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24558         if(this.isLocked() || rowIndex < 0 ){
24559             return;
24560         };
24561         if(e.shiftKey && this.last !== false){
24562             var last = this.last;
24563             this.selectRange(last, rowIndex, e.ctrlKey);
24564             this.last = last; // reset the last
24565             t.focus();
24566     
24567         }else{
24568             var isSelected = this.isSelected(rowIndex);
24569             //Roo.log("select row:" + rowIndex);
24570             if(isSelected){
24571                 this.deselectRow(rowIndex);
24572             } else {
24573                         this.selectRow(rowIndex, true);
24574             }
24575     
24576             /*
24577                 if(e.button !== 0 && isSelected){
24578                 alert('rowIndex 2: ' + rowIndex);
24579                     view.focusRow(rowIndex);
24580                 }else if(e.ctrlKey && isSelected){
24581                     this.deselectRow(rowIndex);
24582                 }else if(!isSelected){
24583                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24584                     view.focusRow(rowIndex);
24585                 }
24586             */
24587         }
24588         this.fireEvent("afterselectionchange", this);
24589     },
24590     // private
24591     handleDragableRowClick :  function(grid, rowIndex, e) 
24592     {
24593         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24594             this.selectRow(rowIndex, false);
24595             grid.view.focusRow(rowIndex);
24596              this.fireEvent("afterselectionchange", this);
24597         }
24598     },
24599     
24600     /**
24601      * Selects multiple rows.
24602      * @param {Array} rows Array of the indexes of the row to select
24603      * @param {Boolean} keepExisting (optional) True to keep existing selections
24604      */
24605     selectRows : function(rows, keepExisting){
24606         if(!keepExisting){
24607             this.clearSelections();
24608         }
24609         for(var i = 0, len = rows.length; i < len; i++){
24610             this.selectRow(rows[i], true);
24611         }
24612     },
24613
24614     /**
24615      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24616      * @param {Number} startRow The index of the first row in the range
24617      * @param {Number} endRow The index of the last row in the range
24618      * @param {Boolean} keepExisting (optional) True to retain existing selections
24619      */
24620     selectRange : function(startRow, endRow, keepExisting){
24621         if(this.locked) {
24622             return;
24623         }
24624         if(!keepExisting){
24625             this.clearSelections();
24626         }
24627         if(startRow <= endRow){
24628             for(var i = startRow; i <= endRow; i++){
24629                 this.selectRow(i, true);
24630             }
24631         }else{
24632             for(var i = startRow; i >= endRow; i--){
24633                 this.selectRow(i, true);
24634             }
24635         }
24636     },
24637
24638     /**
24639      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24640      * @param {Number} startRow The index of the first row in the range
24641      * @param {Number} endRow The index of the last row in the range
24642      */
24643     deselectRange : function(startRow, endRow, preventViewNotify){
24644         if(this.locked) {
24645             return;
24646         }
24647         for(var i = startRow; i <= endRow; i++){
24648             this.deselectRow(i, preventViewNotify);
24649         }
24650     },
24651
24652     /**
24653      * Selects a row.
24654      * @param {Number} row The index of the row to select
24655      * @param {Boolean} keepExisting (optional) True to keep existing selections
24656      */
24657     selectRow : function(index, keepExisting, preventViewNotify)
24658     {
24659             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24660             return;
24661         }
24662         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24663             if(!keepExisting || this.singleSelect){
24664                 this.clearSelections();
24665             }
24666             
24667             var r = this.grid.store.getAt(index);
24668             //console.log('selectRow - record id :' + r.id);
24669             
24670             this.selections.add(r);
24671             this.last = this.lastActive = index;
24672             if(!preventViewNotify){
24673                 var proxy = new Roo.Element(
24674                                 this.grid.getRowDom(index)
24675                 );
24676                 proxy.addClass('bg-info info');
24677             }
24678             this.fireEvent("rowselect", this, index, r);
24679             this.fireEvent("selectionchange", this);
24680         }
24681     },
24682
24683     /**
24684      * Deselects a row.
24685      * @param {Number} row The index of the row to deselect
24686      */
24687     deselectRow : function(index, preventViewNotify)
24688     {
24689         if(this.locked) {
24690             return;
24691         }
24692         if(this.last == index){
24693             this.last = false;
24694         }
24695         if(this.lastActive == index){
24696             this.lastActive = false;
24697         }
24698         
24699         var r = this.grid.store.getAt(index);
24700         if (!r) {
24701             return;
24702         }
24703         
24704         this.selections.remove(r);
24705         //.console.log('deselectRow - record id :' + r.id);
24706         if(!preventViewNotify){
24707         
24708             var proxy = new Roo.Element(
24709                 this.grid.getRowDom(index)
24710             );
24711             proxy.removeClass('bg-info info');
24712         }
24713         this.fireEvent("rowdeselect", this, index);
24714         this.fireEvent("selectionchange", this);
24715     },
24716
24717     // private
24718     restoreLast : function(){
24719         if(this._last){
24720             this.last = this._last;
24721         }
24722     },
24723
24724     // private
24725     acceptsNav : function(row, col, cm){
24726         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24727     },
24728
24729     // private
24730     onEditorKey : function(field, e){
24731         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24732         if(k == e.TAB){
24733             e.stopEvent();
24734             ed.completeEdit();
24735             if(e.shiftKey){
24736                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24737             }else{
24738                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24739             }
24740         }else if(k == e.ENTER && !e.ctrlKey){
24741             e.stopEvent();
24742             ed.completeEdit();
24743             if(e.shiftKey){
24744                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24745             }else{
24746                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24747             }
24748         }else if(k == e.ESC){
24749             ed.cancelEdit();
24750         }
24751         if(newCell){
24752             g.startEditing(newCell[0], newCell[1]);
24753         }
24754     }
24755 });
24756 /*
24757  * Based on:
24758  * Ext JS Library 1.1.1
24759  * Copyright(c) 2006-2007, Ext JS, LLC.
24760  *
24761  * Originally Released Under LGPL - original licence link has changed is not relivant.
24762  *
24763  * Fork - LGPL
24764  * <script type="text/javascript">
24765  */
24766  
24767 /**
24768  * @class Roo.bootstrap.PagingToolbar
24769  * @extends Roo.bootstrap.NavSimplebar
24770  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24771  * @constructor
24772  * Create a new PagingToolbar
24773  * @param {Object} config The config object
24774  * @param {Roo.data.Store} store
24775  */
24776 Roo.bootstrap.PagingToolbar = function(config)
24777 {
24778     // old args format still supported... - xtype is prefered..
24779         // created from xtype...
24780     
24781     this.ds = config.dataSource;
24782     
24783     if (config.store && !this.ds) {
24784         this.store= Roo.factory(config.store, Roo.data);
24785         this.ds = this.store;
24786         this.ds.xmodule = this.xmodule || false;
24787     }
24788     
24789     this.toolbarItems = [];
24790     if (config.items) {
24791         this.toolbarItems = config.items;
24792     }
24793     
24794     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24795     
24796     this.cursor = 0;
24797     
24798     if (this.ds) { 
24799         this.bind(this.ds);
24800     }
24801     
24802     if (Roo.bootstrap.version == 4) {
24803         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24804     } else {
24805         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24806     }
24807     
24808 };
24809
24810 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24811     /**
24812      * @cfg {Roo.data.Store} dataSource
24813      * The underlying data store providing the paged data
24814      */
24815     /**
24816      * @cfg {String/HTMLElement/Element} container
24817      * container The id or element that will contain the toolbar
24818      */
24819     /**
24820      * @cfg {Boolean} displayInfo
24821      * True to display the displayMsg (defaults to false)
24822      */
24823     /**
24824      * @cfg {Number} pageSize
24825      * The number of records to display per page (defaults to 20)
24826      */
24827     pageSize: 20,
24828     /**
24829      * @cfg {String} displayMsg
24830      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24831      */
24832     displayMsg : 'Displaying {0} - {1} of {2}',
24833     /**
24834      * @cfg {String} emptyMsg
24835      * The message to display when no records are found (defaults to "No data to display")
24836      */
24837     emptyMsg : 'No data to display',
24838     /**
24839      * Customizable piece of the default paging text (defaults to "Page")
24840      * @type String
24841      */
24842     beforePageText : "Page",
24843     /**
24844      * Customizable piece of the default paging text (defaults to "of %0")
24845      * @type String
24846      */
24847     afterPageText : "of {0}",
24848     /**
24849      * Customizable piece of the default paging text (defaults to "First Page")
24850      * @type String
24851      */
24852     firstText : "First Page",
24853     /**
24854      * Customizable piece of the default paging text (defaults to "Previous Page")
24855      * @type String
24856      */
24857     prevText : "Previous Page",
24858     /**
24859      * Customizable piece of the default paging text (defaults to "Next Page")
24860      * @type String
24861      */
24862     nextText : "Next Page",
24863     /**
24864      * Customizable piece of the default paging text (defaults to "Last Page")
24865      * @type String
24866      */
24867     lastText : "Last Page",
24868     /**
24869      * Customizable piece of the default paging text (defaults to "Refresh")
24870      * @type String
24871      */
24872     refreshText : "Refresh",
24873
24874     buttons : false,
24875     // private
24876     onRender : function(ct, position) 
24877     {
24878         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24879         this.navgroup.parentId = this.id;
24880         this.navgroup.onRender(this.el, null);
24881         // add the buttons to the navgroup
24882         
24883         if(this.displayInfo){
24884             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24885             this.displayEl = this.el.select('.x-paging-info', true).first();
24886 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24887 //            this.displayEl = navel.el.select('span',true).first();
24888         }
24889         
24890         var _this = this;
24891         
24892         if(this.buttons){
24893             Roo.each(_this.buttons, function(e){ // this might need to use render????
24894                Roo.factory(e).render(_this.el);
24895             });
24896         }
24897             
24898         Roo.each(_this.toolbarItems, function(e) {
24899             _this.navgroup.addItem(e);
24900         });
24901         
24902         
24903         this.first = this.navgroup.addItem({
24904             tooltip: this.firstText,
24905             cls: "prev btn-outline-secondary",
24906             html : ' <i class="fa fa-step-backward"></i>',
24907             disabled: true,
24908             preventDefault: true,
24909             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24910         });
24911         
24912         this.prev =  this.navgroup.addItem({
24913             tooltip: this.prevText,
24914             cls: "prev btn-outline-secondary",
24915             html : ' <i class="fa fa-backward"></i>',
24916             disabled: true,
24917             preventDefault: true,
24918             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24919         });
24920     //this.addSeparator();
24921         
24922         
24923         var field = this.navgroup.addItem( {
24924             tagtype : 'span',
24925             cls : 'x-paging-position  btn-outline-secondary',
24926              disabled: true,
24927             html : this.beforePageText  +
24928                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24929                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24930          } ); //?? escaped?
24931         
24932         this.field = field.el.select('input', true).first();
24933         this.field.on("keydown", this.onPagingKeydown, this);
24934         this.field.on("focus", function(){this.dom.select();});
24935     
24936     
24937         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24938         //this.field.setHeight(18);
24939         //this.addSeparator();
24940         this.next = this.navgroup.addItem({
24941             tooltip: this.nextText,
24942             cls: "next btn-outline-secondary",
24943             html : ' <i class="fa fa-forward"></i>',
24944             disabled: true,
24945             preventDefault: true,
24946             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24947         });
24948         this.last = this.navgroup.addItem({
24949             tooltip: this.lastText,
24950             html : ' <i class="fa fa-step-forward"></i>',
24951             cls: "next btn-outline-secondary",
24952             disabled: true,
24953             preventDefault: true,
24954             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24955         });
24956     //this.addSeparator();
24957         this.loading = this.navgroup.addItem({
24958             tooltip: this.refreshText,
24959             cls: "btn-outline-secondary",
24960             html : ' <i class="fa fa-refresh"></i>',
24961             preventDefault: true,
24962             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24963         });
24964         
24965     },
24966
24967     // private
24968     updateInfo : function(){
24969         if(this.displayEl){
24970             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24971             var msg = count == 0 ?
24972                 this.emptyMsg :
24973                 String.format(
24974                     this.displayMsg,
24975                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24976                 );
24977             this.displayEl.update(msg);
24978         }
24979     },
24980
24981     // private
24982     onLoad : function(ds, r, o)
24983     {
24984         this.cursor = o.params.start ? o.params.start : 0;
24985         
24986         var d = this.getPageData(),
24987             ap = d.activePage,
24988             ps = d.pages;
24989         
24990         
24991         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24992         this.field.dom.value = ap;
24993         this.first.setDisabled(ap == 1);
24994         this.prev.setDisabled(ap == 1);
24995         this.next.setDisabled(ap == ps);
24996         this.last.setDisabled(ap == ps);
24997         this.loading.enable();
24998         this.updateInfo();
24999     },
25000
25001     // private
25002     getPageData : function(){
25003         var total = this.ds.getTotalCount();
25004         return {
25005             total : total,
25006             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25007             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25008         };
25009     },
25010
25011     // private
25012     onLoadError : function(){
25013         this.loading.enable();
25014     },
25015
25016     // private
25017     onPagingKeydown : function(e){
25018         var k = e.getKey();
25019         var d = this.getPageData();
25020         if(k == e.RETURN){
25021             var v = this.field.dom.value, pageNum;
25022             if(!v || isNaN(pageNum = parseInt(v, 10))){
25023                 this.field.dom.value = d.activePage;
25024                 return;
25025             }
25026             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25027             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25028             e.stopEvent();
25029         }
25030         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))
25031         {
25032           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25033           this.field.dom.value = pageNum;
25034           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25035           e.stopEvent();
25036         }
25037         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25038         {
25039           var v = this.field.dom.value, pageNum; 
25040           var increment = (e.shiftKey) ? 10 : 1;
25041           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25042                 increment *= -1;
25043           }
25044           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25045             this.field.dom.value = d.activePage;
25046             return;
25047           }
25048           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25049           {
25050             this.field.dom.value = parseInt(v, 10) + increment;
25051             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25052             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25053           }
25054           e.stopEvent();
25055         }
25056     },
25057
25058     // private
25059     beforeLoad : function(){
25060         if(this.loading){
25061             this.loading.disable();
25062         }
25063     },
25064
25065     // private
25066     onClick : function(which){
25067         
25068         var ds = this.ds;
25069         if (!ds) {
25070             return;
25071         }
25072         
25073         switch(which){
25074             case "first":
25075                 ds.load({params:{start: 0, limit: this.pageSize}});
25076             break;
25077             case "prev":
25078                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25079             break;
25080             case "next":
25081                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25082             break;
25083             case "last":
25084                 var total = ds.getTotalCount();
25085                 var extra = total % this.pageSize;
25086                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25087                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25088             break;
25089             case "refresh":
25090                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25091             break;
25092         }
25093     },
25094
25095     /**
25096      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25097      * @param {Roo.data.Store} store The data store to unbind
25098      */
25099     unbind : function(ds){
25100         ds.un("beforeload", this.beforeLoad, this);
25101         ds.un("load", this.onLoad, this);
25102         ds.un("loadexception", this.onLoadError, this);
25103         ds.un("remove", this.updateInfo, this);
25104         ds.un("add", this.updateInfo, this);
25105         this.ds = undefined;
25106     },
25107
25108     /**
25109      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25110      * @param {Roo.data.Store} store The data store to bind
25111      */
25112     bind : function(ds){
25113         ds.on("beforeload", this.beforeLoad, this);
25114         ds.on("load", this.onLoad, this);
25115         ds.on("loadexception", this.onLoadError, this);
25116         ds.on("remove", this.updateInfo, this);
25117         ds.on("add", this.updateInfo, this);
25118         this.ds = ds;
25119     }
25120 });/*
25121  * - LGPL
25122  *
25123  * element
25124  * 
25125  */
25126
25127 /**
25128  * @class Roo.bootstrap.MessageBar
25129  * @extends Roo.bootstrap.Component
25130  * Bootstrap MessageBar class
25131  * @cfg {String} html contents of the MessageBar
25132  * @cfg {String} weight (info | success | warning | danger) default info
25133  * @cfg {String} beforeClass insert the bar before the given class
25134  * @cfg {Boolean} closable (true | false) default false
25135  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25136  * 
25137  * @constructor
25138  * Create a new Element
25139  * @param {Object} config The config object
25140  */
25141
25142 Roo.bootstrap.MessageBar = function(config){
25143     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25144 };
25145
25146 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25147     
25148     html: '',
25149     weight: 'info',
25150     closable: false,
25151     fixed: false,
25152     beforeClass: 'bootstrap-sticky-wrap',
25153     
25154     getAutoCreate : function(){
25155         
25156         var cfg = {
25157             tag: 'div',
25158             cls: 'alert alert-dismissable alert-' + this.weight,
25159             cn: [
25160                 {
25161                     tag: 'span',
25162                     cls: 'message',
25163                     html: this.html || ''
25164                 }
25165             ]
25166         };
25167         
25168         if(this.fixed){
25169             cfg.cls += ' alert-messages-fixed';
25170         }
25171         
25172         if(this.closable){
25173             cfg.cn.push({
25174                 tag: 'button',
25175                 cls: 'close',
25176                 html: 'x'
25177             });
25178         }
25179         
25180         return cfg;
25181     },
25182     
25183     onRender : function(ct, position)
25184     {
25185         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25186         
25187         if(!this.el){
25188             var cfg = Roo.apply({},  this.getAutoCreate());
25189             cfg.id = Roo.id();
25190             
25191             if (this.cls) {
25192                 cfg.cls += ' ' + this.cls;
25193             }
25194             if (this.style) {
25195                 cfg.style = this.style;
25196             }
25197             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25198             
25199             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25200         }
25201         
25202         this.el.select('>button.close').on('click', this.hide, this);
25203         
25204     },
25205     
25206     show : function()
25207     {
25208         if (!this.rendered) {
25209             this.render();
25210         }
25211         
25212         this.el.show();
25213         
25214         this.fireEvent('show', this);
25215         
25216     },
25217     
25218     hide : function()
25219     {
25220         if (!this.rendered) {
25221             this.render();
25222         }
25223         
25224         this.el.hide();
25225         
25226         this.fireEvent('hide', this);
25227     },
25228     
25229     update : function()
25230     {
25231 //        var e = this.el.dom.firstChild;
25232 //        
25233 //        if(this.closable){
25234 //            e = e.nextSibling;
25235 //        }
25236 //        
25237 //        e.data = this.html || '';
25238
25239         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25240     }
25241    
25242 });
25243
25244  
25245
25246      /*
25247  * - LGPL
25248  *
25249  * Graph
25250  * 
25251  */
25252
25253
25254 /**
25255  * @class Roo.bootstrap.Graph
25256  * @extends Roo.bootstrap.Component
25257  * Bootstrap Graph class
25258 > Prameters
25259  -sm {number} sm 4
25260  -md {number} md 5
25261  @cfg {String} graphtype  bar | vbar | pie
25262  @cfg {number} g_x coodinator | centre x (pie)
25263  @cfg {number} g_y coodinator | centre y (pie)
25264  @cfg {number} g_r radius (pie)
25265  @cfg {number} g_height height of the chart (respected by all elements in the set)
25266  @cfg {number} g_width width of the chart (respected by all elements in the set)
25267  @cfg {Object} title The title of the chart
25268     
25269  -{Array}  values
25270  -opts (object) options for the chart 
25271      o {
25272      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25273      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25274      o vgutter (number)
25275      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.
25276      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25277      o to
25278      o stretch (boolean)
25279      o }
25280  -opts (object) options for the pie
25281      o{
25282      o cut
25283      o startAngle (number)
25284      o endAngle (number)
25285      } 
25286  *
25287  * @constructor
25288  * Create a new Input
25289  * @param {Object} config The config object
25290  */
25291
25292 Roo.bootstrap.Graph = function(config){
25293     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25294     
25295     this.addEvents({
25296         // img events
25297         /**
25298          * @event click
25299          * The img click event for the img.
25300          * @param {Roo.EventObject} e
25301          */
25302         "click" : true
25303     });
25304 };
25305
25306 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25307     
25308     sm: 4,
25309     md: 5,
25310     graphtype: 'bar',
25311     g_height: 250,
25312     g_width: 400,
25313     g_x: 50,
25314     g_y: 50,
25315     g_r: 30,
25316     opts:{
25317         //g_colors: this.colors,
25318         g_type: 'soft',
25319         g_gutter: '20%'
25320
25321     },
25322     title : false,
25323
25324     getAutoCreate : function(){
25325         
25326         var cfg = {
25327             tag: 'div',
25328             html : null
25329         };
25330         
25331         
25332         return  cfg;
25333     },
25334
25335     onRender : function(ct,position){
25336         
25337         
25338         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25339         
25340         if (typeof(Raphael) == 'undefined') {
25341             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25342             return;
25343         }
25344         
25345         this.raphael = Raphael(this.el.dom);
25346         
25347                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25348                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25349                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25350                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25351                 /*
25352                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25353                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25354                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25355                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25356                 
25357                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25358                 r.barchart(330, 10, 300, 220, data1);
25359                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25360                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25361                 */
25362                 
25363                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25364                 // r.barchart(30, 30, 560, 250,  xdata, {
25365                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25366                 //     axis : "0 0 1 1",
25367                 //     axisxlabels :  xdata
25368                 //     //yvalues : cols,
25369                    
25370                 // });
25371 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25372 //        
25373 //        this.load(null,xdata,{
25374 //                axis : "0 0 1 1",
25375 //                axisxlabels :  xdata
25376 //                });
25377
25378     },
25379
25380     load : function(graphtype,xdata,opts)
25381     {
25382         this.raphael.clear();
25383         if(!graphtype) {
25384             graphtype = this.graphtype;
25385         }
25386         if(!opts){
25387             opts = this.opts;
25388         }
25389         var r = this.raphael,
25390             fin = function () {
25391                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25392             },
25393             fout = function () {
25394                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25395             },
25396             pfin = function() {
25397                 this.sector.stop();
25398                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25399
25400                 if (this.label) {
25401                     this.label[0].stop();
25402                     this.label[0].attr({ r: 7.5 });
25403                     this.label[1].attr({ "font-weight": 800 });
25404                 }
25405             },
25406             pfout = function() {
25407                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25408
25409                 if (this.label) {
25410                     this.label[0].animate({ r: 5 }, 500, "bounce");
25411                     this.label[1].attr({ "font-weight": 400 });
25412                 }
25413             };
25414
25415         switch(graphtype){
25416             case 'bar':
25417                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25418                 break;
25419             case 'hbar':
25420                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25421                 break;
25422             case 'pie':
25423 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25424 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25425 //            
25426                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25427                 
25428                 break;
25429
25430         }
25431         
25432         if(this.title){
25433             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25434         }
25435         
25436     },
25437     
25438     setTitle: function(o)
25439     {
25440         this.title = o;
25441     },
25442     
25443     initEvents: function() {
25444         
25445         if(!this.href){
25446             this.el.on('click', this.onClick, this);
25447         }
25448     },
25449     
25450     onClick : function(e)
25451     {
25452         Roo.log('img onclick');
25453         this.fireEvent('click', this, e);
25454     }
25455    
25456 });
25457
25458  
25459 /*
25460  * - LGPL
25461  *
25462  * numberBox
25463  * 
25464  */
25465 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25466
25467 /**
25468  * @class Roo.bootstrap.dash.NumberBox
25469  * @extends Roo.bootstrap.Component
25470  * Bootstrap NumberBox class
25471  * @cfg {String} headline Box headline
25472  * @cfg {String} content Box content
25473  * @cfg {String} icon Box icon
25474  * @cfg {String} footer Footer text
25475  * @cfg {String} fhref Footer href
25476  * 
25477  * @constructor
25478  * Create a new NumberBox
25479  * @param {Object} config The config object
25480  */
25481
25482
25483 Roo.bootstrap.dash.NumberBox = function(config){
25484     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25485     
25486 };
25487
25488 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25489     
25490     headline : '',
25491     content : '',
25492     icon : '',
25493     footer : '',
25494     fhref : '',
25495     ficon : '',
25496     
25497     getAutoCreate : function(){
25498         
25499         var cfg = {
25500             tag : 'div',
25501             cls : 'small-box ',
25502             cn : [
25503                 {
25504                     tag : 'div',
25505                     cls : 'inner',
25506                     cn :[
25507                         {
25508                             tag : 'h3',
25509                             cls : 'roo-headline',
25510                             html : this.headline
25511                         },
25512                         {
25513                             tag : 'p',
25514                             cls : 'roo-content',
25515                             html : this.content
25516                         }
25517                     ]
25518                 }
25519             ]
25520         };
25521         
25522         if(this.icon){
25523             cfg.cn.push({
25524                 tag : 'div',
25525                 cls : 'icon',
25526                 cn :[
25527                     {
25528                         tag : 'i',
25529                         cls : 'ion ' + this.icon
25530                     }
25531                 ]
25532             });
25533         }
25534         
25535         if(this.footer){
25536             var footer = {
25537                 tag : 'a',
25538                 cls : 'small-box-footer',
25539                 href : this.fhref || '#',
25540                 html : this.footer
25541             };
25542             
25543             cfg.cn.push(footer);
25544             
25545         }
25546         
25547         return  cfg;
25548     },
25549
25550     onRender : function(ct,position){
25551         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25552
25553
25554        
25555                 
25556     },
25557
25558     setHeadline: function (value)
25559     {
25560         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25561     },
25562     
25563     setFooter: function (value, href)
25564     {
25565         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25566         
25567         if(href){
25568             this.el.select('a.small-box-footer',true).first().attr('href', href);
25569         }
25570         
25571     },
25572
25573     setContent: function (value)
25574     {
25575         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25576     },
25577
25578     initEvents: function() 
25579     {   
25580         
25581     }
25582     
25583 });
25584
25585  
25586 /*
25587  * - LGPL
25588  *
25589  * TabBox
25590  * 
25591  */
25592 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25593
25594 /**
25595  * @class Roo.bootstrap.dash.TabBox
25596  * @extends Roo.bootstrap.Component
25597  * Bootstrap TabBox class
25598  * @cfg {String} title Title of the TabBox
25599  * @cfg {String} icon Icon of the TabBox
25600  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25601  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25602  * 
25603  * @constructor
25604  * Create a new TabBox
25605  * @param {Object} config The config object
25606  */
25607
25608
25609 Roo.bootstrap.dash.TabBox = function(config){
25610     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25611     this.addEvents({
25612         // raw events
25613         /**
25614          * @event addpane
25615          * When a pane is added
25616          * @param {Roo.bootstrap.dash.TabPane} pane
25617          */
25618         "addpane" : true,
25619         /**
25620          * @event activatepane
25621          * When a pane is activated
25622          * @param {Roo.bootstrap.dash.TabPane} pane
25623          */
25624         "activatepane" : true
25625         
25626          
25627     });
25628     
25629     this.panes = [];
25630 };
25631
25632 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25633
25634     title : '',
25635     icon : false,
25636     showtabs : true,
25637     tabScrollable : false,
25638     
25639     getChildContainer : function()
25640     {
25641         return this.el.select('.tab-content', true).first();
25642     },
25643     
25644     getAutoCreate : function(){
25645         
25646         var header = {
25647             tag: 'li',
25648             cls: 'pull-left header',
25649             html: this.title,
25650             cn : []
25651         };
25652         
25653         if(this.icon){
25654             header.cn.push({
25655                 tag: 'i',
25656                 cls: 'fa ' + this.icon
25657             });
25658         }
25659         
25660         var h = {
25661             tag: 'ul',
25662             cls: 'nav nav-tabs pull-right',
25663             cn: [
25664                 header
25665             ]
25666         };
25667         
25668         if(this.tabScrollable){
25669             h = {
25670                 tag: 'div',
25671                 cls: 'tab-header',
25672                 cn: [
25673                     {
25674                         tag: 'ul',
25675                         cls: 'nav nav-tabs pull-right',
25676                         cn: [
25677                             header
25678                         ]
25679                     }
25680                 ]
25681             };
25682         }
25683         
25684         var cfg = {
25685             tag: 'div',
25686             cls: 'nav-tabs-custom',
25687             cn: [
25688                 h,
25689                 {
25690                     tag: 'div',
25691                     cls: 'tab-content no-padding',
25692                     cn: []
25693                 }
25694             ]
25695         };
25696
25697         return  cfg;
25698     },
25699     initEvents : function()
25700     {
25701         //Roo.log('add add pane handler');
25702         this.on('addpane', this.onAddPane, this);
25703     },
25704      /**
25705      * Updates the box title
25706      * @param {String} html to set the title to.
25707      */
25708     setTitle : function(value)
25709     {
25710         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25711     },
25712     onAddPane : function(pane)
25713     {
25714         this.panes.push(pane);
25715         //Roo.log('addpane');
25716         //Roo.log(pane);
25717         // tabs are rendere left to right..
25718         if(!this.showtabs){
25719             return;
25720         }
25721         
25722         var ctr = this.el.select('.nav-tabs', true).first();
25723          
25724          
25725         var existing = ctr.select('.nav-tab',true);
25726         var qty = existing.getCount();;
25727         
25728         
25729         var tab = ctr.createChild({
25730             tag : 'li',
25731             cls : 'nav-tab' + (qty ? '' : ' active'),
25732             cn : [
25733                 {
25734                     tag : 'a',
25735                     href:'#',
25736                     html : pane.title
25737                 }
25738             ]
25739         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25740         pane.tab = tab;
25741         
25742         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25743         if (!qty) {
25744             pane.el.addClass('active');
25745         }
25746         
25747                 
25748     },
25749     onTabClick : function(ev,un,ob,pane)
25750     {
25751         //Roo.log('tab - prev default');
25752         ev.preventDefault();
25753         
25754         
25755         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25756         pane.tab.addClass('active');
25757         //Roo.log(pane.title);
25758         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25759         // technically we should have a deactivate event.. but maybe add later.
25760         // and it should not de-activate the selected tab...
25761         this.fireEvent('activatepane', pane);
25762         pane.el.addClass('active');
25763         pane.fireEvent('activate');
25764         
25765         
25766     },
25767     
25768     getActivePane : function()
25769     {
25770         var r = false;
25771         Roo.each(this.panes, function(p) {
25772             if(p.el.hasClass('active')){
25773                 r = p;
25774                 return false;
25775             }
25776             
25777             return;
25778         });
25779         
25780         return r;
25781     }
25782     
25783     
25784 });
25785
25786  
25787 /*
25788  * - LGPL
25789  *
25790  * Tab pane
25791  * 
25792  */
25793 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25794 /**
25795  * @class Roo.bootstrap.TabPane
25796  * @extends Roo.bootstrap.Component
25797  * Bootstrap TabPane class
25798  * @cfg {Boolean} active (false | true) Default false
25799  * @cfg {String} title title of panel
25800
25801  * 
25802  * @constructor
25803  * Create a new TabPane
25804  * @param {Object} config The config object
25805  */
25806
25807 Roo.bootstrap.dash.TabPane = function(config){
25808     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25809     
25810     this.addEvents({
25811         // raw events
25812         /**
25813          * @event activate
25814          * When a pane is activated
25815          * @param {Roo.bootstrap.dash.TabPane} pane
25816          */
25817         "activate" : true
25818          
25819     });
25820 };
25821
25822 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25823     
25824     active : false,
25825     title : '',
25826     
25827     // the tabBox that this is attached to.
25828     tab : false,
25829      
25830     getAutoCreate : function() 
25831     {
25832         var cfg = {
25833             tag: 'div',
25834             cls: 'tab-pane'
25835         };
25836         
25837         if(this.active){
25838             cfg.cls += ' active';
25839         }
25840         
25841         return cfg;
25842     },
25843     initEvents  : function()
25844     {
25845         //Roo.log('trigger add pane handler');
25846         this.parent().fireEvent('addpane', this)
25847     },
25848     
25849      /**
25850      * Updates the tab title 
25851      * @param {String} html to set the title to.
25852      */
25853     setTitle: function(str)
25854     {
25855         if (!this.tab) {
25856             return;
25857         }
25858         this.title = str;
25859         this.tab.select('a', true).first().dom.innerHTML = str;
25860         
25861     }
25862     
25863     
25864     
25865 });
25866
25867  
25868
25869
25870  /*
25871  * - LGPL
25872  *
25873  * menu
25874  * 
25875  */
25876 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25877
25878 /**
25879  * @class Roo.bootstrap.menu.Menu
25880  * @extends Roo.bootstrap.Component
25881  * Bootstrap Menu class - container for Menu
25882  * @cfg {String} html Text of the menu
25883  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25884  * @cfg {String} icon Font awesome icon
25885  * @cfg {String} pos Menu align to (top | bottom) default bottom
25886  * 
25887  * 
25888  * @constructor
25889  * Create a new Menu
25890  * @param {Object} config The config object
25891  */
25892
25893
25894 Roo.bootstrap.menu.Menu = function(config){
25895     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25896     
25897     this.addEvents({
25898         /**
25899          * @event beforeshow
25900          * Fires before this menu is displayed
25901          * @param {Roo.bootstrap.menu.Menu} this
25902          */
25903         beforeshow : true,
25904         /**
25905          * @event beforehide
25906          * Fires before this menu is hidden
25907          * @param {Roo.bootstrap.menu.Menu} this
25908          */
25909         beforehide : true,
25910         /**
25911          * @event show
25912          * Fires after this menu is displayed
25913          * @param {Roo.bootstrap.menu.Menu} this
25914          */
25915         show : true,
25916         /**
25917          * @event hide
25918          * Fires after this menu is hidden
25919          * @param {Roo.bootstrap.menu.Menu} this
25920          */
25921         hide : true,
25922         /**
25923          * @event click
25924          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25925          * @param {Roo.bootstrap.menu.Menu} this
25926          * @param {Roo.EventObject} e
25927          */
25928         click : true
25929     });
25930     
25931 };
25932
25933 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25934     
25935     submenu : false,
25936     html : '',
25937     weight : 'default',
25938     icon : false,
25939     pos : 'bottom',
25940     
25941     
25942     getChildContainer : function() {
25943         if(this.isSubMenu){
25944             return this.el;
25945         }
25946         
25947         return this.el.select('ul.dropdown-menu', true).first();  
25948     },
25949     
25950     getAutoCreate : function()
25951     {
25952         var text = [
25953             {
25954                 tag : 'span',
25955                 cls : 'roo-menu-text',
25956                 html : this.html
25957             }
25958         ];
25959         
25960         if(this.icon){
25961             text.unshift({
25962                 tag : 'i',
25963                 cls : 'fa ' + this.icon
25964             })
25965         }
25966         
25967         
25968         var cfg = {
25969             tag : 'div',
25970             cls : 'btn-group',
25971             cn : [
25972                 {
25973                     tag : 'button',
25974                     cls : 'dropdown-button btn btn-' + this.weight,
25975                     cn : text
25976                 },
25977                 {
25978                     tag : 'button',
25979                     cls : 'dropdown-toggle btn btn-' + this.weight,
25980                     cn : [
25981                         {
25982                             tag : 'span',
25983                             cls : 'caret'
25984                         }
25985                     ]
25986                 },
25987                 {
25988                     tag : 'ul',
25989                     cls : 'dropdown-menu'
25990                 }
25991             ]
25992             
25993         };
25994         
25995         if(this.pos == 'top'){
25996             cfg.cls += ' dropup';
25997         }
25998         
25999         if(this.isSubMenu){
26000             cfg = {
26001                 tag : 'ul',
26002                 cls : 'dropdown-menu'
26003             }
26004         }
26005         
26006         return cfg;
26007     },
26008     
26009     onRender : function(ct, position)
26010     {
26011         this.isSubMenu = ct.hasClass('dropdown-submenu');
26012         
26013         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26014     },
26015     
26016     initEvents : function() 
26017     {
26018         if(this.isSubMenu){
26019             return;
26020         }
26021         
26022         this.hidden = true;
26023         
26024         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26025         this.triggerEl.on('click', this.onTriggerPress, this);
26026         
26027         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26028         this.buttonEl.on('click', this.onClick, this);
26029         
26030     },
26031     
26032     list : function()
26033     {
26034         if(this.isSubMenu){
26035             return this.el;
26036         }
26037         
26038         return this.el.select('ul.dropdown-menu', true).first();
26039     },
26040     
26041     onClick : function(e)
26042     {
26043         this.fireEvent("click", this, e);
26044     },
26045     
26046     onTriggerPress  : function(e)
26047     {   
26048         if (this.isVisible()) {
26049             this.hide();
26050         } else {
26051             this.show();
26052         }
26053     },
26054     
26055     isVisible : function(){
26056         return !this.hidden;
26057     },
26058     
26059     show : function()
26060     {
26061         this.fireEvent("beforeshow", this);
26062         
26063         this.hidden = false;
26064         this.el.addClass('open');
26065         
26066         Roo.get(document).on("mouseup", this.onMouseUp, this);
26067         
26068         this.fireEvent("show", this);
26069         
26070         
26071     },
26072     
26073     hide : function()
26074     {
26075         this.fireEvent("beforehide", this);
26076         
26077         this.hidden = true;
26078         this.el.removeClass('open');
26079         
26080         Roo.get(document).un("mouseup", this.onMouseUp);
26081         
26082         this.fireEvent("hide", this);
26083     },
26084     
26085     onMouseUp : function()
26086     {
26087         this.hide();
26088     }
26089     
26090 });
26091
26092  
26093  /*
26094  * - LGPL
26095  *
26096  * menu item
26097  * 
26098  */
26099 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26100
26101 /**
26102  * @class Roo.bootstrap.menu.Item
26103  * @extends Roo.bootstrap.Component
26104  * Bootstrap MenuItem class
26105  * @cfg {Boolean} submenu (true | false) default false
26106  * @cfg {String} html text of the item
26107  * @cfg {String} href the link
26108  * @cfg {Boolean} disable (true | false) default false
26109  * @cfg {Boolean} preventDefault (true | false) default true
26110  * @cfg {String} icon Font awesome icon
26111  * @cfg {String} pos Submenu align to (left | right) default right 
26112  * 
26113  * 
26114  * @constructor
26115  * Create a new Item
26116  * @param {Object} config The config object
26117  */
26118
26119
26120 Roo.bootstrap.menu.Item = function(config){
26121     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26122     this.addEvents({
26123         /**
26124          * @event mouseover
26125          * Fires when the mouse is hovering over this menu
26126          * @param {Roo.bootstrap.menu.Item} this
26127          * @param {Roo.EventObject} e
26128          */
26129         mouseover : true,
26130         /**
26131          * @event mouseout
26132          * Fires when the mouse exits this menu
26133          * @param {Roo.bootstrap.menu.Item} this
26134          * @param {Roo.EventObject} e
26135          */
26136         mouseout : true,
26137         // raw events
26138         /**
26139          * @event click
26140          * The raw click event for the entire grid.
26141          * @param {Roo.EventObject} e
26142          */
26143         click : true
26144     });
26145 };
26146
26147 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26148     
26149     submenu : false,
26150     href : '',
26151     html : '',
26152     preventDefault: true,
26153     disable : false,
26154     icon : false,
26155     pos : 'right',
26156     
26157     getAutoCreate : function()
26158     {
26159         var text = [
26160             {
26161                 tag : 'span',
26162                 cls : 'roo-menu-item-text',
26163                 html : this.html
26164             }
26165         ];
26166         
26167         if(this.icon){
26168             text.unshift({
26169                 tag : 'i',
26170                 cls : 'fa ' + this.icon
26171             })
26172         }
26173         
26174         var cfg = {
26175             tag : 'li',
26176             cn : [
26177                 {
26178                     tag : 'a',
26179                     href : this.href || '#',
26180                     cn : text
26181                 }
26182             ]
26183         };
26184         
26185         if(this.disable){
26186             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26187         }
26188         
26189         if(this.submenu){
26190             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26191             
26192             if(this.pos == 'left'){
26193                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26194             }
26195         }
26196         
26197         return cfg;
26198     },
26199     
26200     initEvents : function() 
26201     {
26202         this.el.on('mouseover', this.onMouseOver, this);
26203         this.el.on('mouseout', this.onMouseOut, this);
26204         
26205         this.el.select('a', true).first().on('click', this.onClick, this);
26206         
26207     },
26208     
26209     onClick : function(e)
26210     {
26211         if(this.preventDefault){
26212             e.preventDefault();
26213         }
26214         
26215         this.fireEvent("click", this, e);
26216     },
26217     
26218     onMouseOver : function(e)
26219     {
26220         if(this.submenu && this.pos == 'left'){
26221             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26222         }
26223         
26224         this.fireEvent("mouseover", this, e);
26225     },
26226     
26227     onMouseOut : function(e)
26228     {
26229         this.fireEvent("mouseout", this, e);
26230     }
26231 });
26232
26233  
26234
26235  /*
26236  * - LGPL
26237  *
26238  * menu separator
26239  * 
26240  */
26241 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26242
26243 /**
26244  * @class Roo.bootstrap.menu.Separator
26245  * @extends Roo.bootstrap.Component
26246  * Bootstrap Separator class
26247  * 
26248  * @constructor
26249  * Create a new Separator
26250  * @param {Object} config The config object
26251  */
26252
26253
26254 Roo.bootstrap.menu.Separator = function(config){
26255     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26256 };
26257
26258 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26259     
26260     getAutoCreate : function(){
26261         var cfg = {
26262             tag : 'li',
26263             cls: 'divider'
26264         };
26265         
26266         return cfg;
26267     }
26268    
26269 });
26270
26271  
26272
26273  /*
26274  * - LGPL
26275  *
26276  * Tooltip
26277  * 
26278  */
26279
26280 /**
26281  * @class Roo.bootstrap.Tooltip
26282  * Bootstrap Tooltip class
26283  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26284  * to determine which dom element triggers the tooltip.
26285  * 
26286  * It needs to add support for additional attributes like tooltip-position
26287  * 
26288  * @constructor
26289  * Create a new Toolti
26290  * @param {Object} config The config object
26291  */
26292
26293 Roo.bootstrap.Tooltip = function(config){
26294     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26295     
26296     this.alignment = Roo.bootstrap.Tooltip.alignment;
26297     
26298     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26299         this.alignment = config.alignment;
26300     }
26301     
26302 };
26303
26304 Roo.apply(Roo.bootstrap.Tooltip, {
26305     /**
26306      * @function init initialize tooltip monitoring.
26307      * @static
26308      */
26309     currentEl : false,
26310     currentTip : false,
26311     currentRegion : false,
26312     
26313     //  init : delay?
26314     
26315     init : function()
26316     {
26317         Roo.get(document).on('mouseover', this.enter ,this);
26318         Roo.get(document).on('mouseout', this.leave, this);
26319          
26320         
26321         this.currentTip = new Roo.bootstrap.Tooltip();
26322     },
26323     
26324     enter : function(ev)
26325     {
26326         var dom = ev.getTarget();
26327         
26328         //Roo.log(['enter',dom]);
26329         var el = Roo.fly(dom);
26330         if (this.currentEl) {
26331             //Roo.log(dom);
26332             //Roo.log(this.currentEl);
26333             //Roo.log(this.currentEl.contains(dom));
26334             if (this.currentEl == el) {
26335                 return;
26336             }
26337             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26338                 return;
26339             }
26340
26341         }
26342         
26343         if (this.currentTip.el) {
26344             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26345         }    
26346         //Roo.log(ev);
26347         
26348         if(!el || el.dom == document){
26349             return;
26350         }
26351         
26352         var bindEl = el;
26353         
26354         // you can not look for children, as if el is the body.. then everythign is the child..
26355         if (!el.attr('tooltip')) { //
26356             if (!el.select("[tooltip]").elements.length) {
26357                 return;
26358             }
26359             // is the mouse over this child...?
26360             bindEl = el.select("[tooltip]").first();
26361             var xy = ev.getXY();
26362             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26363                 //Roo.log("not in region.");
26364                 return;
26365             }
26366             //Roo.log("child element over..");
26367             
26368         }
26369         this.currentEl = bindEl;
26370         this.currentTip.bind(bindEl);
26371         this.currentRegion = Roo.lib.Region.getRegion(dom);
26372         this.currentTip.enter();
26373         
26374     },
26375     leave : function(ev)
26376     {
26377         var dom = ev.getTarget();
26378         //Roo.log(['leave',dom]);
26379         if (!this.currentEl) {
26380             return;
26381         }
26382         
26383         
26384         if (dom != this.currentEl.dom) {
26385             return;
26386         }
26387         var xy = ev.getXY();
26388         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26389             return;
26390         }
26391         // only activate leave if mouse cursor is outside... bounding box..
26392         
26393         
26394         
26395         
26396         if (this.currentTip) {
26397             this.currentTip.leave();
26398         }
26399         //Roo.log('clear currentEl');
26400         this.currentEl = false;
26401         
26402         
26403     },
26404     alignment : {
26405         'left' : ['r-l', [-2,0], 'right'],
26406         'right' : ['l-r', [2,0], 'left'],
26407         'bottom' : ['t-b', [0,2], 'top'],
26408         'top' : [ 'b-t', [0,-2], 'bottom']
26409     }
26410     
26411 });
26412
26413
26414 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26415     
26416     
26417     bindEl : false,
26418     
26419     delay : null, // can be { show : 300 , hide: 500}
26420     
26421     timeout : null,
26422     
26423     hoverState : null, //???
26424     
26425     placement : 'bottom', 
26426     
26427     alignment : false,
26428     
26429     getAutoCreate : function(){
26430     
26431         var cfg = {
26432            cls : 'tooltip',
26433            role : 'tooltip',
26434            cn : [
26435                 {
26436                     cls : 'tooltip-arrow'
26437                 },
26438                 {
26439                     cls : 'tooltip-inner'
26440                 }
26441            ]
26442         };
26443         
26444         return cfg;
26445     },
26446     bind : function(el)
26447     {
26448         this.bindEl = el;
26449     },
26450       
26451     
26452     enter : function () {
26453        
26454         if (this.timeout != null) {
26455             clearTimeout(this.timeout);
26456         }
26457         
26458         this.hoverState = 'in';
26459          //Roo.log("enter - show");
26460         if (!this.delay || !this.delay.show) {
26461             this.show();
26462             return;
26463         }
26464         var _t = this;
26465         this.timeout = setTimeout(function () {
26466             if (_t.hoverState == 'in') {
26467                 _t.show();
26468             }
26469         }, this.delay.show);
26470     },
26471     leave : function()
26472     {
26473         clearTimeout(this.timeout);
26474     
26475         this.hoverState = 'out';
26476          if (!this.delay || !this.delay.hide) {
26477             this.hide();
26478             return;
26479         }
26480        
26481         var _t = this;
26482         this.timeout = setTimeout(function () {
26483             //Roo.log("leave - timeout");
26484             
26485             if (_t.hoverState == 'out') {
26486                 _t.hide();
26487                 Roo.bootstrap.Tooltip.currentEl = false;
26488             }
26489         }, delay);
26490     },
26491     
26492     show : function (msg)
26493     {
26494         if (!this.el) {
26495             this.render(document.body);
26496         }
26497         // set content.
26498         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26499         
26500         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26501         
26502         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26503         
26504         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26505         
26506         var placement = typeof this.placement == 'function' ?
26507             this.placement.call(this, this.el, on_el) :
26508             this.placement;
26509             
26510         var autoToken = /\s?auto?\s?/i;
26511         var autoPlace = autoToken.test(placement);
26512         if (autoPlace) {
26513             placement = placement.replace(autoToken, '') || 'top';
26514         }
26515         
26516         //this.el.detach()
26517         //this.el.setXY([0,0]);
26518         this.el.show();
26519         //this.el.dom.style.display='block';
26520         
26521         //this.el.appendTo(on_el);
26522         
26523         var p = this.getPosition();
26524         var box = this.el.getBox();
26525         
26526         if (autoPlace) {
26527             // fixme..
26528         }
26529         
26530         var align = this.alignment[placement];
26531         
26532         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26533         
26534         if(placement == 'top' || placement == 'bottom'){
26535             if(xy[0] < 0){
26536                 placement = 'right';
26537             }
26538             
26539             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26540                 placement = 'left';
26541             }
26542             
26543             var scroll = Roo.select('body', true).first().getScroll();
26544             
26545             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26546                 placement = 'top';
26547             }
26548             
26549             align = this.alignment[placement];
26550         }
26551         
26552         this.el.alignTo(this.bindEl, align[0],align[1]);
26553         //var arrow = this.el.select('.arrow',true).first();
26554         //arrow.set(align[2], 
26555         
26556         this.el.addClass(placement);
26557         
26558         this.el.addClass('in fade');
26559         
26560         this.hoverState = null;
26561         
26562         if (this.el.hasClass('fade')) {
26563             // fade it?
26564         }
26565         
26566     },
26567     hide : function()
26568     {
26569          
26570         if (!this.el) {
26571             return;
26572         }
26573         //this.el.setXY([0,0]);
26574         this.el.removeClass('in');
26575         //this.el.hide();
26576         
26577     }
26578     
26579 });
26580  
26581
26582  /*
26583  * - LGPL
26584  *
26585  * Location Picker
26586  * 
26587  */
26588
26589 /**
26590  * @class Roo.bootstrap.LocationPicker
26591  * @extends Roo.bootstrap.Component
26592  * Bootstrap LocationPicker class
26593  * @cfg {Number} latitude Position when init default 0
26594  * @cfg {Number} longitude Position when init default 0
26595  * @cfg {Number} zoom default 15
26596  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26597  * @cfg {Boolean} mapTypeControl default false
26598  * @cfg {Boolean} disableDoubleClickZoom default false
26599  * @cfg {Boolean} scrollwheel default true
26600  * @cfg {Boolean} streetViewControl default false
26601  * @cfg {Number} radius default 0
26602  * @cfg {String} locationName
26603  * @cfg {Boolean} draggable default true
26604  * @cfg {Boolean} enableAutocomplete default false
26605  * @cfg {Boolean} enableReverseGeocode default true
26606  * @cfg {String} markerTitle
26607  * 
26608  * @constructor
26609  * Create a new LocationPicker
26610  * @param {Object} config The config object
26611  */
26612
26613
26614 Roo.bootstrap.LocationPicker = function(config){
26615     
26616     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26617     
26618     this.addEvents({
26619         /**
26620          * @event initial
26621          * Fires when the picker initialized.
26622          * @param {Roo.bootstrap.LocationPicker} this
26623          * @param {Google Location} location
26624          */
26625         initial : true,
26626         /**
26627          * @event positionchanged
26628          * Fires when the picker position changed.
26629          * @param {Roo.bootstrap.LocationPicker} this
26630          * @param {Google Location} location
26631          */
26632         positionchanged : true,
26633         /**
26634          * @event resize
26635          * Fires when the map resize.
26636          * @param {Roo.bootstrap.LocationPicker} this
26637          */
26638         resize : true,
26639         /**
26640          * @event show
26641          * Fires when the map show.
26642          * @param {Roo.bootstrap.LocationPicker} this
26643          */
26644         show : true,
26645         /**
26646          * @event hide
26647          * Fires when the map hide.
26648          * @param {Roo.bootstrap.LocationPicker} this
26649          */
26650         hide : true,
26651         /**
26652          * @event mapClick
26653          * Fires when click the map.
26654          * @param {Roo.bootstrap.LocationPicker} this
26655          * @param {Map event} e
26656          */
26657         mapClick : true,
26658         /**
26659          * @event mapRightClick
26660          * Fires when right click the map.
26661          * @param {Roo.bootstrap.LocationPicker} this
26662          * @param {Map event} e
26663          */
26664         mapRightClick : true,
26665         /**
26666          * @event markerClick
26667          * Fires when click the marker.
26668          * @param {Roo.bootstrap.LocationPicker} this
26669          * @param {Map event} e
26670          */
26671         markerClick : true,
26672         /**
26673          * @event markerRightClick
26674          * Fires when right click the marker.
26675          * @param {Roo.bootstrap.LocationPicker} this
26676          * @param {Map event} e
26677          */
26678         markerRightClick : true,
26679         /**
26680          * @event OverlayViewDraw
26681          * Fires when OverlayView Draw
26682          * @param {Roo.bootstrap.LocationPicker} this
26683          */
26684         OverlayViewDraw : true,
26685         /**
26686          * @event OverlayViewOnAdd
26687          * Fires when OverlayView Draw
26688          * @param {Roo.bootstrap.LocationPicker} this
26689          */
26690         OverlayViewOnAdd : true,
26691         /**
26692          * @event OverlayViewOnRemove
26693          * Fires when OverlayView Draw
26694          * @param {Roo.bootstrap.LocationPicker} this
26695          */
26696         OverlayViewOnRemove : true,
26697         /**
26698          * @event OverlayViewShow
26699          * Fires when OverlayView Draw
26700          * @param {Roo.bootstrap.LocationPicker} this
26701          * @param {Pixel} cpx
26702          */
26703         OverlayViewShow : true,
26704         /**
26705          * @event OverlayViewHide
26706          * Fires when OverlayView Draw
26707          * @param {Roo.bootstrap.LocationPicker} this
26708          */
26709         OverlayViewHide : true,
26710         /**
26711          * @event loadexception
26712          * Fires when load google lib failed.
26713          * @param {Roo.bootstrap.LocationPicker} this
26714          */
26715         loadexception : true
26716     });
26717         
26718 };
26719
26720 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26721     
26722     gMapContext: false,
26723     
26724     latitude: 0,
26725     longitude: 0,
26726     zoom: 15,
26727     mapTypeId: false,
26728     mapTypeControl: false,
26729     disableDoubleClickZoom: false,
26730     scrollwheel: true,
26731     streetViewControl: false,
26732     radius: 0,
26733     locationName: '',
26734     draggable: true,
26735     enableAutocomplete: false,
26736     enableReverseGeocode: true,
26737     markerTitle: '',
26738     
26739     getAutoCreate: function()
26740     {
26741
26742         var cfg = {
26743             tag: 'div',
26744             cls: 'roo-location-picker'
26745         };
26746         
26747         return cfg
26748     },
26749     
26750     initEvents: function(ct, position)
26751     {       
26752         if(!this.el.getWidth() || this.isApplied()){
26753             return;
26754         }
26755         
26756         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26757         
26758         this.initial();
26759     },
26760     
26761     initial: function()
26762     {
26763         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26764             this.fireEvent('loadexception', this);
26765             return;
26766         }
26767         
26768         if(!this.mapTypeId){
26769             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26770         }
26771         
26772         this.gMapContext = this.GMapContext();
26773         
26774         this.initOverlayView();
26775         
26776         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26777         
26778         var _this = this;
26779                 
26780         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26781             _this.setPosition(_this.gMapContext.marker.position);
26782         });
26783         
26784         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26785             _this.fireEvent('mapClick', this, event);
26786             
26787         });
26788
26789         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26790             _this.fireEvent('mapRightClick', this, event);
26791             
26792         });
26793         
26794         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26795             _this.fireEvent('markerClick', this, event);
26796             
26797         });
26798
26799         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26800             _this.fireEvent('markerRightClick', this, event);
26801             
26802         });
26803         
26804         this.setPosition(this.gMapContext.location);
26805         
26806         this.fireEvent('initial', this, this.gMapContext.location);
26807     },
26808     
26809     initOverlayView: function()
26810     {
26811         var _this = this;
26812         
26813         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26814             
26815             draw: function()
26816             {
26817                 _this.fireEvent('OverlayViewDraw', _this);
26818             },
26819             
26820             onAdd: function()
26821             {
26822                 _this.fireEvent('OverlayViewOnAdd', _this);
26823             },
26824             
26825             onRemove: function()
26826             {
26827                 _this.fireEvent('OverlayViewOnRemove', _this);
26828             },
26829             
26830             show: function(cpx)
26831             {
26832                 _this.fireEvent('OverlayViewShow', _this, cpx);
26833             },
26834             
26835             hide: function()
26836             {
26837                 _this.fireEvent('OverlayViewHide', _this);
26838             }
26839             
26840         });
26841     },
26842     
26843     fromLatLngToContainerPixel: function(event)
26844     {
26845         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26846     },
26847     
26848     isApplied: function() 
26849     {
26850         return this.getGmapContext() == false ? false : true;
26851     },
26852     
26853     getGmapContext: function() 
26854     {
26855         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26856     },
26857     
26858     GMapContext: function() 
26859     {
26860         var position = new google.maps.LatLng(this.latitude, this.longitude);
26861         
26862         var _map = new google.maps.Map(this.el.dom, {
26863             center: position,
26864             zoom: this.zoom,
26865             mapTypeId: this.mapTypeId,
26866             mapTypeControl: this.mapTypeControl,
26867             disableDoubleClickZoom: this.disableDoubleClickZoom,
26868             scrollwheel: this.scrollwheel,
26869             streetViewControl: this.streetViewControl,
26870             locationName: this.locationName,
26871             draggable: this.draggable,
26872             enableAutocomplete: this.enableAutocomplete,
26873             enableReverseGeocode: this.enableReverseGeocode
26874         });
26875         
26876         var _marker = new google.maps.Marker({
26877             position: position,
26878             map: _map,
26879             title: this.markerTitle,
26880             draggable: this.draggable
26881         });
26882         
26883         return {
26884             map: _map,
26885             marker: _marker,
26886             circle: null,
26887             location: position,
26888             radius: this.radius,
26889             locationName: this.locationName,
26890             addressComponents: {
26891                 formatted_address: null,
26892                 addressLine1: null,
26893                 addressLine2: null,
26894                 streetName: null,
26895                 streetNumber: null,
26896                 city: null,
26897                 district: null,
26898                 state: null,
26899                 stateOrProvince: null
26900             },
26901             settings: this,
26902             domContainer: this.el.dom,
26903             geodecoder: new google.maps.Geocoder()
26904         };
26905     },
26906     
26907     drawCircle: function(center, radius, options) 
26908     {
26909         if (this.gMapContext.circle != null) {
26910             this.gMapContext.circle.setMap(null);
26911         }
26912         if (radius > 0) {
26913             radius *= 1;
26914             options = Roo.apply({}, options, {
26915                 strokeColor: "#0000FF",
26916                 strokeOpacity: .35,
26917                 strokeWeight: 2,
26918                 fillColor: "#0000FF",
26919                 fillOpacity: .2
26920             });
26921             
26922             options.map = this.gMapContext.map;
26923             options.radius = radius;
26924             options.center = center;
26925             this.gMapContext.circle = new google.maps.Circle(options);
26926             return this.gMapContext.circle;
26927         }
26928         
26929         return null;
26930     },
26931     
26932     setPosition: function(location) 
26933     {
26934         this.gMapContext.location = location;
26935         this.gMapContext.marker.setPosition(location);
26936         this.gMapContext.map.panTo(location);
26937         this.drawCircle(location, this.gMapContext.radius, {});
26938         
26939         var _this = this;
26940         
26941         if (this.gMapContext.settings.enableReverseGeocode) {
26942             this.gMapContext.geodecoder.geocode({
26943                 latLng: this.gMapContext.location
26944             }, function(results, status) {
26945                 
26946                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26947                     _this.gMapContext.locationName = results[0].formatted_address;
26948                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26949                     
26950                     _this.fireEvent('positionchanged', this, location);
26951                 }
26952             });
26953             
26954             return;
26955         }
26956         
26957         this.fireEvent('positionchanged', this, location);
26958     },
26959     
26960     resize: function()
26961     {
26962         google.maps.event.trigger(this.gMapContext.map, "resize");
26963         
26964         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26965         
26966         this.fireEvent('resize', this);
26967     },
26968     
26969     setPositionByLatLng: function(latitude, longitude)
26970     {
26971         this.setPosition(new google.maps.LatLng(latitude, longitude));
26972     },
26973     
26974     getCurrentPosition: function() 
26975     {
26976         return {
26977             latitude: this.gMapContext.location.lat(),
26978             longitude: this.gMapContext.location.lng()
26979         };
26980     },
26981     
26982     getAddressName: function() 
26983     {
26984         return this.gMapContext.locationName;
26985     },
26986     
26987     getAddressComponents: function() 
26988     {
26989         return this.gMapContext.addressComponents;
26990     },
26991     
26992     address_component_from_google_geocode: function(address_components) 
26993     {
26994         var result = {};
26995         
26996         for (var i = 0; i < address_components.length; i++) {
26997             var component = address_components[i];
26998             if (component.types.indexOf("postal_code") >= 0) {
26999                 result.postalCode = component.short_name;
27000             } else if (component.types.indexOf("street_number") >= 0) {
27001                 result.streetNumber = component.short_name;
27002             } else if (component.types.indexOf("route") >= 0) {
27003                 result.streetName = component.short_name;
27004             } else if (component.types.indexOf("neighborhood") >= 0) {
27005                 result.city = component.short_name;
27006             } else if (component.types.indexOf("locality") >= 0) {
27007                 result.city = component.short_name;
27008             } else if (component.types.indexOf("sublocality") >= 0) {
27009                 result.district = component.short_name;
27010             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27011                 result.stateOrProvince = component.short_name;
27012             } else if (component.types.indexOf("country") >= 0) {
27013                 result.country = component.short_name;
27014             }
27015         }
27016         
27017         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27018         result.addressLine2 = "";
27019         return result;
27020     },
27021     
27022     setZoomLevel: function(zoom)
27023     {
27024         this.gMapContext.map.setZoom(zoom);
27025     },
27026     
27027     show: function()
27028     {
27029         if(!this.el){
27030             return;
27031         }
27032         
27033         this.el.show();
27034         
27035         this.resize();
27036         
27037         this.fireEvent('show', this);
27038     },
27039     
27040     hide: function()
27041     {
27042         if(!this.el){
27043             return;
27044         }
27045         
27046         this.el.hide();
27047         
27048         this.fireEvent('hide', this);
27049     }
27050     
27051 });
27052
27053 Roo.apply(Roo.bootstrap.LocationPicker, {
27054     
27055     OverlayView : function(map, options)
27056     {
27057         options = options || {};
27058         
27059         this.setMap(map);
27060     }
27061     
27062     
27063 });/*
27064  * - LGPL
27065  *
27066  * Alert
27067  * 
27068  */
27069
27070 /**
27071  * @class Roo.bootstrap.Alert
27072  * @extends Roo.bootstrap.Component
27073  * Bootstrap Alert class
27074  * @cfg {String} title The title of alert
27075  * @cfg {String} html The content of alert
27076  * @cfg {String} weight (  success | info | warning | danger )
27077  * @cfg {String} faicon font-awesomeicon
27078  * 
27079  * @constructor
27080  * Create a new alert
27081  * @param {Object} config The config object
27082  */
27083
27084
27085 Roo.bootstrap.Alert = function(config){
27086     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27087     
27088 };
27089
27090 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27091     
27092     title: '',
27093     html: '',
27094     weight: false,
27095     faicon: false,
27096     
27097     getAutoCreate : function()
27098     {
27099         
27100         var cfg = {
27101             tag : 'div',
27102             cls : 'alert',
27103             cn : [
27104                 {
27105                     tag : 'i',
27106                     cls : 'roo-alert-icon'
27107                     
27108                 },
27109                 {
27110                     tag : 'b',
27111                     cls : 'roo-alert-title',
27112                     html : this.title
27113                 },
27114                 {
27115                     tag : 'span',
27116                     cls : 'roo-alert-text',
27117                     html : this.html
27118                 }
27119             ]
27120         };
27121         
27122         if(this.faicon){
27123             cfg.cn[0].cls += ' fa ' + this.faicon;
27124         }
27125         
27126         if(this.weight){
27127             cfg.cls += ' alert-' + this.weight;
27128         }
27129         
27130         return cfg;
27131     },
27132     
27133     initEvents: function() 
27134     {
27135         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27136     },
27137     
27138     setTitle : function(str)
27139     {
27140         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27141     },
27142     
27143     setText : function(str)
27144     {
27145         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27146     },
27147     
27148     setWeight : function(weight)
27149     {
27150         if(this.weight){
27151             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27152         }
27153         
27154         this.weight = weight;
27155         
27156         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27157     },
27158     
27159     setIcon : function(icon)
27160     {
27161         if(this.faicon){
27162             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27163         }
27164         
27165         this.faicon = icon;
27166         
27167         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27168     },
27169     
27170     hide: function() 
27171     {
27172         this.el.hide();   
27173     },
27174     
27175     show: function() 
27176     {  
27177         this.el.show();   
27178     }
27179     
27180 });
27181
27182  
27183 /*
27184 * Licence: LGPL
27185 */
27186
27187 /**
27188  * @class Roo.bootstrap.UploadCropbox
27189  * @extends Roo.bootstrap.Component
27190  * Bootstrap UploadCropbox class
27191  * @cfg {String} emptyText show when image has been loaded
27192  * @cfg {String} rotateNotify show when image too small to rotate
27193  * @cfg {Number} errorTimeout default 3000
27194  * @cfg {Number} minWidth default 300
27195  * @cfg {Number} minHeight default 300
27196  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27197  * @cfg {Boolean} isDocument (true|false) default false
27198  * @cfg {String} url action url
27199  * @cfg {String} paramName default 'imageUpload'
27200  * @cfg {String} method default POST
27201  * @cfg {Boolean} loadMask (true|false) default true
27202  * @cfg {Boolean} loadingText default 'Loading...'
27203  * 
27204  * @constructor
27205  * Create a new UploadCropbox
27206  * @param {Object} config The config object
27207  */
27208
27209 Roo.bootstrap.UploadCropbox = function(config){
27210     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27211     
27212     this.addEvents({
27213         /**
27214          * @event beforeselectfile
27215          * Fire before select file
27216          * @param {Roo.bootstrap.UploadCropbox} this
27217          */
27218         "beforeselectfile" : true,
27219         /**
27220          * @event initial
27221          * Fire after initEvent
27222          * @param {Roo.bootstrap.UploadCropbox} this
27223          */
27224         "initial" : true,
27225         /**
27226          * @event crop
27227          * Fire after initEvent
27228          * @param {Roo.bootstrap.UploadCropbox} this
27229          * @param {String} data
27230          */
27231         "crop" : true,
27232         /**
27233          * @event prepare
27234          * Fire when preparing the file data
27235          * @param {Roo.bootstrap.UploadCropbox} this
27236          * @param {Object} file
27237          */
27238         "prepare" : true,
27239         /**
27240          * @event exception
27241          * Fire when get exception
27242          * @param {Roo.bootstrap.UploadCropbox} this
27243          * @param {XMLHttpRequest} xhr
27244          */
27245         "exception" : true,
27246         /**
27247          * @event beforeloadcanvas
27248          * Fire before load the canvas
27249          * @param {Roo.bootstrap.UploadCropbox} this
27250          * @param {String} src
27251          */
27252         "beforeloadcanvas" : true,
27253         /**
27254          * @event trash
27255          * Fire when trash image
27256          * @param {Roo.bootstrap.UploadCropbox} this
27257          */
27258         "trash" : true,
27259         /**
27260          * @event download
27261          * Fire when download the image
27262          * @param {Roo.bootstrap.UploadCropbox} this
27263          */
27264         "download" : true,
27265         /**
27266          * @event footerbuttonclick
27267          * Fire when footerbuttonclick
27268          * @param {Roo.bootstrap.UploadCropbox} this
27269          * @param {String} type
27270          */
27271         "footerbuttonclick" : true,
27272         /**
27273          * @event resize
27274          * Fire when resize
27275          * @param {Roo.bootstrap.UploadCropbox} this
27276          */
27277         "resize" : true,
27278         /**
27279          * @event rotate
27280          * Fire when rotate the image
27281          * @param {Roo.bootstrap.UploadCropbox} this
27282          * @param {String} pos
27283          */
27284         "rotate" : true,
27285         /**
27286          * @event inspect
27287          * Fire when inspect the file
27288          * @param {Roo.bootstrap.UploadCropbox} this
27289          * @param {Object} file
27290          */
27291         "inspect" : true,
27292         /**
27293          * @event upload
27294          * Fire when xhr upload the file
27295          * @param {Roo.bootstrap.UploadCropbox} this
27296          * @param {Object} data
27297          */
27298         "upload" : true,
27299         /**
27300          * @event arrange
27301          * Fire when arrange the file data
27302          * @param {Roo.bootstrap.UploadCropbox} this
27303          * @param {Object} formData
27304          */
27305         "arrange" : true
27306     });
27307     
27308     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27309 };
27310
27311 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27312     
27313     emptyText : 'Click to upload image',
27314     rotateNotify : 'Image is too small to rotate',
27315     errorTimeout : 3000,
27316     scale : 0,
27317     baseScale : 1,
27318     rotate : 0,
27319     dragable : false,
27320     pinching : false,
27321     mouseX : 0,
27322     mouseY : 0,
27323     cropData : false,
27324     minWidth : 300,
27325     minHeight : 300,
27326     file : false,
27327     exif : {},
27328     baseRotate : 1,
27329     cropType : 'image/jpeg',
27330     buttons : false,
27331     canvasLoaded : false,
27332     isDocument : false,
27333     method : 'POST',
27334     paramName : 'imageUpload',
27335     loadMask : true,
27336     loadingText : 'Loading...',
27337     maskEl : false,
27338     
27339     getAutoCreate : function()
27340     {
27341         var cfg = {
27342             tag : 'div',
27343             cls : 'roo-upload-cropbox',
27344             cn : [
27345                 {
27346                     tag : 'input',
27347                     cls : 'roo-upload-cropbox-selector',
27348                     type : 'file'
27349                 },
27350                 {
27351                     tag : 'div',
27352                     cls : 'roo-upload-cropbox-body',
27353                     style : 'cursor:pointer',
27354                     cn : [
27355                         {
27356                             tag : 'div',
27357                             cls : 'roo-upload-cropbox-preview'
27358                         },
27359                         {
27360                             tag : 'div',
27361                             cls : 'roo-upload-cropbox-thumb'
27362                         },
27363                         {
27364                             tag : 'div',
27365                             cls : 'roo-upload-cropbox-empty-notify',
27366                             html : this.emptyText
27367                         },
27368                         {
27369                             tag : 'div',
27370                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27371                             html : this.rotateNotify
27372                         }
27373                     ]
27374                 },
27375                 {
27376                     tag : 'div',
27377                     cls : 'roo-upload-cropbox-footer',
27378                     cn : {
27379                         tag : 'div',
27380                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27381                         cn : []
27382                     }
27383                 }
27384             ]
27385         };
27386         
27387         return cfg;
27388     },
27389     
27390     onRender : function(ct, position)
27391     {
27392         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27393         
27394         if (this.buttons.length) {
27395             
27396             Roo.each(this.buttons, function(bb) {
27397                 
27398                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27399                 
27400                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27401                 
27402             }, this);
27403         }
27404         
27405         if(this.loadMask){
27406             this.maskEl = this.el;
27407         }
27408     },
27409     
27410     initEvents : function()
27411     {
27412         this.urlAPI = (window.createObjectURL && window) || 
27413                                 (window.URL && URL.revokeObjectURL && URL) || 
27414                                 (window.webkitURL && webkitURL);
27415                         
27416         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27417         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27418         
27419         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27420         this.selectorEl.hide();
27421         
27422         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27423         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27424         
27425         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27426         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27427         this.thumbEl.hide();
27428         
27429         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27430         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27431         
27432         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27433         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27434         this.errorEl.hide();
27435         
27436         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27437         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27438         this.footerEl.hide();
27439         
27440         this.setThumbBoxSize();
27441         
27442         this.bind();
27443         
27444         this.resize();
27445         
27446         this.fireEvent('initial', this);
27447     },
27448
27449     bind : function()
27450     {
27451         var _this = this;
27452         
27453         window.addEventListener("resize", function() { _this.resize(); } );
27454         
27455         this.bodyEl.on('click', this.beforeSelectFile, this);
27456         
27457         if(Roo.isTouch){
27458             this.bodyEl.on('touchstart', this.onTouchStart, this);
27459             this.bodyEl.on('touchmove', this.onTouchMove, this);
27460             this.bodyEl.on('touchend', this.onTouchEnd, this);
27461         }
27462         
27463         if(!Roo.isTouch){
27464             this.bodyEl.on('mousedown', this.onMouseDown, this);
27465             this.bodyEl.on('mousemove', this.onMouseMove, this);
27466             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27467             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27468             Roo.get(document).on('mouseup', this.onMouseUp, this);
27469         }
27470         
27471         this.selectorEl.on('change', this.onFileSelected, this);
27472     },
27473     
27474     reset : function()
27475     {    
27476         this.scale = 0;
27477         this.baseScale = 1;
27478         this.rotate = 0;
27479         this.baseRotate = 1;
27480         this.dragable = false;
27481         this.pinching = false;
27482         this.mouseX = 0;
27483         this.mouseY = 0;
27484         this.cropData = false;
27485         this.notifyEl.dom.innerHTML = this.emptyText;
27486         
27487         this.selectorEl.dom.value = '';
27488         
27489     },
27490     
27491     resize : function()
27492     {
27493         if(this.fireEvent('resize', this) != false){
27494             this.setThumbBoxPosition();
27495             this.setCanvasPosition();
27496         }
27497     },
27498     
27499     onFooterButtonClick : function(e, el, o, type)
27500     {
27501         switch (type) {
27502             case 'rotate-left' :
27503                 this.onRotateLeft(e);
27504                 break;
27505             case 'rotate-right' :
27506                 this.onRotateRight(e);
27507                 break;
27508             case 'picture' :
27509                 this.beforeSelectFile(e);
27510                 break;
27511             case 'trash' :
27512                 this.trash(e);
27513                 break;
27514             case 'crop' :
27515                 this.crop(e);
27516                 break;
27517             case 'download' :
27518                 this.download(e);
27519                 break;
27520             default :
27521                 break;
27522         }
27523         
27524         this.fireEvent('footerbuttonclick', this, type);
27525     },
27526     
27527     beforeSelectFile : function(e)
27528     {
27529         e.preventDefault();
27530         
27531         if(this.fireEvent('beforeselectfile', this) != false){
27532             this.selectorEl.dom.click();
27533         }
27534     },
27535     
27536     onFileSelected : function(e)
27537     {
27538         e.preventDefault();
27539         
27540         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27541             return;
27542         }
27543         
27544         var file = this.selectorEl.dom.files[0];
27545         
27546         if(this.fireEvent('inspect', this, file) != false){
27547             this.prepare(file);
27548         }
27549         
27550     },
27551     
27552     trash : function(e)
27553     {
27554         this.fireEvent('trash', this);
27555     },
27556     
27557     download : function(e)
27558     {
27559         this.fireEvent('download', this);
27560     },
27561     
27562     loadCanvas : function(src)
27563     {   
27564         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27565             
27566             this.reset();
27567             
27568             this.imageEl = document.createElement('img');
27569             
27570             var _this = this;
27571             
27572             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27573             
27574             this.imageEl.src = src;
27575         }
27576     },
27577     
27578     onLoadCanvas : function()
27579     {   
27580         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27581         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27582         
27583         this.bodyEl.un('click', this.beforeSelectFile, this);
27584         
27585         this.notifyEl.hide();
27586         this.thumbEl.show();
27587         this.footerEl.show();
27588         
27589         this.baseRotateLevel();
27590         
27591         if(this.isDocument){
27592             this.setThumbBoxSize();
27593         }
27594         
27595         this.setThumbBoxPosition();
27596         
27597         this.baseScaleLevel();
27598         
27599         this.draw();
27600         
27601         this.resize();
27602         
27603         this.canvasLoaded = true;
27604         
27605         if(this.loadMask){
27606             this.maskEl.unmask();
27607         }
27608         
27609     },
27610     
27611     setCanvasPosition : function()
27612     {   
27613         if(!this.canvasEl){
27614             return;
27615         }
27616         
27617         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27618         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27619         
27620         this.previewEl.setLeft(pw);
27621         this.previewEl.setTop(ph);
27622         
27623     },
27624     
27625     onMouseDown : function(e)
27626     {   
27627         e.stopEvent();
27628         
27629         this.dragable = true;
27630         this.pinching = false;
27631         
27632         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27633             this.dragable = false;
27634             return;
27635         }
27636         
27637         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27638         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27639         
27640     },
27641     
27642     onMouseMove : function(e)
27643     {   
27644         e.stopEvent();
27645         
27646         if(!this.canvasLoaded){
27647             return;
27648         }
27649         
27650         if (!this.dragable){
27651             return;
27652         }
27653         
27654         var minX = Math.ceil(this.thumbEl.getLeft(true));
27655         var minY = Math.ceil(this.thumbEl.getTop(true));
27656         
27657         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27658         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27659         
27660         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27661         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27662         
27663         x = x - this.mouseX;
27664         y = y - this.mouseY;
27665         
27666         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27667         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27668         
27669         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27670         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27671         
27672         this.previewEl.setLeft(bgX);
27673         this.previewEl.setTop(bgY);
27674         
27675         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27676         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27677     },
27678     
27679     onMouseUp : function(e)
27680     {   
27681         e.stopEvent();
27682         
27683         this.dragable = false;
27684     },
27685     
27686     onMouseWheel : function(e)
27687     {   
27688         e.stopEvent();
27689         
27690         this.startScale = this.scale;
27691         
27692         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27693         
27694         if(!this.zoomable()){
27695             this.scale = this.startScale;
27696             return;
27697         }
27698         
27699         this.draw();
27700         
27701         return;
27702     },
27703     
27704     zoomable : function()
27705     {
27706         var minScale = this.thumbEl.getWidth() / this.minWidth;
27707         
27708         if(this.minWidth < this.minHeight){
27709             minScale = this.thumbEl.getHeight() / this.minHeight;
27710         }
27711         
27712         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27713         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27714         
27715         if(
27716                 this.isDocument &&
27717                 (this.rotate == 0 || this.rotate == 180) && 
27718                 (
27719                     width > this.imageEl.OriginWidth || 
27720                     height > this.imageEl.OriginHeight ||
27721                     (width < this.minWidth && height < this.minHeight)
27722                 )
27723         ){
27724             return false;
27725         }
27726         
27727         if(
27728                 this.isDocument &&
27729                 (this.rotate == 90 || this.rotate == 270) && 
27730                 (
27731                     width > this.imageEl.OriginWidth || 
27732                     height > this.imageEl.OriginHeight ||
27733                     (width < this.minHeight && height < this.minWidth)
27734                 )
27735         ){
27736             return false;
27737         }
27738         
27739         if(
27740                 !this.isDocument &&
27741                 (this.rotate == 0 || this.rotate == 180) && 
27742                 (
27743                     width < this.minWidth || 
27744                     width > this.imageEl.OriginWidth || 
27745                     height < this.minHeight || 
27746                     height > this.imageEl.OriginHeight
27747                 )
27748         ){
27749             return false;
27750         }
27751         
27752         if(
27753                 !this.isDocument &&
27754                 (this.rotate == 90 || this.rotate == 270) && 
27755                 (
27756                     width < this.minHeight || 
27757                     width > this.imageEl.OriginWidth || 
27758                     height < this.minWidth || 
27759                     height > this.imageEl.OriginHeight
27760                 )
27761         ){
27762             return false;
27763         }
27764         
27765         return true;
27766         
27767     },
27768     
27769     onRotateLeft : function(e)
27770     {   
27771         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27772             
27773             var minScale = this.thumbEl.getWidth() / this.minWidth;
27774             
27775             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27776             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27777             
27778             this.startScale = this.scale;
27779             
27780             while (this.getScaleLevel() < minScale){
27781             
27782                 this.scale = this.scale + 1;
27783                 
27784                 if(!this.zoomable()){
27785                     break;
27786                 }
27787                 
27788                 if(
27789                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27790                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27791                 ){
27792                     continue;
27793                 }
27794                 
27795                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27796
27797                 this.draw();
27798                 
27799                 return;
27800             }
27801             
27802             this.scale = this.startScale;
27803             
27804             this.onRotateFail();
27805             
27806             return false;
27807         }
27808         
27809         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27810
27811         if(this.isDocument){
27812             this.setThumbBoxSize();
27813             this.setThumbBoxPosition();
27814             this.setCanvasPosition();
27815         }
27816         
27817         this.draw();
27818         
27819         this.fireEvent('rotate', this, 'left');
27820         
27821     },
27822     
27823     onRotateRight : function(e)
27824     {
27825         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27826             
27827             var minScale = this.thumbEl.getWidth() / this.minWidth;
27828         
27829             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27830             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27831             
27832             this.startScale = this.scale;
27833             
27834             while (this.getScaleLevel() < minScale){
27835             
27836                 this.scale = this.scale + 1;
27837                 
27838                 if(!this.zoomable()){
27839                     break;
27840                 }
27841                 
27842                 if(
27843                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27844                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27845                 ){
27846                     continue;
27847                 }
27848                 
27849                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27850
27851                 this.draw();
27852                 
27853                 return;
27854             }
27855             
27856             this.scale = this.startScale;
27857             
27858             this.onRotateFail();
27859             
27860             return false;
27861         }
27862         
27863         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27864
27865         if(this.isDocument){
27866             this.setThumbBoxSize();
27867             this.setThumbBoxPosition();
27868             this.setCanvasPosition();
27869         }
27870         
27871         this.draw();
27872         
27873         this.fireEvent('rotate', this, 'right');
27874     },
27875     
27876     onRotateFail : function()
27877     {
27878         this.errorEl.show(true);
27879         
27880         var _this = this;
27881         
27882         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27883     },
27884     
27885     draw : function()
27886     {
27887         this.previewEl.dom.innerHTML = '';
27888         
27889         var canvasEl = document.createElement("canvas");
27890         
27891         var contextEl = canvasEl.getContext("2d");
27892         
27893         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27894         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27895         var center = this.imageEl.OriginWidth / 2;
27896         
27897         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27898             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27899             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27900             center = this.imageEl.OriginHeight / 2;
27901         }
27902         
27903         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27904         
27905         contextEl.translate(center, center);
27906         contextEl.rotate(this.rotate * Math.PI / 180);
27907
27908         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27909         
27910         this.canvasEl = document.createElement("canvas");
27911         
27912         this.contextEl = this.canvasEl.getContext("2d");
27913         
27914         switch (this.rotate) {
27915             case 0 :
27916                 
27917                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27918                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27919                 
27920                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27921                 
27922                 break;
27923             case 90 : 
27924                 
27925                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27926                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27927                 
27928                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27929                     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);
27930                     break;
27931                 }
27932                 
27933                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27934                 
27935                 break;
27936             case 180 :
27937                 
27938                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27939                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27940                 
27941                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27942                     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);
27943                     break;
27944                 }
27945                 
27946                 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);
27947                 
27948                 break;
27949             case 270 :
27950                 
27951                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27952                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27953         
27954                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27955                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27956                     break;
27957                 }
27958                 
27959                 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);
27960                 
27961                 break;
27962             default : 
27963                 break;
27964         }
27965         
27966         this.previewEl.appendChild(this.canvasEl);
27967         
27968         this.setCanvasPosition();
27969     },
27970     
27971     crop : function()
27972     {
27973         if(!this.canvasLoaded){
27974             return;
27975         }
27976         
27977         var imageCanvas = document.createElement("canvas");
27978         
27979         var imageContext = imageCanvas.getContext("2d");
27980         
27981         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27982         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27983         
27984         var center = imageCanvas.width / 2;
27985         
27986         imageContext.translate(center, center);
27987         
27988         imageContext.rotate(this.rotate * Math.PI / 180);
27989         
27990         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27991         
27992         var canvas = document.createElement("canvas");
27993         
27994         var context = canvas.getContext("2d");
27995                 
27996         canvas.width = this.minWidth;
27997         canvas.height = this.minHeight;
27998
27999         switch (this.rotate) {
28000             case 0 :
28001                 
28002                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28003                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28004                 
28005                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28006                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28007                 
28008                 var targetWidth = this.minWidth - 2 * x;
28009                 var targetHeight = this.minHeight - 2 * y;
28010                 
28011                 var scale = 1;
28012                 
28013                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28014                     scale = targetWidth / width;
28015                 }
28016                 
28017                 if(x > 0 && y == 0){
28018                     scale = targetHeight / height;
28019                 }
28020                 
28021                 if(x > 0 && y > 0){
28022                     scale = targetWidth / width;
28023                     
28024                     if(width < height){
28025                         scale = targetHeight / height;
28026                     }
28027                 }
28028                 
28029                 context.scale(scale, scale);
28030                 
28031                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28032                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28033
28034                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28035                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28036
28037                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28038                 
28039                 break;
28040             case 90 : 
28041                 
28042                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28043                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28044                 
28045                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28046                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28047                 
28048                 var targetWidth = this.minWidth - 2 * x;
28049                 var targetHeight = this.minHeight - 2 * y;
28050                 
28051                 var scale = 1;
28052                 
28053                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28054                     scale = targetWidth / width;
28055                 }
28056                 
28057                 if(x > 0 && y == 0){
28058                     scale = targetHeight / height;
28059                 }
28060                 
28061                 if(x > 0 && y > 0){
28062                     scale = targetWidth / width;
28063                     
28064                     if(width < height){
28065                         scale = targetHeight / height;
28066                     }
28067                 }
28068                 
28069                 context.scale(scale, scale);
28070                 
28071                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28072                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28073
28074                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28075                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28076                 
28077                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28078                 
28079                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28080                 
28081                 break;
28082             case 180 :
28083                 
28084                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28085                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28086                 
28087                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28088                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28089                 
28090                 var targetWidth = this.minWidth - 2 * x;
28091                 var targetHeight = this.minHeight - 2 * y;
28092                 
28093                 var scale = 1;
28094                 
28095                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28096                     scale = targetWidth / width;
28097                 }
28098                 
28099                 if(x > 0 && y == 0){
28100                     scale = targetHeight / height;
28101                 }
28102                 
28103                 if(x > 0 && y > 0){
28104                     scale = targetWidth / width;
28105                     
28106                     if(width < height){
28107                         scale = targetHeight / height;
28108                     }
28109                 }
28110                 
28111                 context.scale(scale, scale);
28112                 
28113                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28114                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28115
28116                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28117                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28118
28119                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28120                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28121                 
28122                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28123                 
28124                 break;
28125             case 270 :
28126                 
28127                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28128                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28129                 
28130                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28131                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28132                 
28133                 var targetWidth = this.minWidth - 2 * x;
28134                 var targetHeight = this.minHeight - 2 * y;
28135                 
28136                 var scale = 1;
28137                 
28138                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28139                     scale = targetWidth / width;
28140                 }
28141                 
28142                 if(x > 0 && y == 0){
28143                     scale = targetHeight / height;
28144                 }
28145                 
28146                 if(x > 0 && y > 0){
28147                     scale = targetWidth / width;
28148                     
28149                     if(width < height){
28150                         scale = targetHeight / height;
28151                     }
28152                 }
28153                 
28154                 context.scale(scale, scale);
28155                 
28156                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28157                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28158
28159                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28160                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28161                 
28162                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28163                 
28164                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28165                 
28166                 break;
28167             default : 
28168                 break;
28169         }
28170         
28171         this.cropData = canvas.toDataURL(this.cropType);
28172         
28173         if(this.fireEvent('crop', this, this.cropData) !== false){
28174             this.process(this.file, this.cropData);
28175         }
28176         
28177         return;
28178         
28179     },
28180     
28181     setThumbBoxSize : function()
28182     {
28183         var width, height;
28184         
28185         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28186             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28187             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28188             
28189             this.minWidth = width;
28190             this.minHeight = height;
28191             
28192             if(this.rotate == 90 || this.rotate == 270){
28193                 this.minWidth = height;
28194                 this.minHeight = width;
28195             }
28196         }
28197         
28198         height = 300;
28199         width = Math.ceil(this.minWidth * height / this.minHeight);
28200         
28201         if(this.minWidth > this.minHeight){
28202             width = 300;
28203             height = Math.ceil(this.minHeight * width / this.minWidth);
28204         }
28205         
28206         this.thumbEl.setStyle({
28207             width : width + 'px',
28208             height : height + 'px'
28209         });
28210
28211         return;
28212             
28213     },
28214     
28215     setThumbBoxPosition : function()
28216     {
28217         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28218         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28219         
28220         this.thumbEl.setLeft(x);
28221         this.thumbEl.setTop(y);
28222         
28223     },
28224     
28225     baseRotateLevel : function()
28226     {
28227         this.baseRotate = 1;
28228         
28229         if(
28230                 typeof(this.exif) != 'undefined' &&
28231                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28232                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28233         ){
28234             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28235         }
28236         
28237         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28238         
28239     },
28240     
28241     baseScaleLevel : function()
28242     {
28243         var width, height;
28244         
28245         if(this.isDocument){
28246             
28247             if(this.baseRotate == 6 || this.baseRotate == 8){
28248             
28249                 height = this.thumbEl.getHeight();
28250                 this.baseScale = height / this.imageEl.OriginWidth;
28251
28252                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28253                     width = this.thumbEl.getWidth();
28254                     this.baseScale = width / this.imageEl.OriginHeight;
28255                 }
28256
28257                 return;
28258             }
28259
28260             height = this.thumbEl.getHeight();
28261             this.baseScale = height / this.imageEl.OriginHeight;
28262
28263             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28264                 width = this.thumbEl.getWidth();
28265                 this.baseScale = width / this.imageEl.OriginWidth;
28266             }
28267
28268             return;
28269         }
28270         
28271         if(this.baseRotate == 6 || this.baseRotate == 8){
28272             
28273             width = this.thumbEl.getHeight();
28274             this.baseScale = width / this.imageEl.OriginHeight;
28275             
28276             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28277                 height = this.thumbEl.getWidth();
28278                 this.baseScale = height / this.imageEl.OriginHeight;
28279             }
28280             
28281             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28282                 height = this.thumbEl.getWidth();
28283                 this.baseScale = height / this.imageEl.OriginHeight;
28284                 
28285                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28286                     width = this.thumbEl.getHeight();
28287                     this.baseScale = width / this.imageEl.OriginWidth;
28288                 }
28289             }
28290             
28291             return;
28292         }
28293         
28294         width = this.thumbEl.getWidth();
28295         this.baseScale = width / this.imageEl.OriginWidth;
28296         
28297         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28298             height = this.thumbEl.getHeight();
28299             this.baseScale = height / this.imageEl.OriginHeight;
28300         }
28301         
28302         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28303             
28304             height = this.thumbEl.getHeight();
28305             this.baseScale = height / this.imageEl.OriginHeight;
28306             
28307             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28308                 width = this.thumbEl.getWidth();
28309                 this.baseScale = width / this.imageEl.OriginWidth;
28310             }
28311             
28312         }
28313         
28314         return;
28315     },
28316     
28317     getScaleLevel : function()
28318     {
28319         return this.baseScale * Math.pow(1.1, this.scale);
28320     },
28321     
28322     onTouchStart : function(e)
28323     {
28324         if(!this.canvasLoaded){
28325             this.beforeSelectFile(e);
28326             return;
28327         }
28328         
28329         var touches = e.browserEvent.touches;
28330         
28331         if(!touches){
28332             return;
28333         }
28334         
28335         if(touches.length == 1){
28336             this.onMouseDown(e);
28337             return;
28338         }
28339         
28340         if(touches.length != 2){
28341             return;
28342         }
28343         
28344         var coords = [];
28345         
28346         for(var i = 0, finger; finger = touches[i]; i++){
28347             coords.push(finger.pageX, finger.pageY);
28348         }
28349         
28350         var x = Math.pow(coords[0] - coords[2], 2);
28351         var y = Math.pow(coords[1] - coords[3], 2);
28352         
28353         this.startDistance = Math.sqrt(x + y);
28354         
28355         this.startScale = this.scale;
28356         
28357         this.pinching = true;
28358         this.dragable = false;
28359         
28360     },
28361     
28362     onTouchMove : function(e)
28363     {
28364         if(!this.pinching && !this.dragable){
28365             return;
28366         }
28367         
28368         var touches = e.browserEvent.touches;
28369         
28370         if(!touches){
28371             return;
28372         }
28373         
28374         if(this.dragable){
28375             this.onMouseMove(e);
28376             return;
28377         }
28378         
28379         var coords = [];
28380         
28381         for(var i = 0, finger; finger = touches[i]; i++){
28382             coords.push(finger.pageX, finger.pageY);
28383         }
28384         
28385         var x = Math.pow(coords[0] - coords[2], 2);
28386         var y = Math.pow(coords[1] - coords[3], 2);
28387         
28388         this.endDistance = Math.sqrt(x + y);
28389         
28390         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28391         
28392         if(!this.zoomable()){
28393             this.scale = this.startScale;
28394             return;
28395         }
28396         
28397         this.draw();
28398         
28399     },
28400     
28401     onTouchEnd : function(e)
28402     {
28403         this.pinching = false;
28404         this.dragable = false;
28405         
28406     },
28407     
28408     process : function(file, crop)
28409     {
28410         if(this.loadMask){
28411             this.maskEl.mask(this.loadingText);
28412         }
28413         
28414         this.xhr = new XMLHttpRequest();
28415         
28416         file.xhr = this.xhr;
28417
28418         this.xhr.open(this.method, this.url, true);
28419         
28420         var headers = {
28421             "Accept": "application/json",
28422             "Cache-Control": "no-cache",
28423             "X-Requested-With": "XMLHttpRequest"
28424         };
28425         
28426         for (var headerName in headers) {
28427             var headerValue = headers[headerName];
28428             if (headerValue) {
28429                 this.xhr.setRequestHeader(headerName, headerValue);
28430             }
28431         }
28432         
28433         var _this = this;
28434         
28435         this.xhr.onload = function()
28436         {
28437             _this.xhrOnLoad(_this.xhr);
28438         }
28439         
28440         this.xhr.onerror = function()
28441         {
28442             _this.xhrOnError(_this.xhr);
28443         }
28444         
28445         var formData = new FormData();
28446
28447         formData.append('returnHTML', 'NO');
28448         
28449         if(crop){
28450             formData.append('crop', crop);
28451         }
28452         
28453         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28454             formData.append(this.paramName, file, file.name);
28455         }
28456         
28457         if(typeof(file.filename) != 'undefined'){
28458             formData.append('filename', file.filename);
28459         }
28460         
28461         if(typeof(file.mimetype) != 'undefined'){
28462             formData.append('mimetype', file.mimetype);
28463         }
28464         
28465         if(this.fireEvent('arrange', this, formData) != false){
28466             this.xhr.send(formData);
28467         };
28468     },
28469     
28470     xhrOnLoad : function(xhr)
28471     {
28472         if(this.loadMask){
28473             this.maskEl.unmask();
28474         }
28475         
28476         if (xhr.readyState !== 4) {
28477             this.fireEvent('exception', this, xhr);
28478             return;
28479         }
28480
28481         var response = Roo.decode(xhr.responseText);
28482         
28483         if(!response.success){
28484             this.fireEvent('exception', this, xhr);
28485             return;
28486         }
28487         
28488         var response = Roo.decode(xhr.responseText);
28489         
28490         this.fireEvent('upload', this, response);
28491         
28492     },
28493     
28494     xhrOnError : function()
28495     {
28496         if(this.loadMask){
28497             this.maskEl.unmask();
28498         }
28499         
28500         Roo.log('xhr on error');
28501         
28502         var response = Roo.decode(xhr.responseText);
28503           
28504         Roo.log(response);
28505         
28506     },
28507     
28508     prepare : function(file)
28509     {   
28510         if(this.loadMask){
28511             this.maskEl.mask(this.loadingText);
28512         }
28513         
28514         this.file = false;
28515         this.exif = {};
28516         
28517         if(typeof(file) === 'string'){
28518             this.loadCanvas(file);
28519             return;
28520         }
28521         
28522         if(!file || !this.urlAPI){
28523             return;
28524         }
28525         
28526         this.file = file;
28527         this.cropType = file.type;
28528         
28529         var _this = this;
28530         
28531         if(this.fireEvent('prepare', this, this.file) != false){
28532             
28533             var reader = new FileReader();
28534             
28535             reader.onload = function (e) {
28536                 if (e.target.error) {
28537                     Roo.log(e.target.error);
28538                     return;
28539                 }
28540                 
28541                 var buffer = e.target.result,
28542                     dataView = new DataView(buffer),
28543                     offset = 2,
28544                     maxOffset = dataView.byteLength - 4,
28545                     markerBytes,
28546                     markerLength;
28547                 
28548                 if (dataView.getUint16(0) === 0xffd8) {
28549                     while (offset < maxOffset) {
28550                         markerBytes = dataView.getUint16(offset);
28551                         
28552                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28553                             markerLength = dataView.getUint16(offset + 2) + 2;
28554                             if (offset + markerLength > dataView.byteLength) {
28555                                 Roo.log('Invalid meta data: Invalid segment size.');
28556                                 break;
28557                             }
28558                             
28559                             if(markerBytes == 0xffe1){
28560                                 _this.parseExifData(
28561                                     dataView,
28562                                     offset,
28563                                     markerLength
28564                                 );
28565                             }
28566                             
28567                             offset += markerLength;
28568                             
28569                             continue;
28570                         }
28571                         
28572                         break;
28573                     }
28574                     
28575                 }
28576                 
28577                 var url = _this.urlAPI.createObjectURL(_this.file);
28578                 
28579                 _this.loadCanvas(url);
28580                 
28581                 return;
28582             }
28583             
28584             reader.readAsArrayBuffer(this.file);
28585             
28586         }
28587         
28588     },
28589     
28590     parseExifData : function(dataView, offset, length)
28591     {
28592         var tiffOffset = offset + 10,
28593             littleEndian,
28594             dirOffset;
28595     
28596         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28597             // No Exif data, might be XMP data instead
28598             return;
28599         }
28600         
28601         // Check for the ASCII code for "Exif" (0x45786966):
28602         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28603             // No Exif data, might be XMP data instead
28604             return;
28605         }
28606         if (tiffOffset + 8 > dataView.byteLength) {
28607             Roo.log('Invalid Exif data: Invalid segment size.');
28608             return;
28609         }
28610         // Check for the two null bytes:
28611         if (dataView.getUint16(offset + 8) !== 0x0000) {
28612             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28613             return;
28614         }
28615         // Check the byte alignment:
28616         switch (dataView.getUint16(tiffOffset)) {
28617         case 0x4949:
28618             littleEndian = true;
28619             break;
28620         case 0x4D4D:
28621             littleEndian = false;
28622             break;
28623         default:
28624             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28625             return;
28626         }
28627         // Check for the TIFF tag marker (0x002A):
28628         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28629             Roo.log('Invalid Exif data: Missing TIFF marker.');
28630             return;
28631         }
28632         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28633         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28634         
28635         this.parseExifTags(
28636             dataView,
28637             tiffOffset,
28638             tiffOffset + dirOffset,
28639             littleEndian
28640         );
28641     },
28642     
28643     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28644     {
28645         var tagsNumber,
28646             dirEndOffset,
28647             i;
28648         if (dirOffset + 6 > dataView.byteLength) {
28649             Roo.log('Invalid Exif data: Invalid directory offset.');
28650             return;
28651         }
28652         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28653         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28654         if (dirEndOffset + 4 > dataView.byteLength) {
28655             Roo.log('Invalid Exif data: Invalid directory size.');
28656             return;
28657         }
28658         for (i = 0; i < tagsNumber; i += 1) {
28659             this.parseExifTag(
28660                 dataView,
28661                 tiffOffset,
28662                 dirOffset + 2 + 12 * i, // tag offset
28663                 littleEndian
28664             );
28665         }
28666         // Return the offset to the next directory:
28667         return dataView.getUint32(dirEndOffset, littleEndian);
28668     },
28669     
28670     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28671     {
28672         var tag = dataView.getUint16(offset, littleEndian);
28673         
28674         this.exif[tag] = this.getExifValue(
28675             dataView,
28676             tiffOffset,
28677             offset,
28678             dataView.getUint16(offset + 2, littleEndian), // tag type
28679             dataView.getUint32(offset + 4, littleEndian), // tag length
28680             littleEndian
28681         );
28682     },
28683     
28684     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28685     {
28686         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28687             tagSize,
28688             dataOffset,
28689             values,
28690             i,
28691             str,
28692             c;
28693     
28694         if (!tagType) {
28695             Roo.log('Invalid Exif data: Invalid tag type.');
28696             return;
28697         }
28698         
28699         tagSize = tagType.size * length;
28700         // Determine if the value is contained in the dataOffset bytes,
28701         // or if the value at the dataOffset is a pointer to the actual data:
28702         dataOffset = tagSize > 4 ?
28703                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28704         if (dataOffset + tagSize > dataView.byteLength) {
28705             Roo.log('Invalid Exif data: Invalid data offset.');
28706             return;
28707         }
28708         if (length === 1) {
28709             return tagType.getValue(dataView, dataOffset, littleEndian);
28710         }
28711         values = [];
28712         for (i = 0; i < length; i += 1) {
28713             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28714         }
28715         
28716         if (tagType.ascii) {
28717             str = '';
28718             // Concatenate the chars:
28719             for (i = 0; i < values.length; i += 1) {
28720                 c = values[i];
28721                 // Ignore the terminating NULL byte(s):
28722                 if (c === '\u0000') {
28723                     break;
28724                 }
28725                 str += c;
28726             }
28727             return str;
28728         }
28729         return values;
28730     }
28731     
28732 });
28733
28734 Roo.apply(Roo.bootstrap.UploadCropbox, {
28735     tags : {
28736         'Orientation': 0x0112
28737     },
28738     
28739     Orientation: {
28740             1: 0, //'top-left',
28741 //            2: 'top-right',
28742             3: 180, //'bottom-right',
28743 //            4: 'bottom-left',
28744 //            5: 'left-top',
28745             6: 90, //'right-top',
28746 //            7: 'right-bottom',
28747             8: 270 //'left-bottom'
28748     },
28749     
28750     exifTagTypes : {
28751         // byte, 8-bit unsigned int:
28752         1: {
28753             getValue: function (dataView, dataOffset) {
28754                 return dataView.getUint8(dataOffset);
28755             },
28756             size: 1
28757         },
28758         // ascii, 8-bit byte:
28759         2: {
28760             getValue: function (dataView, dataOffset) {
28761                 return String.fromCharCode(dataView.getUint8(dataOffset));
28762             },
28763             size: 1,
28764             ascii: true
28765         },
28766         // short, 16 bit int:
28767         3: {
28768             getValue: function (dataView, dataOffset, littleEndian) {
28769                 return dataView.getUint16(dataOffset, littleEndian);
28770             },
28771             size: 2
28772         },
28773         // long, 32 bit int:
28774         4: {
28775             getValue: function (dataView, dataOffset, littleEndian) {
28776                 return dataView.getUint32(dataOffset, littleEndian);
28777             },
28778             size: 4
28779         },
28780         // rational = two long values, first is numerator, second is denominator:
28781         5: {
28782             getValue: function (dataView, dataOffset, littleEndian) {
28783                 return dataView.getUint32(dataOffset, littleEndian) /
28784                     dataView.getUint32(dataOffset + 4, littleEndian);
28785             },
28786             size: 8
28787         },
28788         // slong, 32 bit signed int:
28789         9: {
28790             getValue: function (dataView, dataOffset, littleEndian) {
28791                 return dataView.getInt32(dataOffset, littleEndian);
28792             },
28793             size: 4
28794         },
28795         // srational, two slongs, first is numerator, second is denominator:
28796         10: {
28797             getValue: function (dataView, dataOffset, littleEndian) {
28798                 return dataView.getInt32(dataOffset, littleEndian) /
28799                     dataView.getInt32(dataOffset + 4, littleEndian);
28800             },
28801             size: 8
28802         }
28803     },
28804     
28805     footer : {
28806         STANDARD : [
28807             {
28808                 tag : 'div',
28809                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28810                 action : 'rotate-left',
28811                 cn : [
28812                     {
28813                         tag : 'button',
28814                         cls : 'btn btn-default',
28815                         html : '<i class="fa fa-undo"></i>'
28816                     }
28817                 ]
28818             },
28819             {
28820                 tag : 'div',
28821                 cls : 'btn-group roo-upload-cropbox-picture',
28822                 action : 'picture',
28823                 cn : [
28824                     {
28825                         tag : 'button',
28826                         cls : 'btn btn-default',
28827                         html : '<i class="fa fa-picture-o"></i>'
28828                     }
28829                 ]
28830             },
28831             {
28832                 tag : 'div',
28833                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28834                 action : 'rotate-right',
28835                 cn : [
28836                     {
28837                         tag : 'button',
28838                         cls : 'btn btn-default',
28839                         html : '<i class="fa fa-repeat"></i>'
28840                     }
28841                 ]
28842             }
28843         ],
28844         DOCUMENT : [
28845             {
28846                 tag : 'div',
28847                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28848                 action : 'rotate-left',
28849                 cn : [
28850                     {
28851                         tag : 'button',
28852                         cls : 'btn btn-default',
28853                         html : '<i class="fa fa-undo"></i>'
28854                     }
28855                 ]
28856             },
28857             {
28858                 tag : 'div',
28859                 cls : 'btn-group roo-upload-cropbox-download',
28860                 action : 'download',
28861                 cn : [
28862                     {
28863                         tag : 'button',
28864                         cls : 'btn btn-default',
28865                         html : '<i class="fa fa-download"></i>'
28866                     }
28867                 ]
28868             },
28869             {
28870                 tag : 'div',
28871                 cls : 'btn-group roo-upload-cropbox-crop',
28872                 action : 'crop',
28873                 cn : [
28874                     {
28875                         tag : 'button',
28876                         cls : 'btn btn-default',
28877                         html : '<i class="fa fa-crop"></i>'
28878                     }
28879                 ]
28880             },
28881             {
28882                 tag : 'div',
28883                 cls : 'btn-group roo-upload-cropbox-trash',
28884                 action : 'trash',
28885                 cn : [
28886                     {
28887                         tag : 'button',
28888                         cls : 'btn btn-default',
28889                         html : '<i class="fa fa-trash"></i>'
28890                     }
28891                 ]
28892             },
28893             {
28894                 tag : 'div',
28895                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28896                 action : 'rotate-right',
28897                 cn : [
28898                     {
28899                         tag : 'button',
28900                         cls : 'btn btn-default',
28901                         html : '<i class="fa fa-repeat"></i>'
28902                     }
28903                 ]
28904             }
28905         ],
28906         ROTATOR : [
28907             {
28908                 tag : 'div',
28909                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28910                 action : 'rotate-left',
28911                 cn : [
28912                     {
28913                         tag : 'button',
28914                         cls : 'btn btn-default',
28915                         html : '<i class="fa fa-undo"></i>'
28916                     }
28917                 ]
28918             },
28919             {
28920                 tag : 'div',
28921                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28922                 action : 'rotate-right',
28923                 cn : [
28924                     {
28925                         tag : 'button',
28926                         cls : 'btn btn-default',
28927                         html : '<i class="fa fa-repeat"></i>'
28928                     }
28929                 ]
28930             }
28931         ]
28932     }
28933 });
28934
28935 /*
28936 * Licence: LGPL
28937 */
28938
28939 /**
28940  * @class Roo.bootstrap.DocumentManager
28941  * @extends Roo.bootstrap.Component
28942  * Bootstrap DocumentManager class
28943  * @cfg {String} paramName default 'imageUpload'
28944  * @cfg {String} toolTipName default 'filename'
28945  * @cfg {String} method default POST
28946  * @cfg {String} url action url
28947  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28948  * @cfg {Boolean} multiple multiple upload default true
28949  * @cfg {Number} thumbSize default 300
28950  * @cfg {String} fieldLabel
28951  * @cfg {Number} labelWidth default 4
28952  * @cfg {String} labelAlign (left|top) default left
28953  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28954 * @cfg {Number} labellg set the width of label (1-12)
28955  * @cfg {Number} labelmd set the width of label (1-12)
28956  * @cfg {Number} labelsm set the width of label (1-12)
28957  * @cfg {Number} labelxs set the width of label (1-12)
28958  * 
28959  * @constructor
28960  * Create a new DocumentManager
28961  * @param {Object} config The config object
28962  */
28963
28964 Roo.bootstrap.DocumentManager = function(config){
28965     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28966     
28967     this.files = [];
28968     this.delegates = [];
28969     
28970     this.addEvents({
28971         /**
28972          * @event initial
28973          * Fire when initial the DocumentManager
28974          * @param {Roo.bootstrap.DocumentManager} this
28975          */
28976         "initial" : true,
28977         /**
28978          * @event inspect
28979          * inspect selected file
28980          * @param {Roo.bootstrap.DocumentManager} this
28981          * @param {File} file
28982          */
28983         "inspect" : true,
28984         /**
28985          * @event exception
28986          * Fire when xhr load exception
28987          * @param {Roo.bootstrap.DocumentManager} this
28988          * @param {XMLHttpRequest} xhr
28989          */
28990         "exception" : true,
28991         /**
28992          * @event afterupload
28993          * Fire when xhr load exception
28994          * @param {Roo.bootstrap.DocumentManager} this
28995          * @param {XMLHttpRequest} xhr
28996          */
28997         "afterupload" : true,
28998         /**
28999          * @event prepare
29000          * prepare the form data
29001          * @param {Roo.bootstrap.DocumentManager} this
29002          * @param {Object} formData
29003          */
29004         "prepare" : true,
29005         /**
29006          * @event remove
29007          * Fire when remove the file
29008          * @param {Roo.bootstrap.DocumentManager} this
29009          * @param {Object} file
29010          */
29011         "remove" : true,
29012         /**
29013          * @event refresh
29014          * Fire after refresh the file
29015          * @param {Roo.bootstrap.DocumentManager} this
29016          */
29017         "refresh" : true,
29018         /**
29019          * @event click
29020          * Fire after click the image
29021          * @param {Roo.bootstrap.DocumentManager} this
29022          * @param {Object} file
29023          */
29024         "click" : true,
29025         /**
29026          * @event edit
29027          * Fire when upload a image and editable set to true
29028          * @param {Roo.bootstrap.DocumentManager} this
29029          * @param {Object} file
29030          */
29031         "edit" : true,
29032         /**
29033          * @event beforeselectfile
29034          * Fire before select file
29035          * @param {Roo.bootstrap.DocumentManager} this
29036          */
29037         "beforeselectfile" : true,
29038         /**
29039          * @event process
29040          * Fire before process file
29041          * @param {Roo.bootstrap.DocumentManager} this
29042          * @param {Object} file
29043          */
29044         "process" : true,
29045         /**
29046          * @event previewrendered
29047          * Fire when preview rendered
29048          * @param {Roo.bootstrap.DocumentManager} this
29049          * @param {Object} file
29050          */
29051         "previewrendered" : true,
29052         /**
29053          */
29054         "previewResize" : true
29055         
29056     });
29057 };
29058
29059 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29060     
29061     boxes : 0,
29062     inputName : '',
29063     thumbSize : 300,
29064     multiple : true,
29065     files : false,
29066     method : 'POST',
29067     url : '',
29068     paramName : 'imageUpload',
29069     toolTipName : 'filename',
29070     fieldLabel : '',
29071     labelWidth : 4,
29072     labelAlign : 'left',
29073     editable : true,
29074     delegates : false,
29075     xhr : false, 
29076     
29077     labellg : 0,
29078     labelmd : 0,
29079     labelsm : 0,
29080     labelxs : 0,
29081     
29082     getAutoCreate : function()
29083     {   
29084         var managerWidget = {
29085             tag : 'div',
29086             cls : 'roo-document-manager',
29087             cn : [
29088                 {
29089                     tag : 'input',
29090                     cls : 'roo-document-manager-selector',
29091                     type : 'file'
29092                 },
29093                 {
29094                     tag : 'div',
29095                     cls : 'roo-document-manager-uploader',
29096                     cn : [
29097                         {
29098                             tag : 'div',
29099                             cls : 'roo-document-manager-upload-btn',
29100                             html : '<i class="fa fa-plus"></i>'
29101                         }
29102                     ]
29103                     
29104                 }
29105             ]
29106         };
29107         
29108         var content = [
29109             {
29110                 tag : 'div',
29111                 cls : 'column col-md-12',
29112                 cn : managerWidget
29113             }
29114         ];
29115         
29116         if(this.fieldLabel.length){
29117             
29118             content = [
29119                 {
29120                     tag : 'div',
29121                     cls : 'column col-md-12',
29122                     html : this.fieldLabel
29123                 },
29124                 {
29125                     tag : 'div',
29126                     cls : 'column col-md-12',
29127                     cn : managerWidget
29128                 }
29129             ];
29130
29131             if(this.labelAlign == 'left'){
29132                 content = [
29133                     {
29134                         tag : 'div',
29135                         cls : 'column',
29136                         html : this.fieldLabel
29137                     },
29138                     {
29139                         tag : 'div',
29140                         cls : 'column',
29141                         cn : managerWidget
29142                     }
29143                 ];
29144                 
29145                 if(this.labelWidth > 12){
29146                     content[0].style = "width: " + this.labelWidth + 'px';
29147                 }
29148
29149                 if(this.labelWidth < 13 && this.labelmd == 0){
29150                     this.labelmd = this.labelWidth;
29151                 }
29152
29153                 if(this.labellg > 0){
29154                     content[0].cls += ' col-lg-' + this.labellg;
29155                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29156                 }
29157
29158                 if(this.labelmd > 0){
29159                     content[0].cls += ' col-md-' + this.labelmd;
29160                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29161                 }
29162
29163                 if(this.labelsm > 0){
29164                     content[0].cls += ' col-sm-' + this.labelsm;
29165                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29166                 }
29167
29168                 if(this.labelxs > 0){
29169                     content[0].cls += ' col-xs-' + this.labelxs;
29170                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29171                 }
29172                 
29173             }
29174         }
29175         
29176         var cfg = {
29177             tag : 'div',
29178             cls : 'row clearfix',
29179             cn : content
29180         };
29181         
29182         return cfg;
29183         
29184     },
29185     
29186     initEvents : function()
29187     {
29188         this.managerEl = this.el.select('.roo-document-manager', true).first();
29189         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29190         
29191         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29192         this.selectorEl.hide();
29193         
29194         if(this.multiple){
29195             this.selectorEl.attr('multiple', 'multiple');
29196         }
29197         
29198         this.selectorEl.on('change', this.onFileSelected, this);
29199         
29200         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29201         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29202         
29203         this.uploader.on('click', this.onUploaderClick, this);
29204         
29205         this.renderProgressDialog();
29206         
29207         var _this = this;
29208         
29209         window.addEventListener("resize", function() { _this.refresh(); } );
29210         
29211         this.fireEvent('initial', this);
29212     },
29213     
29214     renderProgressDialog : function()
29215     {
29216         var _this = this;
29217         
29218         this.progressDialog = new Roo.bootstrap.Modal({
29219             cls : 'roo-document-manager-progress-dialog',
29220             allow_close : false,
29221             animate : false,
29222             title : '',
29223             buttons : [
29224                 {
29225                     name  :'cancel',
29226                     weight : 'danger',
29227                     html : 'Cancel'
29228                 }
29229             ], 
29230             listeners : { 
29231                 btnclick : function() {
29232                     _this.uploadCancel();
29233                     this.hide();
29234                 }
29235             }
29236         });
29237          
29238         this.progressDialog.render(Roo.get(document.body));
29239          
29240         this.progress = new Roo.bootstrap.Progress({
29241             cls : 'roo-document-manager-progress',
29242             active : true,
29243             striped : true
29244         });
29245         
29246         this.progress.render(this.progressDialog.getChildContainer());
29247         
29248         this.progressBar = new Roo.bootstrap.ProgressBar({
29249             cls : 'roo-document-manager-progress-bar',
29250             aria_valuenow : 0,
29251             aria_valuemin : 0,
29252             aria_valuemax : 12,
29253             panel : 'success'
29254         });
29255         
29256         this.progressBar.render(this.progress.getChildContainer());
29257     },
29258     
29259     onUploaderClick : function(e)
29260     {
29261         e.preventDefault();
29262      
29263         if(this.fireEvent('beforeselectfile', this) != false){
29264             this.selectorEl.dom.click();
29265         }
29266         
29267     },
29268     
29269     onFileSelected : function(e)
29270     {
29271         e.preventDefault();
29272         
29273         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29274             return;
29275         }
29276         
29277         Roo.each(this.selectorEl.dom.files, function(file){
29278             if(this.fireEvent('inspect', this, file) != false){
29279                 this.files.push(file);
29280             }
29281         }, this);
29282         
29283         this.queue();
29284         
29285     },
29286     
29287     queue : function()
29288     {
29289         this.selectorEl.dom.value = '';
29290         
29291         if(!this.files || !this.files.length){
29292             return;
29293         }
29294         
29295         if(this.boxes > 0 && this.files.length > this.boxes){
29296             this.files = this.files.slice(0, this.boxes);
29297         }
29298         
29299         this.uploader.show();
29300         
29301         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29302             this.uploader.hide();
29303         }
29304         
29305         var _this = this;
29306         
29307         var files = [];
29308         
29309         var docs = [];
29310         
29311         Roo.each(this.files, function(file){
29312             
29313             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29314                 var f = this.renderPreview(file);
29315                 files.push(f);
29316                 return;
29317             }
29318             
29319             if(file.type.indexOf('image') != -1){
29320                 this.delegates.push(
29321                     (function(){
29322                         _this.process(file);
29323                     }).createDelegate(this)
29324                 );
29325         
29326                 return;
29327             }
29328             
29329             docs.push(
29330                 (function(){
29331                     _this.process(file);
29332                 }).createDelegate(this)
29333             );
29334             
29335         }, this);
29336         
29337         this.files = files;
29338         
29339         this.delegates = this.delegates.concat(docs);
29340         
29341         if(!this.delegates.length){
29342             this.refresh();
29343             return;
29344         }
29345         
29346         this.progressBar.aria_valuemax = this.delegates.length;
29347         
29348         this.arrange();
29349         
29350         return;
29351     },
29352     
29353     arrange : function()
29354     {
29355         if(!this.delegates.length){
29356             this.progressDialog.hide();
29357             this.refresh();
29358             return;
29359         }
29360         
29361         var delegate = this.delegates.shift();
29362         
29363         this.progressDialog.show();
29364         
29365         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29366         
29367         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29368         
29369         delegate();
29370     },
29371     
29372     refresh : function()
29373     {
29374         this.uploader.show();
29375         
29376         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29377             this.uploader.hide();
29378         }
29379         
29380         Roo.isTouch ? this.closable(false) : this.closable(true);
29381         
29382         this.fireEvent('refresh', this);
29383     },
29384     
29385     onRemove : function(e, el, o)
29386     {
29387         e.preventDefault();
29388         
29389         this.fireEvent('remove', this, o);
29390         
29391     },
29392     
29393     remove : function(o)
29394     {
29395         var files = [];
29396         
29397         Roo.each(this.files, function(file){
29398             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29399                 files.push(file);
29400                 return;
29401             }
29402
29403             o.target.remove();
29404
29405         }, this);
29406         
29407         this.files = files;
29408         
29409         this.refresh();
29410     },
29411     
29412     clear : function()
29413     {
29414         Roo.each(this.files, function(file){
29415             if(!file.target){
29416                 return;
29417             }
29418             
29419             file.target.remove();
29420
29421         }, this);
29422         
29423         this.files = [];
29424         
29425         this.refresh();
29426     },
29427     
29428     onClick : function(e, el, o)
29429     {
29430         e.preventDefault();
29431         
29432         this.fireEvent('click', this, o);
29433         
29434     },
29435     
29436     closable : function(closable)
29437     {
29438         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29439             
29440             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29441             
29442             if(closable){
29443                 el.show();
29444                 return;
29445             }
29446             
29447             el.hide();
29448             
29449         }, this);
29450     },
29451     
29452     xhrOnLoad : function(xhr)
29453     {
29454         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29455             el.remove();
29456         }, this);
29457         
29458         if (xhr.readyState !== 4) {
29459             this.arrange();
29460             this.fireEvent('exception', this, xhr);
29461             return;
29462         }
29463
29464         var response = Roo.decode(xhr.responseText);
29465         
29466         if(!response.success){
29467             this.arrange();
29468             this.fireEvent('exception', this, xhr);
29469             return;
29470         }
29471         
29472         var file = this.renderPreview(response.data);
29473         
29474         this.files.push(file);
29475         
29476         this.arrange();
29477         
29478         this.fireEvent('afterupload', this, xhr);
29479         
29480     },
29481     
29482     xhrOnError : function(xhr)
29483     {
29484         Roo.log('xhr on error');
29485         
29486         var response = Roo.decode(xhr.responseText);
29487           
29488         Roo.log(response);
29489         
29490         this.arrange();
29491     },
29492     
29493     process : function(file)
29494     {
29495         if(this.fireEvent('process', this, file) !== false){
29496             if(this.editable && file.type.indexOf('image') != -1){
29497                 this.fireEvent('edit', this, file);
29498                 return;
29499             }
29500
29501             this.uploadStart(file, false);
29502
29503             return;
29504         }
29505         
29506     },
29507     
29508     uploadStart : function(file, crop)
29509     {
29510         this.xhr = new XMLHttpRequest();
29511         
29512         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29513             this.arrange();
29514             return;
29515         }
29516         
29517         file.xhr = this.xhr;
29518             
29519         this.managerEl.createChild({
29520             tag : 'div',
29521             cls : 'roo-document-manager-loading',
29522             cn : [
29523                 {
29524                     tag : 'div',
29525                     tooltip : file.name,
29526                     cls : 'roo-document-manager-thumb',
29527                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29528                 }
29529             ]
29530
29531         });
29532
29533         this.xhr.open(this.method, this.url, true);
29534         
29535         var headers = {
29536             "Accept": "application/json",
29537             "Cache-Control": "no-cache",
29538             "X-Requested-With": "XMLHttpRequest"
29539         };
29540         
29541         for (var headerName in headers) {
29542             var headerValue = headers[headerName];
29543             if (headerValue) {
29544                 this.xhr.setRequestHeader(headerName, headerValue);
29545             }
29546         }
29547         
29548         var _this = this;
29549         
29550         this.xhr.onload = function()
29551         {
29552             _this.xhrOnLoad(_this.xhr);
29553         }
29554         
29555         this.xhr.onerror = function()
29556         {
29557             _this.xhrOnError(_this.xhr);
29558         }
29559         
29560         var formData = new FormData();
29561
29562         formData.append('returnHTML', 'NO');
29563         
29564         if(crop){
29565             formData.append('crop', crop);
29566         }
29567         
29568         formData.append(this.paramName, file, file.name);
29569         
29570         var options = {
29571             file : file, 
29572             manually : false
29573         };
29574         
29575         if(this.fireEvent('prepare', this, formData, options) != false){
29576             
29577             if(options.manually){
29578                 return;
29579             }
29580             
29581             this.xhr.send(formData);
29582             return;
29583         };
29584         
29585         this.uploadCancel();
29586     },
29587     
29588     uploadCancel : function()
29589     {
29590         if (this.xhr) {
29591             this.xhr.abort();
29592         }
29593         
29594         this.delegates = [];
29595         
29596         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29597             el.remove();
29598         }, this);
29599         
29600         this.arrange();
29601     },
29602     
29603     renderPreview : function(file)
29604     {
29605         if(typeof(file.target) != 'undefined' && file.target){
29606             return file;
29607         }
29608         
29609         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29610         
29611         var previewEl = this.managerEl.createChild({
29612             tag : 'div',
29613             cls : 'roo-document-manager-preview',
29614             cn : [
29615                 {
29616                     tag : 'div',
29617                     tooltip : file[this.toolTipName],
29618                     cls : 'roo-document-manager-thumb',
29619                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29620                 },
29621                 {
29622                     tag : 'button',
29623                     cls : 'close',
29624                     html : '<i class="fa fa-times-circle"></i>'
29625                 }
29626             ]
29627         });
29628
29629         var close = previewEl.select('button.close', true).first();
29630
29631         close.on('click', this.onRemove, this, file);
29632
29633         file.target = previewEl;
29634
29635         var image = previewEl.select('img', true).first();
29636         
29637         var _this = this;
29638         
29639         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29640         
29641         image.on('click', this.onClick, this, file);
29642         
29643         this.fireEvent('previewrendered', this, file);
29644         
29645         return file;
29646         
29647     },
29648     
29649     onPreviewLoad : function(file, image)
29650     {
29651         if(typeof(file.target) == 'undefined' || !file.target){
29652             return;
29653         }
29654         
29655         var width = image.dom.naturalWidth || image.dom.width;
29656         var height = image.dom.naturalHeight || image.dom.height;
29657         
29658         if(!this.previewResize) {
29659             return;
29660         }
29661         
29662         if(width > height){
29663             file.target.addClass('wide');
29664             return;
29665         }
29666         
29667         file.target.addClass('tall');
29668         return;
29669         
29670     },
29671     
29672     uploadFromSource : function(file, crop)
29673     {
29674         this.xhr = new XMLHttpRequest();
29675         
29676         this.managerEl.createChild({
29677             tag : 'div',
29678             cls : 'roo-document-manager-loading',
29679             cn : [
29680                 {
29681                     tag : 'div',
29682                     tooltip : file.name,
29683                     cls : 'roo-document-manager-thumb',
29684                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29685                 }
29686             ]
29687
29688         });
29689
29690         this.xhr.open(this.method, this.url, true);
29691         
29692         var headers = {
29693             "Accept": "application/json",
29694             "Cache-Control": "no-cache",
29695             "X-Requested-With": "XMLHttpRequest"
29696         };
29697         
29698         for (var headerName in headers) {
29699             var headerValue = headers[headerName];
29700             if (headerValue) {
29701                 this.xhr.setRequestHeader(headerName, headerValue);
29702             }
29703         }
29704         
29705         var _this = this;
29706         
29707         this.xhr.onload = function()
29708         {
29709             _this.xhrOnLoad(_this.xhr);
29710         }
29711         
29712         this.xhr.onerror = function()
29713         {
29714             _this.xhrOnError(_this.xhr);
29715         }
29716         
29717         var formData = new FormData();
29718
29719         formData.append('returnHTML', 'NO');
29720         
29721         formData.append('crop', crop);
29722         
29723         if(typeof(file.filename) != 'undefined'){
29724             formData.append('filename', file.filename);
29725         }
29726         
29727         if(typeof(file.mimetype) != 'undefined'){
29728             formData.append('mimetype', file.mimetype);
29729         }
29730         
29731         Roo.log(formData);
29732         
29733         if(this.fireEvent('prepare', this, formData) != false){
29734             this.xhr.send(formData);
29735         };
29736     }
29737 });
29738
29739 /*
29740 * Licence: LGPL
29741 */
29742
29743 /**
29744  * @class Roo.bootstrap.DocumentViewer
29745  * @extends Roo.bootstrap.Component
29746  * Bootstrap DocumentViewer class
29747  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29748  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29749  * 
29750  * @constructor
29751  * Create a new DocumentViewer
29752  * @param {Object} config The config object
29753  */
29754
29755 Roo.bootstrap.DocumentViewer = function(config){
29756     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29757     
29758     this.addEvents({
29759         /**
29760          * @event initial
29761          * Fire after initEvent
29762          * @param {Roo.bootstrap.DocumentViewer} this
29763          */
29764         "initial" : true,
29765         /**
29766          * @event click
29767          * Fire after click
29768          * @param {Roo.bootstrap.DocumentViewer} this
29769          */
29770         "click" : true,
29771         /**
29772          * @event download
29773          * Fire after download button
29774          * @param {Roo.bootstrap.DocumentViewer} this
29775          */
29776         "download" : true,
29777         /**
29778          * @event trash
29779          * Fire after trash button
29780          * @param {Roo.bootstrap.DocumentViewer} this
29781          */
29782         "trash" : true
29783         
29784     });
29785 };
29786
29787 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29788     
29789     showDownload : true,
29790     
29791     showTrash : true,
29792     
29793     getAutoCreate : function()
29794     {
29795         var cfg = {
29796             tag : 'div',
29797             cls : 'roo-document-viewer',
29798             cn : [
29799                 {
29800                     tag : 'div',
29801                     cls : 'roo-document-viewer-body',
29802                     cn : [
29803                         {
29804                             tag : 'div',
29805                             cls : 'roo-document-viewer-thumb',
29806                             cn : [
29807                                 {
29808                                     tag : 'img',
29809                                     cls : 'roo-document-viewer-image'
29810                                 }
29811                             ]
29812                         }
29813                     ]
29814                 },
29815                 {
29816                     tag : 'div',
29817                     cls : 'roo-document-viewer-footer',
29818                     cn : {
29819                         tag : 'div',
29820                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29821                         cn : [
29822                             {
29823                                 tag : 'div',
29824                                 cls : 'btn-group roo-document-viewer-download',
29825                                 cn : [
29826                                     {
29827                                         tag : 'button',
29828                                         cls : 'btn btn-default',
29829                                         html : '<i class="fa fa-download"></i>'
29830                                     }
29831                                 ]
29832                             },
29833                             {
29834                                 tag : 'div',
29835                                 cls : 'btn-group roo-document-viewer-trash',
29836                                 cn : [
29837                                     {
29838                                         tag : 'button',
29839                                         cls : 'btn btn-default',
29840                                         html : '<i class="fa fa-trash"></i>'
29841                                     }
29842                                 ]
29843                             }
29844                         ]
29845                     }
29846                 }
29847             ]
29848         };
29849         
29850         return cfg;
29851     },
29852     
29853     initEvents : function()
29854     {
29855         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29856         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29857         
29858         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29859         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29860         
29861         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29862         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29863         
29864         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29865         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29866         
29867         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29868         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29869         
29870         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29871         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29872         
29873         this.bodyEl.on('click', this.onClick, this);
29874         this.downloadBtn.on('click', this.onDownload, this);
29875         this.trashBtn.on('click', this.onTrash, this);
29876         
29877         this.downloadBtn.hide();
29878         this.trashBtn.hide();
29879         
29880         if(this.showDownload){
29881             this.downloadBtn.show();
29882         }
29883         
29884         if(this.showTrash){
29885             this.trashBtn.show();
29886         }
29887         
29888         if(!this.showDownload && !this.showTrash) {
29889             this.footerEl.hide();
29890         }
29891         
29892     },
29893     
29894     initial : function()
29895     {
29896         this.fireEvent('initial', this);
29897         
29898     },
29899     
29900     onClick : function(e)
29901     {
29902         e.preventDefault();
29903         
29904         this.fireEvent('click', this);
29905     },
29906     
29907     onDownload : function(e)
29908     {
29909         e.preventDefault();
29910         
29911         this.fireEvent('download', this);
29912     },
29913     
29914     onTrash : function(e)
29915     {
29916         e.preventDefault();
29917         
29918         this.fireEvent('trash', this);
29919     }
29920     
29921 });
29922 /*
29923  * - LGPL
29924  *
29925  * nav progress bar
29926  * 
29927  */
29928
29929 /**
29930  * @class Roo.bootstrap.NavProgressBar
29931  * @extends Roo.bootstrap.Component
29932  * Bootstrap NavProgressBar class
29933  * 
29934  * @constructor
29935  * Create a new nav progress bar
29936  * @param {Object} config The config object
29937  */
29938
29939 Roo.bootstrap.NavProgressBar = function(config){
29940     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29941
29942     this.bullets = this.bullets || [];
29943    
29944 //    Roo.bootstrap.NavProgressBar.register(this);
29945      this.addEvents({
29946         /**
29947              * @event changed
29948              * Fires when the active item changes
29949              * @param {Roo.bootstrap.NavProgressBar} this
29950              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29951              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29952          */
29953         'changed': true
29954      });
29955     
29956 };
29957
29958 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29959     
29960     bullets : [],
29961     barItems : [],
29962     
29963     getAutoCreate : function()
29964     {
29965         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29966         
29967         cfg = {
29968             tag : 'div',
29969             cls : 'roo-navigation-bar-group',
29970             cn : [
29971                 {
29972                     tag : 'div',
29973                     cls : 'roo-navigation-top-bar'
29974                 },
29975                 {
29976                     tag : 'div',
29977                     cls : 'roo-navigation-bullets-bar',
29978                     cn : [
29979                         {
29980                             tag : 'ul',
29981                             cls : 'roo-navigation-bar'
29982                         }
29983                     ]
29984                 },
29985                 
29986                 {
29987                     tag : 'div',
29988                     cls : 'roo-navigation-bottom-bar'
29989                 }
29990             ]
29991             
29992         };
29993         
29994         return cfg;
29995         
29996     },
29997     
29998     initEvents: function() 
29999     {
30000         
30001     },
30002     
30003     onRender : function(ct, position) 
30004     {
30005         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30006         
30007         if(this.bullets.length){
30008             Roo.each(this.bullets, function(b){
30009                this.addItem(b);
30010             }, this);
30011         }
30012         
30013         this.format();
30014         
30015     },
30016     
30017     addItem : function(cfg)
30018     {
30019         var item = new Roo.bootstrap.NavProgressItem(cfg);
30020         
30021         item.parentId = this.id;
30022         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30023         
30024         if(cfg.html){
30025             var top = new Roo.bootstrap.Element({
30026                 tag : 'div',
30027                 cls : 'roo-navigation-bar-text'
30028             });
30029             
30030             var bottom = new Roo.bootstrap.Element({
30031                 tag : 'div',
30032                 cls : 'roo-navigation-bar-text'
30033             });
30034             
30035             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30036             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30037             
30038             var topText = new Roo.bootstrap.Element({
30039                 tag : 'span',
30040                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30041             });
30042             
30043             var bottomText = new Roo.bootstrap.Element({
30044                 tag : 'span',
30045                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30046             });
30047             
30048             topText.onRender(top.el, null);
30049             bottomText.onRender(bottom.el, null);
30050             
30051             item.topEl = top;
30052             item.bottomEl = bottom;
30053         }
30054         
30055         this.barItems.push(item);
30056         
30057         return item;
30058     },
30059     
30060     getActive : function()
30061     {
30062         var active = false;
30063         
30064         Roo.each(this.barItems, function(v){
30065             
30066             if (!v.isActive()) {
30067                 return;
30068             }
30069             
30070             active = v;
30071             return false;
30072             
30073         });
30074         
30075         return active;
30076     },
30077     
30078     setActiveItem : function(item)
30079     {
30080         var prev = false;
30081         
30082         Roo.each(this.barItems, function(v){
30083             if (v.rid == item.rid) {
30084                 return ;
30085             }
30086             
30087             if (v.isActive()) {
30088                 v.setActive(false);
30089                 prev = v;
30090             }
30091         });
30092
30093         item.setActive(true);
30094         
30095         this.fireEvent('changed', this, item, prev);
30096     },
30097     
30098     getBarItem: function(rid)
30099     {
30100         var ret = false;
30101         
30102         Roo.each(this.barItems, function(e) {
30103             if (e.rid != rid) {
30104                 return;
30105             }
30106             
30107             ret =  e;
30108             return false;
30109         });
30110         
30111         return ret;
30112     },
30113     
30114     indexOfItem : function(item)
30115     {
30116         var index = false;
30117         
30118         Roo.each(this.barItems, function(v, i){
30119             
30120             if (v.rid != item.rid) {
30121                 return;
30122             }
30123             
30124             index = i;
30125             return false
30126         });
30127         
30128         return index;
30129     },
30130     
30131     setActiveNext : function()
30132     {
30133         var i = this.indexOfItem(this.getActive());
30134         
30135         if (i > this.barItems.length) {
30136             return;
30137         }
30138         
30139         this.setActiveItem(this.barItems[i+1]);
30140     },
30141     
30142     setActivePrev : function()
30143     {
30144         var i = this.indexOfItem(this.getActive());
30145         
30146         if (i  < 1) {
30147             return;
30148         }
30149         
30150         this.setActiveItem(this.barItems[i-1]);
30151     },
30152     
30153     format : function()
30154     {
30155         if(!this.barItems.length){
30156             return;
30157         }
30158      
30159         var width = 100 / this.barItems.length;
30160         
30161         Roo.each(this.barItems, function(i){
30162             i.el.setStyle('width', width + '%');
30163             i.topEl.el.setStyle('width', width + '%');
30164             i.bottomEl.el.setStyle('width', width + '%');
30165         }, this);
30166         
30167     }
30168     
30169 });
30170 /*
30171  * - LGPL
30172  *
30173  * Nav Progress Item
30174  * 
30175  */
30176
30177 /**
30178  * @class Roo.bootstrap.NavProgressItem
30179  * @extends Roo.bootstrap.Component
30180  * Bootstrap NavProgressItem class
30181  * @cfg {String} rid the reference id
30182  * @cfg {Boolean} active (true|false) Is item active default false
30183  * @cfg {Boolean} disabled (true|false) Is item active default false
30184  * @cfg {String} html
30185  * @cfg {String} position (top|bottom) text position default bottom
30186  * @cfg {String} icon show icon instead of number
30187  * 
30188  * @constructor
30189  * Create a new NavProgressItem
30190  * @param {Object} config The config object
30191  */
30192 Roo.bootstrap.NavProgressItem = function(config){
30193     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30194     this.addEvents({
30195         // raw events
30196         /**
30197          * @event click
30198          * The raw click event for the entire grid.
30199          * @param {Roo.bootstrap.NavProgressItem} this
30200          * @param {Roo.EventObject} e
30201          */
30202         "click" : true
30203     });
30204    
30205 };
30206
30207 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30208     
30209     rid : '',
30210     active : false,
30211     disabled : false,
30212     html : '',
30213     position : 'bottom',
30214     icon : false,
30215     
30216     getAutoCreate : function()
30217     {
30218         var iconCls = 'roo-navigation-bar-item-icon';
30219         
30220         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30221         
30222         var cfg = {
30223             tag: 'li',
30224             cls: 'roo-navigation-bar-item',
30225             cn : [
30226                 {
30227                     tag : 'i',
30228                     cls : iconCls
30229                 }
30230             ]
30231         };
30232         
30233         if(this.active){
30234             cfg.cls += ' active';
30235         }
30236         if(this.disabled){
30237             cfg.cls += ' disabled';
30238         }
30239         
30240         return cfg;
30241     },
30242     
30243     disable : function()
30244     {
30245         this.setDisabled(true);
30246     },
30247     
30248     enable : function()
30249     {
30250         this.setDisabled(false);
30251     },
30252     
30253     initEvents: function() 
30254     {
30255         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30256         
30257         this.iconEl.on('click', this.onClick, this);
30258     },
30259     
30260     onClick : function(e)
30261     {
30262         e.preventDefault();
30263         
30264         if(this.disabled){
30265             return;
30266         }
30267         
30268         if(this.fireEvent('click', this, e) === false){
30269             return;
30270         };
30271         
30272         this.parent().setActiveItem(this);
30273     },
30274     
30275     isActive: function () 
30276     {
30277         return this.active;
30278     },
30279     
30280     setActive : function(state)
30281     {
30282         if(this.active == state){
30283             return;
30284         }
30285         
30286         this.active = state;
30287         
30288         if (state) {
30289             this.el.addClass('active');
30290             return;
30291         }
30292         
30293         this.el.removeClass('active');
30294         
30295         return;
30296     },
30297     
30298     setDisabled : function(state)
30299     {
30300         if(this.disabled == state){
30301             return;
30302         }
30303         
30304         this.disabled = state;
30305         
30306         if (state) {
30307             this.el.addClass('disabled');
30308             return;
30309         }
30310         
30311         this.el.removeClass('disabled');
30312     },
30313     
30314     tooltipEl : function()
30315     {
30316         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30317     }
30318 });
30319  
30320
30321  /*
30322  * - LGPL
30323  *
30324  * FieldLabel
30325  * 
30326  */
30327
30328 /**
30329  * @class Roo.bootstrap.FieldLabel
30330  * @extends Roo.bootstrap.Component
30331  * Bootstrap FieldLabel class
30332  * @cfg {String} html contents of the element
30333  * @cfg {String} tag tag of the element default label
30334  * @cfg {String} cls class of the element
30335  * @cfg {String} target label target 
30336  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30337  * @cfg {String} invalidClass default "text-warning"
30338  * @cfg {String} validClass default "text-success"
30339  * @cfg {String} iconTooltip default "This field is required"
30340  * @cfg {String} indicatorpos (left|right) default left
30341  * 
30342  * @constructor
30343  * Create a new FieldLabel
30344  * @param {Object} config The config object
30345  */
30346
30347 Roo.bootstrap.FieldLabel = function(config){
30348     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30349     
30350     this.addEvents({
30351             /**
30352              * @event invalid
30353              * Fires after the field has been marked as invalid.
30354              * @param {Roo.form.FieldLabel} this
30355              * @param {String} msg The validation message
30356              */
30357             invalid : true,
30358             /**
30359              * @event valid
30360              * Fires after the field has been validated with no errors.
30361              * @param {Roo.form.FieldLabel} this
30362              */
30363             valid : true
30364         });
30365 };
30366
30367 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30368     
30369     tag: 'label',
30370     cls: '',
30371     html: '',
30372     target: '',
30373     allowBlank : true,
30374     invalidClass : 'has-warning',
30375     validClass : 'has-success',
30376     iconTooltip : 'This field is required',
30377     indicatorpos : 'left',
30378     
30379     getAutoCreate : function(){
30380         
30381         var cls = "";
30382         if (!this.allowBlank) {
30383             cls  = "visible";
30384         }
30385         
30386         var cfg = {
30387             tag : this.tag,
30388             cls : 'roo-bootstrap-field-label ' + this.cls,
30389             for : this.target,
30390             cn : [
30391                 {
30392                     tag : 'i',
30393                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30394                     tooltip : this.iconTooltip
30395                 },
30396                 {
30397                     tag : 'span',
30398                     html : this.html
30399                 }
30400             ] 
30401         };
30402         
30403         if(this.indicatorpos == 'right'){
30404             var cfg = {
30405                 tag : this.tag,
30406                 cls : 'roo-bootstrap-field-label ' + this.cls,
30407                 for : this.target,
30408                 cn : [
30409                     {
30410                         tag : 'span',
30411                         html : this.html
30412                     },
30413                     {
30414                         tag : 'i',
30415                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30416                         tooltip : this.iconTooltip
30417                     }
30418                 ] 
30419             };
30420         }
30421         
30422         return cfg;
30423     },
30424     
30425     initEvents: function() 
30426     {
30427         Roo.bootstrap.Element.superclass.initEvents.call(this);
30428         
30429         this.indicator = this.indicatorEl();
30430         
30431         if(this.indicator){
30432             this.indicator.removeClass('visible');
30433             this.indicator.addClass('invisible');
30434         }
30435         
30436         Roo.bootstrap.FieldLabel.register(this);
30437     },
30438     
30439     indicatorEl : function()
30440     {
30441         var indicator = this.el.select('i.roo-required-indicator',true).first();
30442         
30443         if(!indicator){
30444             return false;
30445         }
30446         
30447         return indicator;
30448         
30449     },
30450     
30451     /**
30452      * Mark this field as valid
30453      */
30454     markValid : function()
30455     {
30456         if(this.indicator){
30457             this.indicator.removeClass('visible');
30458             this.indicator.addClass('invisible');
30459         }
30460         
30461         this.el.removeClass(this.invalidClass);
30462         
30463         this.el.addClass(this.validClass);
30464         
30465         this.fireEvent('valid', this);
30466     },
30467     
30468     /**
30469      * Mark this field as invalid
30470      * @param {String} msg The validation message
30471      */
30472     markInvalid : function(msg)
30473     {
30474         if(this.indicator){
30475             this.indicator.removeClass('invisible');
30476             this.indicator.addClass('visible');
30477         }
30478         
30479         this.el.removeClass(this.validClass);
30480         
30481         this.el.addClass(this.invalidClass);
30482         
30483         this.fireEvent('invalid', this, msg);
30484     }
30485     
30486    
30487 });
30488
30489 Roo.apply(Roo.bootstrap.FieldLabel, {
30490     
30491     groups: {},
30492     
30493      /**
30494     * register a FieldLabel Group
30495     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30496     */
30497     register : function(label)
30498     {
30499         if(this.groups.hasOwnProperty(label.target)){
30500             return;
30501         }
30502      
30503         this.groups[label.target] = label;
30504         
30505     },
30506     /**
30507     * fetch a FieldLabel Group based on the target
30508     * @param {string} target
30509     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30510     */
30511     get: function(target) {
30512         if (typeof(this.groups[target]) == 'undefined') {
30513             return false;
30514         }
30515         
30516         return this.groups[target] ;
30517     }
30518 });
30519
30520  
30521
30522  /*
30523  * - LGPL
30524  *
30525  * page DateSplitField.
30526  * 
30527  */
30528
30529
30530 /**
30531  * @class Roo.bootstrap.DateSplitField
30532  * @extends Roo.bootstrap.Component
30533  * Bootstrap DateSplitField class
30534  * @cfg {string} fieldLabel - the label associated
30535  * @cfg {Number} labelWidth set the width of label (0-12)
30536  * @cfg {String} labelAlign (top|left)
30537  * @cfg {Boolean} dayAllowBlank (true|false) default false
30538  * @cfg {Boolean} monthAllowBlank (true|false) default false
30539  * @cfg {Boolean} yearAllowBlank (true|false) default false
30540  * @cfg {string} dayPlaceholder 
30541  * @cfg {string} monthPlaceholder
30542  * @cfg {string} yearPlaceholder
30543  * @cfg {string} dayFormat default 'd'
30544  * @cfg {string} monthFormat default 'm'
30545  * @cfg {string} yearFormat default 'Y'
30546  * @cfg {Number} labellg set the width of label (1-12)
30547  * @cfg {Number} labelmd set the width of label (1-12)
30548  * @cfg {Number} labelsm set the width of label (1-12)
30549  * @cfg {Number} labelxs set the width of label (1-12)
30550
30551  *     
30552  * @constructor
30553  * Create a new DateSplitField
30554  * @param {Object} config The config object
30555  */
30556
30557 Roo.bootstrap.DateSplitField = function(config){
30558     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30559     
30560     this.addEvents({
30561         // raw events
30562          /**
30563          * @event years
30564          * getting the data of years
30565          * @param {Roo.bootstrap.DateSplitField} this
30566          * @param {Object} years
30567          */
30568         "years" : true,
30569         /**
30570          * @event days
30571          * getting the data of days
30572          * @param {Roo.bootstrap.DateSplitField} this
30573          * @param {Object} days
30574          */
30575         "days" : true,
30576         /**
30577          * @event invalid
30578          * Fires after the field has been marked as invalid.
30579          * @param {Roo.form.Field} this
30580          * @param {String} msg The validation message
30581          */
30582         invalid : true,
30583        /**
30584          * @event valid
30585          * Fires after the field has been validated with no errors.
30586          * @param {Roo.form.Field} this
30587          */
30588         valid : true
30589     });
30590 };
30591
30592 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30593     
30594     fieldLabel : '',
30595     labelAlign : 'top',
30596     labelWidth : 3,
30597     dayAllowBlank : false,
30598     monthAllowBlank : false,
30599     yearAllowBlank : false,
30600     dayPlaceholder : '',
30601     monthPlaceholder : '',
30602     yearPlaceholder : '',
30603     dayFormat : 'd',
30604     monthFormat : 'm',
30605     yearFormat : 'Y',
30606     isFormField : true,
30607     labellg : 0,
30608     labelmd : 0,
30609     labelsm : 0,
30610     labelxs : 0,
30611     
30612     getAutoCreate : function()
30613     {
30614         var cfg = {
30615             tag : 'div',
30616             cls : 'row roo-date-split-field-group',
30617             cn : [
30618                 {
30619                     tag : 'input',
30620                     type : 'hidden',
30621                     cls : 'form-hidden-field roo-date-split-field-group-value',
30622                     name : this.name
30623                 }
30624             ]
30625         };
30626         
30627         var labelCls = 'col-md-12';
30628         var contentCls = 'col-md-4';
30629         
30630         if(this.fieldLabel){
30631             
30632             var label = {
30633                 tag : 'div',
30634                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30635                 cn : [
30636                     {
30637                         tag : 'label',
30638                         html : this.fieldLabel
30639                     }
30640                 ]
30641             };
30642             
30643             if(this.labelAlign == 'left'){
30644             
30645                 if(this.labelWidth > 12){
30646                     label.style = "width: " + this.labelWidth + 'px';
30647                 }
30648
30649                 if(this.labelWidth < 13 && this.labelmd == 0){
30650                     this.labelmd = this.labelWidth;
30651                 }
30652
30653                 if(this.labellg > 0){
30654                     labelCls = ' col-lg-' + this.labellg;
30655                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30656                 }
30657
30658                 if(this.labelmd > 0){
30659                     labelCls = ' col-md-' + this.labelmd;
30660                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30661                 }
30662
30663                 if(this.labelsm > 0){
30664                     labelCls = ' col-sm-' + this.labelsm;
30665                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30666                 }
30667
30668                 if(this.labelxs > 0){
30669                     labelCls = ' col-xs-' + this.labelxs;
30670                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30671                 }
30672             }
30673             
30674             label.cls += ' ' + labelCls;
30675             
30676             cfg.cn.push(label);
30677         }
30678         
30679         Roo.each(['day', 'month', 'year'], function(t){
30680             cfg.cn.push({
30681                 tag : 'div',
30682                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30683             });
30684         }, this);
30685         
30686         return cfg;
30687     },
30688     
30689     inputEl: function ()
30690     {
30691         return this.el.select('.roo-date-split-field-group-value', true).first();
30692     },
30693     
30694     onRender : function(ct, position) 
30695     {
30696         var _this = this;
30697         
30698         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30699         
30700         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30701         
30702         this.dayField = new Roo.bootstrap.ComboBox({
30703             allowBlank : this.dayAllowBlank,
30704             alwaysQuery : true,
30705             displayField : 'value',
30706             editable : false,
30707             fieldLabel : '',
30708             forceSelection : true,
30709             mode : 'local',
30710             placeholder : this.dayPlaceholder,
30711             selectOnFocus : true,
30712             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30713             triggerAction : 'all',
30714             typeAhead : true,
30715             valueField : 'value',
30716             store : new Roo.data.SimpleStore({
30717                 data : (function() {    
30718                     var days = [];
30719                     _this.fireEvent('days', _this, days);
30720                     return days;
30721                 })(),
30722                 fields : [ 'value' ]
30723             }),
30724             listeners : {
30725                 select : function (_self, record, index)
30726                 {
30727                     _this.setValue(_this.getValue());
30728                 }
30729             }
30730         });
30731
30732         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30733         
30734         this.monthField = new Roo.bootstrap.MonthField({
30735             after : '<i class=\"fa fa-calendar\"></i>',
30736             allowBlank : this.monthAllowBlank,
30737             placeholder : this.monthPlaceholder,
30738             readOnly : true,
30739             listeners : {
30740                 render : function (_self)
30741                 {
30742                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30743                         e.preventDefault();
30744                         _self.focus();
30745                     });
30746                 },
30747                 select : function (_self, oldvalue, newvalue)
30748                 {
30749                     _this.setValue(_this.getValue());
30750                 }
30751             }
30752         });
30753         
30754         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30755         
30756         this.yearField = new Roo.bootstrap.ComboBox({
30757             allowBlank : this.yearAllowBlank,
30758             alwaysQuery : true,
30759             displayField : 'value',
30760             editable : false,
30761             fieldLabel : '',
30762             forceSelection : true,
30763             mode : 'local',
30764             placeholder : this.yearPlaceholder,
30765             selectOnFocus : true,
30766             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30767             triggerAction : 'all',
30768             typeAhead : true,
30769             valueField : 'value',
30770             store : new Roo.data.SimpleStore({
30771                 data : (function() {
30772                     var years = [];
30773                     _this.fireEvent('years', _this, years);
30774                     return years;
30775                 })(),
30776                 fields : [ 'value' ]
30777             }),
30778             listeners : {
30779                 select : function (_self, record, index)
30780                 {
30781                     _this.setValue(_this.getValue());
30782                 }
30783             }
30784         });
30785
30786         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30787     },
30788     
30789     setValue : function(v, format)
30790     {
30791         this.inputEl.dom.value = v;
30792         
30793         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30794         
30795         var d = Date.parseDate(v, f);
30796         
30797         if(!d){
30798             this.validate();
30799             return;
30800         }
30801         
30802         this.setDay(d.format(this.dayFormat));
30803         this.setMonth(d.format(this.monthFormat));
30804         this.setYear(d.format(this.yearFormat));
30805         
30806         this.validate();
30807         
30808         return;
30809     },
30810     
30811     setDay : function(v)
30812     {
30813         this.dayField.setValue(v);
30814         this.inputEl.dom.value = this.getValue();
30815         this.validate();
30816         return;
30817     },
30818     
30819     setMonth : function(v)
30820     {
30821         this.monthField.setValue(v, true);
30822         this.inputEl.dom.value = this.getValue();
30823         this.validate();
30824         return;
30825     },
30826     
30827     setYear : function(v)
30828     {
30829         this.yearField.setValue(v);
30830         this.inputEl.dom.value = this.getValue();
30831         this.validate();
30832         return;
30833     },
30834     
30835     getDay : function()
30836     {
30837         return this.dayField.getValue();
30838     },
30839     
30840     getMonth : function()
30841     {
30842         return this.monthField.getValue();
30843     },
30844     
30845     getYear : function()
30846     {
30847         return this.yearField.getValue();
30848     },
30849     
30850     getValue : function()
30851     {
30852         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30853         
30854         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30855         
30856         return date;
30857     },
30858     
30859     reset : function()
30860     {
30861         this.setDay('');
30862         this.setMonth('');
30863         this.setYear('');
30864         this.inputEl.dom.value = '';
30865         this.validate();
30866         return;
30867     },
30868     
30869     validate : function()
30870     {
30871         var d = this.dayField.validate();
30872         var m = this.monthField.validate();
30873         var y = this.yearField.validate();
30874         
30875         var valid = true;
30876         
30877         if(
30878                 (!this.dayAllowBlank && !d) ||
30879                 (!this.monthAllowBlank && !m) ||
30880                 (!this.yearAllowBlank && !y)
30881         ){
30882             valid = false;
30883         }
30884         
30885         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30886             return valid;
30887         }
30888         
30889         if(valid){
30890             this.markValid();
30891             return valid;
30892         }
30893         
30894         this.markInvalid();
30895         
30896         return valid;
30897     },
30898     
30899     markValid : function()
30900     {
30901         
30902         var label = this.el.select('label', true).first();
30903         var icon = this.el.select('i.fa-star', true).first();
30904
30905         if(label && icon){
30906             icon.remove();
30907         }
30908         
30909         this.fireEvent('valid', this);
30910     },
30911     
30912      /**
30913      * Mark this field as invalid
30914      * @param {String} msg The validation message
30915      */
30916     markInvalid : function(msg)
30917     {
30918         
30919         var label = this.el.select('label', true).first();
30920         var icon = this.el.select('i.fa-star', true).first();
30921
30922         if(label && !icon){
30923             this.el.select('.roo-date-split-field-label', true).createChild({
30924                 tag : 'i',
30925                 cls : 'text-danger fa fa-lg fa-star',
30926                 tooltip : 'This field is required',
30927                 style : 'margin-right:5px;'
30928             }, label, true);
30929         }
30930         
30931         this.fireEvent('invalid', this, msg);
30932     },
30933     
30934     clearInvalid : function()
30935     {
30936         var label = this.el.select('label', true).first();
30937         var icon = this.el.select('i.fa-star', true).first();
30938
30939         if(label && icon){
30940             icon.remove();
30941         }
30942         
30943         this.fireEvent('valid', this);
30944     },
30945     
30946     getName: function()
30947     {
30948         return this.name;
30949     }
30950     
30951 });
30952
30953  /**
30954  *
30955  * This is based on 
30956  * http://masonry.desandro.com
30957  *
30958  * The idea is to render all the bricks based on vertical width...
30959  *
30960  * The original code extends 'outlayer' - we might need to use that....
30961  * 
30962  */
30963
30964
30965 /**
30966  * @class Roo.bootstrap.LayoutMasonry
30967  * @extends Roo.bootstrap.Component
30968  * Bootstrap Layout Masonry class
30969  * 
30970  * @constructor
30971  * Create a new Element
30972  * @param {Object} config The config object
30973  */
30974
30975 Roo.bootstrap.LayoutMasonry = function(config){
30976     
30977     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30978     
30979     this.bricks = [];
30980     
30981     Roo.bootstrap.LayoutMasonry.register(this);
30982     
30983     this.addEvents({
30984         // raw events
30985         /**
30986          * @event layout
30987          * Fire after layout the items
30988          * @param {Roo.bootstrap.LayoutMasonry} this
30989          * @param {Roo.EventObject} e
30990          */
30991         "layout" : true
30992     });
30993     
30994 };
30995
30996 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30997     
30998     /**
30999      * @cfg {Boolean} isLayoutInstant = no animation?
31000      */   
31001     isLayoutInstant : false, // needed?
31002    
31003     /**
31004      * @cfg {Number} boxWidth  width of the columns
31005      */   
31006     boxWidth : 450,
31007     
31008       /**
31009      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31010      */   
31011     boxHeight : 0,
31012     
31013     /**
31014      * @cfg {Number} padWidth padding below box..
31015      */   
31016     padWidth : 10, 
31017     
31018     /**
31019      * @cfg {Number} gutter gutter width..
31020      */   
31021     gutter : 10,
31022     
31023      /**
31024      * @cfg {Number} maxCols maximum number of columns
31025      */   
31026     
31027     maxCols: 0,
31028     
31029     /**
31030      * @cfg {Boolean} isAutoInitial defalut true
31031      */   
31032     isAutoInitial : true, 
31033     
31034     containerWidth: 0,
31035     
31036     /**
31037      * @cfg {Boolean} isHorizontal defalut false
31038      */   
31039     isHorizontal : false, 
31040
31041     currentSize : null,
31042     
31043     tag: 'div',
31044     
31045     cls: '',
31046     
31047     bricks: null, //CompositeElement
31048     
31049     cols : 1,
31050     
31051     _isLayoutInited : false,
31052     
31053 //    isAlternative : false, // only use for vertical layout...
31054     
31055     /**
31056      * @cfg {Number} alternativePadWidth padding below box..
31057      */   
31058     alternativePadWidth : 50,
31059     
31060     selectedBrick : [],
31061     
31062     getAutoCreate : function(){
31063         
31064         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31065         
31066         var cfg = {
31067             tag: this.tag,
31068             cls: 'blog-masonary-wrapper ' + this.cls,
31069             cn : {
31070                 cls : 'mas-boxes masonary'
31071             }
31072         };
31073         
31074         return cfg;
31075     },
31076     
31077     getChildContainer: function( )
31078     {
31079         if (this.boxesEl) {
31080             return this.boxesEl;
31081         }
31082         
31083         this.boxesEl = this.el.select('.mas-boxes').first();
31084         
31085         return this.boxesEl;
31086     },
31087     
31088     
31089     initEvents : function()
31090     {
31091         var _this = this;
31092         
31093         if(this.isAutoInitial){
31094             Roo.log('hook children rendered');
31095             this.on('childrenrendered', function() {
31096                 Roo.log('children rendered');
31097                 _this.initial();
31098             } ,this);
31099         }
31100     },
31101     
31102     initial : function()
31103     {
31104         this.selectedBrick = [];
31105         
31106         this.currentSize = this.el.getBox(true);
31107         
31108         Roo.EventManager.onWindowResize(this.resize, this); 
31109
31110         if(!this.isAutoInitial){
31111             this.layout();
31112             return;
31113         }
31114         
31115         this.layout();
31116         
31117         return;
31118         //this.layout.defer(500,this);
31119         
31120     },
31121     
31122     resize : function()
31123     {
31124         var cs = this.el.getBox(true);
31125         
31126         if (
31127                 this.currentSize.width == cs.width && 
31128                 this.currentSize.x == cs.x && 
31129                 this.currentSize.height == cs.height && 
31130                 this.currentSize.y == cs.y 
31131         ) {
31132             Roo.log("no change in with or X or Y");
31133             return;
31134         }
31135         
31136         this.currentSize = cs;
31137         
31138         this.layout();
31139         
31140     },
31141     
31142     layout : function()
31143     {   
31144         this._resetLayout();
31145         
31146         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31147         
31148         this.layoutItems( isInstant );
31149       
31150         this._isLayoutInited = true;
31151         
31152         this.fireEvent('layout', this);
31153         
31154     },
31155     
31156     _resetLayout : function()
31157     {
31158         if(this.isHorizontal){
31159             this.horizontalMeasureColumns();
31160             return;
31161         }
31162         
31163         this.verticalMeasureColumns();
31164         
31165     },
31166     
31167     verticalMeasureColumns : function()
31168     {
31169         this.getContainerWidth();
31170         
31171 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31172 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31173 //            return;
31174 //        }
31175         
31176         var boxWidth = this.boxWidth + this.padWidth;
31177         
31178         if(this.containerWidth < this.boxWidth){
31179             boxWidth = this.containerWidth
31180         }
31181         
31182         var containerWidth = this.containerWidth;
31183         
31184         var cols = Math.floor(containerWidth / boxWidth);
31185         
31186         this.cols = Math.max( cols, 1 );
31187         
31188         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31189         
31190         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31191         
31192         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31193         
31194         this.colWidth = boxWidth + avail - this.padWidth;
31195         
31196         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31197         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31198     },
31199     
31200     horizontalMeasureColumns : function()
31201     {
31202         this.getContainerWidth();
31203         
31204         var boxWidth = this.boxWidth;
31205         
31206         if(this.containerWidth < boxWidth){
31207             boxWidth = this.containerWidth;
31208         }
31209         
31210         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31211         
31212         this.el.setHeight(boxWidth);
31213         
31214     },
31215     
31216     getContainerWidth : function()
31217     {
31218         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31219     },
31220     
31221     layoutItems : function( isInstant )
31222     {
31223         Roo.log(this.bricks);
31224         
31225         var items = Roo.apply([], this.bricks);
31226         
31227         if(this.isHorizontal){
31228             this._horizontalLayoutItems( items , isInstant );
31229             return;
31230         }
31231         
31232 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31233 //            this._verticalAlternativeLayoutItems( items , isInstant );
31234 //            return;
31235 //        }
31236         
31237         this._verticalLayoutItems( items , isInstant );
31238         
31239     },
31240     
31241     _verticalLayoutItems : function ( items , isInstant)
31242     {
31243         if ( !items || !items.length ) {
31244             return;
31245         }
31246         
31247         var standard = [
31248             ['xs', 'xs', 'xs', 'tall'],
31249             ['xs', 'xs', 'tall'],
31250             ['xs', 'xs', 'sm'],
31251             ['xs', 'xs', 'xs'],
31252             ['xs', 'tall'],
31253             ['xs', 'sm'],
31254             ['xs', 'xs'],
31255             ['xs'],
31256             
31257             ['sm', 'xs', 'xs'],
31258             ['sm', 'xs'],
31259             ['sm'],
31260             
31261             ['tall', 'xs', 'xs', 'xs'],
31262             ['tall', 'xs', 'xs'],
31263             ['tall', 'xs'],
31264             ['tall']
31265             
31266         ];
31267         
31268         var queue = [];
31269         
31270         var boxes = [];
31271         
31272         var box = [];
31273         
31274         Roo.each(items, function(item, k){
31275             
31276             switch (item.size) {
31277                 // these layouts take up a full box,
31278                 case 'md' :
31279                 case 'md-left' :
31280                 case 'md-right' :
31281                 case 'wide' :
31282                     
31283                     if(box.length){
31284                         boxes.push(box);
31285                         box = [];
31286                     }
31287                     
31288                     boxes.push([item]);
31289                     
31290                     break;
31291                     
31292                 case 'xs' :
31293                 case 'sm' :
31294                 case 'tall' :
31295                     
31296                     box.push(item);
31297                     
31298                     break;
31299                 default :
31300                     break;
31301                     
31302             }
31303             
31304         }, this);
31305         
31306         if(box.length){
31307             boxes.push(box);
31308             box = [];
31309         }
31310         
31311         var filterPattern = function(box, length)
31312         {
31313             if(!box.length){
31314                 return;
31315             }
31316             
31317             var match = false;
31318             
31319             var pattern = box.slice(0, length);
31320             
31321             var format = [];
31322             
31323             Roo.each(pattern, function(i){
31324                 format.push(i.size);
31325             }, this);
31326             
31327             Roo.each(standard, function(s){
31328                 
31329                 if(String(s) != String(format)){
31330                     return;
31331                 }
31332                 
31333                 match = true;
31334                 return false;
31335                 
31336             }, this);
31337             
31338             if(!match && length == 1){
31339                 return;
31340             }
31341             
31342             if(!match){
31343                 filterPattern(box, length - 1);
31344                 return;
31345             }
31346                 
31347             queue.push(pattern);
31348
31349             box = box.slice(length, box.length);
31350
31351             filterPattern(box, 4);
31352
31353             return;
31354             
31355         }
31356         
31357         Roo.each(boxes, function(box, k){
31358             
31359             if(!box.length){
31360                 return;
31361             }
31362             
31363             if(box.length == 1){
31364                 queue.push(box);
31365                 return;
31366             }
31367             
31368             filterPattern(box, 4);
31369             
31370         }, this);
31371         
31372         this._processVerticalLayoutQueue( queue, isInstant );
31373         
31374     },
31375     
31376 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31377 //    {
31378 //        if ( !items || !items.length ) {
31379 //            return;
31380 //        }
31381 //
31382 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31383 //        
31384 //    },
31385     
31386     _horizontalLayoutItems : function ( items , isInstant)
31387     {
31388         if ( !items || !items.length || items.length < 3) {
31389             return;
31390         }
31391         
31392         items.reverse();
31393         
31394         var eItems = items.slice(0, 3);
31395         
31396         items = items.slice(3, items.length);
31397         
31398         var standard = [
31399             ['xs', 'xs', 'xs', 'wide'],
31400             ['xs', 'xs', 'wide'],
31401             ['xs', 'xs', 'sm'],
31402             ['xs', 'xs', 'xs'],
31403             ['xs', 'wide'],
31404             ['xs', 'sm'],
31405             ['xs', 'xs'],
31406             ['xs'],
31407             
31408             ['sm', 'xs', 'xs'],
31409             ['sm', 'xs'],
31410             ['sm'],
31411             
31412             ['wide', 'xs', 'xs', 'xs'],
31413             ['wide', 'xs', 'xs'],
31414             ['wide', 'xs'],
31415             ['wide'],
31416             
31417             ['wide-thin']
31418         ];
31419         
31420         var queue = [];
31421         
31422         var boxes = [];
31423         
31424         var box = [];
31425         
31426         Roo.each(items, function(item, k){
31427             
31428             switch (item.size) {
31429                 case 'md' :
31430                 case 'md-left' :
31431                 case 'md-right' :
31432                 case 'tall' :
31433                     
31434                     if(box.length){
31435                         boxes.push(box);
31436                         box = [];
31437                     }
31438                     
31439                     boxes.push([item]);
31440                     
31441                     break;
31442                     
31443                 case 'xs' :
31444                 case 'sm' :
31445                 case 'wide' :
31446                 case 'wide-thin' :
31447                     
31448                     box.push(item);
31449                     
31450                     break;
31451                 default :
31452                     break;
31453                     
31454             }
31455             
31456         }, this);
31457         
31458         if(box.length){
31459             boxes.push(box);
31460             box = [];
31461         }
31462         
31463         var filterPattern = function(box, length)
31464         {
31465             if(!box.length){
31466                 return;
31467             }
31468             
31469             var match = false;
31470             
31471             var pattern = box.slice(0, length);
31472             
31473             var format = [];
31474             
31475             Roo.each(pattern, function(i){
31476                 format.push(i.size);
31477             }, this);
31478             
31479             Roo.each(standard, function(s){
31480                 
31481                 if(String(s) != String(format)){
31482                     return;
31483                 }
31484                 
31485                 match = true;
31486                 return false;
31487                 
31488             }, this);
31489             
31490             if(!match && length == 1){
31491                 return;
31492             }
31493             
31494             if(!match){
31495                 filterPattern(box, length - 1);
31496                 return;
31497             }
31498                 
31499             queue.push(pattern);
31500
31501             box = box.slice(length, box.length);
31502
31503             filterPattern(box, 4);
31504
31505             return;
31506             
31507         }
31508         
31509         Roo.each(boxes, function(box, k){
31510             
31511             if(!box.length){
31512                 return;
31513             }
31514             
31515             if(box.length == 1){
31516                 queue.push(box);
31517                 return;
31518             }
31519             
31520             filterPattern(box, 4);
31521             
31522         }, this);
31523         
31524         
31525         var prune = [];
31526         
31527         var pos = this.el.getBox(true);
31528         
31529         var minX = pos.x;
31530         
31531         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31532         
31533         var hit_end = false;
31534         
31535         Roo.each(queue, function(box){
31536             
31537             if(hit_end){
31538                 
31539                 Roo.each(box, function(b){
31540                 
31541                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31542                     b.el.hide();
31543
31544                 }, this);
31545
31546                 return;
31547             }
31548             
31549             var mx = 0;
31550             
31551             Roo.each(box, function(b){
31552                 
31553                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31554                 b.el.show();
31555
31556                 mx = Math.max(mx, b.x);
31557                 
31558             }, this);
31559             
31560             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31561             
31562             if(maxX < minX){
31563                 
31564                 Roo.each(box, function(b){
31565                 
31566                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31567                     b.el.hide();
31568                     
31569                 }, this);
31570                 
31571                 hit_end = true;
31572                 
31573                 return;
31574             }
31575             
31576             prune.push(box);
31577             
31578         }, this);
31579         
31580         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31581     },
31582     
31583     /** Sets position of item in DOM
31584     * @param {Element} item
31585     * @param {Number} x - horizontal position
31586     * @param {Number} y - vertical position
31587     * @param {Boolean} isInstant - disables transitions
31588     */
31589     _processVerticalLayoutQueue : function( queue, isInstant )
31590     {
31591         var pos = this.el.getBox(true);
31592         var x = pos.x;
31593         var y = pos.y;
31594         var maxY = [];
31595         
31596         for (var i = 0; i < this.cols; i++){
31597             maxY[i] = pos.y;
31598         }
31599         
31600         Roo.each(queue, function(box, k){
31601             
31602             var col = k % this.cols;
31603             
31604             Roo.each(box, function(b,kk){
31605                 
31606                 b.el.position('absolute');
31607                 
31608                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31609                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31610                 
31611                 if(b.size == 'md-left' || b.size == 'md-right'){
31612                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31613                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31614                 }
31615                 
31616                 b.el.setWidth(width);
31617                 b.el.setHeight(height);
31618                 // iframe?
31619                 b.el.select('iframe',true).setSize(width,height);
31620                 
31621             }, this);
31622             
31623             for (var i = 0; i < this.cols; i++){
31624                 
31625                 if(maxY[i] < maxY[col]){
31626                     col = i;
31627                     continue;
31628                 }
31629                 
31630                 col = Math.min(col, i);
31631                 
31632             }
31633             
31634             x = pos.x + col * (this.colWidth + this.padWidth);
31635             
31636             y = maxY[col];
31637             
31638             var positions = [];
31639             
31640             switch (box.length){
31641                 case 1 :
31642                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31643                     break;
31644                 case 2 :
31645                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31646                     break;
31647                 case 3 :
31648                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31649                     break;
31650                 case 4 :
31651                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31652                     break;
31653                 default :
31654                     break;
31655             }
31656             
31657             Roo.each(box, function(b,kk){
31658                 
31659                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31660                 
31661                 var sz = b.el.getSize();
31662                 
31663                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31664                 
31665             }, this);
31666             
31667         }, this);
31668         
31669         var mY = 0;
31670         
31671         for (var i = 0; i < this.cols; i++){
31672             mY = Math.max(mY, maxY[i]);
31673         }
31674         
31675         this.el.setHeight(mY - pos.y);
31676         
31677     },
31678     
31679 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31680 //    {
31681 //        var pos = this.el.getBox(true);
31682 //        var x = pos.x;
31683 //        var y = pos.y;
31684 //        var maxX = pos.right;
31685 //        
31686 //        var maxHeight = 0;
31687 //        
31688 //        Roo.each(items, function(item, k){
31689 //            
31690 //            var c = k % 2;
31691 //            
31692 //            item.el.position('absolute');
31693 //                
31694 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31695 //
31696 //            item.el.setWidth(width);
31697 //
31698 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31699 //
31700 //            item.el.setHeight(height);
31701 //            
31702 //            if(c == 0){
31703 //                item.el.setXY([x, y], isInstant ? false : true);
31704 //            } else {
31705 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31706 //            }
31707 //            
31708 //            y = y + height + this.alternativePadWidth;
31709 //            
31710 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31711 //            
31712 //        }, this);
31713 //        
31714 //        this.el.setHeight(maxHeight);
31715 //        
31716 //    },
31717     
31718     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31719     {
31720         var pos = this.el.getBox(true);
31721         
31722         var minX = pos.x;
31723         var minY = pos.y;
31724         
31725         var maxX = pos.right;
31726         
31727         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31728         
31729         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31730         
31731         Roo.each(queue, function(box, k){
31732             
31733             Roo.each(box, function(b, kk){
31734                 
31735                 b.el.position('absolute');
31736                 
31737                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31738                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31739                 
31740                 if(b.size == 'md-left' || b.size == 'md-right'){
31741                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31742                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31743                 }
31744                 
31745                 b.el.setWidth(width);
31746                 b.el.setHeight(height);
31747                 
31748             }, this);
31749             
31750             if(!box.length){
31751                 return;
31752             }
31753             
31754             var positions = [];
31755             
31756             switch (box.length){
31757                 case 1 :
31758                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31759                     break;
31760                 case 2 :
31761                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31762                     break;
31763                 case 3 :
31764                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31765                     break;
31766                 case 4 :
31767                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31768                     break;
31769                 default :
31770                     break;
31771             }
31772             
31773             Roo.each(box, function(b,kk){
31774                 
31775                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31776                 
31777                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31778                 
31779             }, this);
31780             
31781         }, this);
31782         
31783     },
31784     
31785     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31786     {
31787         Roo.each(eItems, function(b,k){
31788             
31789             b.size = (k == 0) ? 'sm' : 'xs';
31790             b.x = (k == 0) ? 2 : 1;
31791             b.y = (k == 0) ? 2 : 1;
31792             
31793             b.el.position('absolute');
31794             
31795             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31796                 
31797             b.el.setWidth(width);
31798             
31799             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31800             
31801             b.el.setHeight(height);
31802             
31803         }, this);
31804
31805         var positions = [];
31806         
31807         positions.push({
31808             x : maxX - this.unitWidth * 2 - this.gutter,
31809             y : minY
31810         });
31811         
31812         positions.push({
31813             x : maxX - this.unitWidth,
31814             y : minY + (this.unitWidth + this.gutter) * 2
31815         });
31816         
31817         positions.push({
31818             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31819             y : minY
31820         });
31821         
31822         Roo.each(eItems, function(b,k){
31823             
31824             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31825
31826         }, this);
31827         
31828     },
31829     
31830     getVerticalOneBoxColPositions : function(x, y, box)
31831     {
31832         var pos = [];
31833         
31834         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31835         
31836         if(box[0].size == 'md-left'){
31837             rand = 0;
31838         }
31839         
31840         if(box[0].size == 'md-right'){
31841             rand = 1;
31842         }
31843         
31844         pos.push({
31845             x : x + (this.unitWidth + this.gutter) * rand,
31846             y : y
31847         });
31848         
31849         return pos;
31850     },
31851     
31852     getVerticalTwoBoxColPositions : function(x, y, box)
31853     {
31854         var pos = [];
31855         
31856         if(box[0].size == 'xs'){
31857             
31858             pos.push({
31859                 x : x,
31860                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31861             });
31862
31863             pos.push({
31864                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31865                 y : y
31866             });
31867             
31868             return pos;
31869             
31870         }
31871         
31872         pos.push({
31873             x : x,
31874             y : y
31875         });
31876
31877         pos.push({
31878             x : x + (this.unitWidth + this.gutter) * 2,
31879             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31880         });
31881         
31882         return pos;
31883         
31884     },
31885     
31886     getVerticalThreeBoxColPositions : function(x, y, box)
31887     {
31888         var pos = [];
31889         
31890         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31891             
31892             pos.push({
31893                 x : x,
31894                 y : y
31895             });
31896
31897             pos.push({
31898                 x : x + (this.unitWidth + this.gutter) * 1,
31899                 y : y
31900             });
31901             
31902             pos.push({
31903                 x : x + (this.unitWidth + this.gutter) * 2,
31904                 y : y
31905             });
31906             
31907             return pos;
31908             
31909         }
31910         
31911         if(box[0].size == 'xs' && box[1].size == 'xs'){
31912             
31913             pos.push({
31914                 x : x,
31915                 y : y
31916             });
31917
31918             pos.push({
31919                 x : x,
31920                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31921             });
31922             
31923             pos.push({
31924                 x : x + (this.unitWidth + this.gutter) * 1,
31925                 y : y
31926             });
31927             
31928             return pos;
31929             
31930         }
31931         
31932         pos.push({
31933             x : x,
31934             y : y
31935         });
31936
31937         pos.push({
31938             x : x + (this.unitWidth + this.gutter) * 2,
31939             y : y
31940         });
31941
31942         pos.push({
31943             x : x + (this.unitWidth + this.gutter) * 2,
31944             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31945         });
31946             
31947         return pos;
31948         
31949     },
31950     
31951     getVerticalFourBoxColPositions : function(x, y, box)
31952     {
31953         var pos = [];
31954         
31955         if(box[0].size == 'xs'){
31956             
31957             pos.push({
31958                 x : x,
31959                 y : y
31960             });
31961
31962             pos.push({
31963                 x : x,
31964                 y : y + (this.unitHeight + this.gutter) * 1
31965             });
31966             
31967             pos.push({
31968                 x : x,
31969                 y : y + (this.unitHeight + this.gutter) * 2
31970             });
31971             
31972             pos.push({
31973                 x : x + (this.unitWidth + this.gutter) * 1,
31974                 y : y
31975             });
31976             
31977             return pos;
31978             
31979         }
31980         
31981         pos.push({
31982             x : x,
31983             y : y
31984         });
31985
31986         pos.push({
31987             x : x + (this.unitWidth + this.gutter) * 2,
31988             y : y
31989         });
31990
31991         pos.push({
31992             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31993             y : y + (this.unitHeight + this.gutter) * 1
31994         });
31995
31996         pos.push({
31997             x : x + (this.unitWidth + this.gutter) * 2,
31998             y : y + (this.unitWidth + this.gutter) * 2
31999         });
32000
32001         return pos;
32002         
32003     },
32004     
32005     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32006     {
32007         var pos = [];
32008         
32009         if(box[0].size == 'md-left'){
32010             pos.push({
32011                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32012                 y : minY
32013             });
32014             
32015             return pos;
32016         }
32017         
32018         if(box[0].size == 'md-right'){
32019             pos.push({
32020                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32021                 y : minY + (this.unitWidth + this.gutter) * 1
32022             });
32023             
32024             return pos;
32025         }
32026         
32027         var rand = Math.floor(Math.random() * (4 - box[0].y));
32028         
32029         pos.push({
32030             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32031             y : minY + (this.unitWidth + this.gutter) * rand
32032         });
32033         
32034         return pos;
32035         
32036     },
32037     
32038     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32039     {
32040         var pos = [];
32041         
32042         if(box[0].size == 'xs'){
32043             
32044             pos.push({
32045                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32046                 y : minY
32047             });
32048
32049             pos.push({
32050                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32051                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32052             });
32053             
32054             return pos;
32055             
32056         }
32057         
32058         pos.push({
32059             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32060             y : minY
32061         });
32062
32063         pos.push({
32064             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32065             y : minY + (this.unitWidth + this.gutter) * 2
32066         });
32067         
32068         return pos;
32069         
32070     },
32071     
32072     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32073     {
32074         var pos = [];
32075         
32076         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32077             
32078             pos.push({
32079                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32080                 y : minY
32081             });
32082
32083             pos.push({
32084                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32085                 y : minY + (this.unitWidth + this.gutter) * 1
32086             });
32087             
32088             pos.push({
32089                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32090                 y : minY + (this.unitWidth + this.gutter) * 2
32091             });
32092             
32093             return pos;
32094             
32095         }
32096         
32097         if(box[0].size == 'xs' && box[1].size == 'xs'){
32098             
32099             pos.push({
32100                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32101                 y : minY
32102             });
32103
32104             pos.push({
32105                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32106                 y : minY
32107             });
32108             
32109             pos.push({
32110                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32111                 y : minY + (this.unitWidth + this.gutter) * 1
32112             });
32113             
32114             return pos;
32115             
32116         }
32117         
32118         pos.push({
32119             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32120             y : minY
32121         });
32122
32123         pos.push({
32124             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32125             y : minY + (this.unitWidth + this.gutter) * 2
32126         });
32127
32128         pos.push({
32129             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32130             y : minY + (this.unitWidth + this.gutter) * 2
32131         });
32132             
32133         return pos;
32134         
32135     },
32136     
32137     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32138     {
32139         var pos = [];
32140         
32141         if(box[0].size == 'xs'){
32142             
32143             pos.push({
32144                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32145                 y : minY
32146             });
32147
32148             pos.push({
32149                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32150                 y : minY
32151             });
32152             
32153             pos.push({
32154                 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),
32155                 y : minY
32156             });
32157             
32158             pos.push({
32159                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32160                 y : minY + (this.unitWidth + this.gutter) * 1
32161             });
32162             
32163             return pos;
32164             
32165         }
32166         
32167         pos.push({
32168             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32169             y : minY
32170         });
32171         
32172         pos.push({
32173             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32174             y : minY + (this.unitWidth + this.gutter) * 2
32175         });
32176         
32177         pos.push({
32178             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32179             y : minY + (this.unitWidth + this.gutter) * 2
32180         });
32181         
32182         pos.push({
32183             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),
32184             y : minY + (this.unitWidth + this.gutter) * 2
32185         });
32186
32187         return pos;
32188         
32189     },
32190     
32191     /**
32192     * remove a Masonry Brick
32193     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32194     */
32195     removeBrick : function(brick_id)
32196     {
32197         if (!brick_id) {
32198             return;
32199         }
32200         
32201         for (var i = 0; i<this.bricks.length; i++) {
32202             if (this.bricks[i].id == brick_id) {
32203                 this.bricks.splice(i,1);
32204                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32205                 this.initial();
32206             }
32207         }
32208     },
32209     
32210     /**
32211     * adds a Masonry Brick
32212     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32213     */
32214     addBrick : function(cfg)
32215     {
32216         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32217         //this.register(cn);
32218         cn.parentId = this.id;
32219         cn.render(this.el);
32220         return cn;
32221     },
32222     
32223     /**
32224     * register a Masonry Brick
32225     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32226     */
32227     
32228     register : function(brick)
32229     {
32230         this.bricks.push(brick);
32231         brick.masonryId = this.id;
32232     },
32233     
32234     /**
32235     * clear all the Masonry Brick
32236     */
32237     clearAll : function()
32238     {
32239         this.bricks = [];
32240         //this.getChildContainer().dom.innerHTML = "";
32241         this.el.dom.innerHTML = '';
32242     },
32243     
32244     getSelected : function()
32245     {
32246         if (!this.selectedBrick) {
32247             return false;
32248         }
32249         
32250         return this.selectedBrick;
32251     }
32252 });
32253
32254 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32255     
32256     groups: {},
32257      /**
32258     * register a Masonry Layout
32259     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32260     */
32261     
32262     register : function(layout)
32263     {
32264         this.groups[layout.id] = layout;
32265     },
32266     /**
32267     * fetch a  Masonry Layout based on the masonry layout ID
32268     * @param {string} the masonry layout to add
32269     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32270     */
32271     
32272     get: function(layout_id) {
32273         if (typeof(this.groups[layout_id]) == 'undefined') {
32274             return false;
32275         }
32276         return this.groups[layout_id] ;
32277     }
32278     
32279     
32280     
32281 });
32282
32283  
32284
32285  /**
32286  *
32287  * This is based on 
32288  * http://masonry.desandro.com
32289  *
32290  * The idea is to render all the bricks based on vertical width...
32291  *
32292  * The original code extends 'outlayer' - we might need to use that....
32293  * 
32294  */
32295
32296
32297 /**
32298  * @class Roo.bootstrap.LayoutMasonryAuto
32299  * @extends Roo.bootstrap.Component
32300  * Bootstrap Layout Masonry class
32301  * 
32302  * @constructor
32303  * Create a new Element
32304  * @param {Object} config The config object
32305  */
32306
32307 Roo.bootstrap.LayoutMasonryAuto = function(config){
32308     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32309 };
32310
32311 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32312     
32313       /**
32314      * @cfg {Boolean} isFitWidth  - resize the width..
32315      */   
32316     isFitWidth : false,  // options..
32317     /**
32318      * @cfg {Boolean} isOriginLeft = left align?
32319      */   
32320     isOriginLeft : true,
32321     /**
32322      * @cfg {Boolean} isOriginTop = top align?
32323      */   
32324     isOriginTop : false,
32325     /**
32326      * @cfg {Boolean} isLayoutInstant = no animation?
32327      */   
32328     isLayoutInstant : false, // needed?
32329     /**
32330      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32331      */   
32332     isResizingContainer : true,
32333     /**
32334      * @cfg {Number} columnWidth  width of the columns 
32335      */   
32336     
32337     columnWidth : 0,
32338     
32339     /**
32340      * @cfg {Number} maxCols maximum number of columns
32341      */   
32342     
32343     maxCols: 0,
32344     /**
32345      * @cfg {Number} padHeight padding below box..
32346      */   
32347     
32348     padHeight : 10, 
32349     
32350     /**
32351      * @cfg {Boolean} isAutoInitial defalut true
32352      */   
32353     
32354     isAutoInitial : true, 
32355     
32356     // private?
32357     gutter : 0,
32358     
32359     containerWidth: 0,
32360     initialColumnWidth : 0,
32361     currentSize : null,
32362     
32363     colYs : null, // array.
32364     maxY : 0,
32365     padWidth: 10,
32366     
32367     
32368     tag: 'div',
32369     cls: '',
32370     bricks: null, //CompositeElement
32371     cols : 0, // array?
32372     // element : null, // wrapped now this.el
32373     _isLayoutInited : null, 
32374     
32375     
32376     getAutoCreate : function(){
32377         
32378         var cfg = {
32379             tag: this.tag,
32380             cls: 'blog-masonary-wrapper ' + this.cls,
32381             cn : {
32382                 cls : 'mas-boxes masonary'
32383             }
32384         };
32385         
32386         return cfg;
32387     },
32388     
32389     getChildContainer: function( )
32390     {
32391         if (this.boxesEl) {
32392             return this.boxesEl;
32393         }
32394         
32395         this.boxesEl = this.el.select('.mas-boxes').first();
32396         
32397         return this.boxesEl;
32398     },
32399     
32400     
32401     initEvents : function()
32402     {
32403         var _this = this;
32404         
32405         if(this.isAutoInitial){
32406             Roo.log('hook children rendered');
32407             this.on('childrenrendered', function() {
32408                 Roo.log('children rendered');
32409                 _this.initial();
32410             } ,this);
32411         }
32412         
32413     },
32414     
32415     initial : function()
32416     {
32417         this.reloadItems();
32418
32419         this.currentSize = this.el.getBox(true);
32420
32421         /// was window resize... - let's see if this works..
32422         Roo.EventManager.onWindowResize(this.resize, this); 
32423
32424         if(!this.isAutoInitial){
32425             this.layout();
32426             return;
32427         }
32428         
32429         this.layout.defer(500,this);
32430     },
32431     
32432     reloadItems: function()
32433     {
32434         this.bricks = this.el.select('.masonry-brick', true);
32435         
32436         this.bricks.each(function(b) {
32437             //Roo.log(b.getSize());
32438             if (!b.attr('originalwidth')) {
32439                 b.attr('originalwidth',  b.getSize().width);
32440             }
32441             
32442         });
32443         
32444         Roo.log(this.bricks.elements.length);
32445     },
32446     
32447     resize : function()
32448     {
32449         Roo.log('resize');
32450         var cs = this.el.getBox(true);
32451         
32452         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32453             Roo.log("no change in with or X");
32454             return;
32455         }
32456         this.currentSize = cs;
32457         this.layout();
32458     },
32459     
32460     layout : function()
32461     {
32462          Roo.log('layout');
32463         this._resetLayout();
32464         //this._manageStamps();
32465       
32466         // don't animate first layout
32467         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32468         this.layoutItems( isInstant );
32469       
32470         // flag for initalized
32471         this._isLayoutInited = true;
32472     },
32473     
32474     layoutItems : function( isInstant )
32475     {
32476         //var items = this._getItemsForLayout( this.items );
32477         // original code supports filtering layout items.. we just ignore it..
32478         
32479         this._layoutItems( this.bricks , isInstant );
32480       
32481         this._postLayout();
32482     },
32483     _layoutItems : function ( items , isInstant)
32484     {
32485        //this.fireEvent( 'layout', this, items );
32486     
32487
32488         if ( !items || !items.elements.length ) {
32489           // no items, emit event with empty array
32490             return;
32491         }
32492
32493         var queue = [];
32494         items.each(function(item) {
32495             Roo.log("layout item");
32496             Roo.log(item);
32497             // get x/y object from method
32498             var position = this._getItemLayoutPosition( item );
32499             // enqueue
32500             position.item = item;
32501             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32502             queue.push( position );
32503         }, this);
32504       
32505         this._processLayoutQueue( queue );
32506     },
32507     /** Sets position of item in DOM
32508     * @param {Element} item
32509     * @param {Number} x - horizontal position
32510     * @param {Number} y - vertical position
32511     * @param {Boolean} isInstant - disables transitions
32512     */
32513     _processLayoutQueue : function( queue )
32514     {
32515         for ( var i=0, len = queue.length; i < len; i++ ) {
32516             var obj = queue[i];
32517             obj.item.position('absolute');
32518             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32519         }
32520     },
32521       
32522     
32523     /**
32524     * Any logic you want to do after each layout,
32525     * i.e. size the container
32526     */
32527     _postLayout : function()
32528     {
32529         this.resizeContainer();
32530     },
32531     
32532     resizeContainer : function()
32533     {
32534         if ( !this.isResizingContainer ) {
32535             return;
32536         }
32537         var size = this._getContainerSize();
32538         if ( size ) {
32539             this.el.setSize(size.width,size.height);
32540             this.boxesEl.setSize(size.width,size.height);
32541         }
32542     },
32543     
32544     
32545     
32546     _resetLayout : function()
32547     {
32548         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32549         this.colWidth = this.el.getWidth();
32550         //this.gutter = this.el.getWidth(); 
32551         
32552         this.measureColumns();
32553
32554         // reset column Y
32555         var i = this.cols;
32556         this.colYs = [];
32557         while (i--) {
32558             this.colYs.push( 0 );
32559         }
32560     
32561         this.maxY = 0;
32562     },
32563
32564     measureColumns : function()
32565     {
32566         this.getContainerWidth();
32567       // if columnWidth is 0, default to outerWidth of first item
32568         if ( !this.columnWidth ) {
32569             var firstItem = this.bricks.first();
32570             Roo.log(firstItem);
32571             this.columnWidth  = this.containerWidth;
32572             if (firstItem && firstItem.attr('originalwidth') ) {
32573                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32574             }
32575             // columnWidth fall back to item of first element
32576             Roo.log("set column width?");
32577                         this.initialColumnWidth = this.columnWidth  ;
32578
32579             // if first elem has no width, default to size of container
32580             
32581         }
32582         
32583         
32584         if (this.initialColumnWidth) {
32585             this.columnWidth = this.initialColumnWidth;
32586         }
32587         
32588         
32589             
32590         // column width is fixed at the top - however if container width get's smaller we should
32591         // reduce it...
32592         
32593         // this bit calcs how man columns..
32594             
32595         var columnWidth = this.columnWidth += this.gutter;
32596       
32597         // calculate columns
32598         var containerWidth = this.containerWidth + this.gutter;
32599         
32600         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32601         // fix rounding errors, typically with gutters
32602         var excess = columnWidth - containerWidth % columnWidth;
32603         
32604         
32605         // if overshoot is less than a pixel, round up, otherwise floor it
32606         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32607         cols = Math[ mathMethod ]( cols );
32608         this.cols = Math.max( cols, 1 );
32609         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32610         
32611          // padding positioning..
32612         var totalColWidth = this.cols * this.columnWidth;
32613         var padavail = this.containerWidth - totalColWidth;
32614         // so for 2 columns - we need 3 'pads'
32615         
32616         var padNeeded = (1+this.cols) * this.padWidth;
32617         
32618         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32619         
32620         this.columnWidth += padExtra
32621         //this.padWidth = Math.floor(padavail /  ( this.cols));
32622         
32623         // adjust colum width so that padding is fixed??
32624         
32625         // we have 3 columns ... total = width * 3
32626         // we have X left over... that should be used by 
32627         
32628         //if (this.expandC) {
32629             
32630         //}
32631         
32632         
32633         
32634     },
32635     
32636     getContainerWidth : function()
32637     {
32638        /* // container is parent if fit width
32639         var container = this.isFitWidth ? this.element.parentNode : this.element;
32640         // check that this.size and size are there
32641         // IE8 triggers resize on body size change, so they might not be
32642         
32643         var size = getSize( container );  //FIXME
32644         this.containerWidth = size && size.innerWidth; //FIXME
32645         */
32646          
32647         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32648         
32649     },
32650     
32651     _getItemLayoutPosition : function( item )  // what is item?
32652     {
32653         // we resize the item to our columnWidth..
32654       
32655         item.setWidth(this.columnWidth);
32656         item.autoBoxAdjust  = false;
32657         
32658         var sz = item.getSize();
32659  
32660         // how many columns does this brick span
32661         var remainder = this.containerWidth % this.columnWidth;
32662         
32663         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32664         // round if off by 1 pixel, otherwise use ceil
32665         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32666         colSpan = Math.min( colSpan, this.cols );
32667         
32668         // normally this should be '1' as we dont' currently allow multi width columns..
32669         
32670         var colGroup = this._getColGroup( colSpan );
32671         // get the minimum Y value from the columns
32672         var minimumY = Math.min.apply( Math, colGroup );
32673         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32674         
32675         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32676          
32677         // position the brick
32678         var position = {
32679             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32680             y: this.currentSize.y + minimumY + this.padHeight
32681         };
32682         
32683         Roo.log(position);
32684         // apply setHeight to necessary columns
32685         var setHeight = minimumY + sz.height + this.padHeight;
32686         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32687         
32688         var setSpan = this.cols + 1 - colGroup.length;
32689         for ( var i = 0; i < setSpan; i++ ) {
32690           this.colYs[ shortColIndex + i ] = setHeight ;
32691         }
32692       
32693         return position;
32694     },
32695     
32696     /**
32697      * @param {Number} colSpan - number of columns the element spans
32698      * @returns {Array} colGroup
32699      */
32700     _getColGroup : function( colSpan )
32701     {
32702         if ( colSpan < 2 ) {
32703           // if brick spans only one column, use all the column Ys
32704           return this.colYs;
32705         }
32706       
32707         var colGroup = [];
32708         // how many different places could this brick fit horizontally
32709         var groupCount = this.cols + 1 - colSpan;
32710         // for each group potential horizontal position
32711         for ( var i = 0; i < groupCount; i++ ) {
32712           // make an array of colY values for that one group
32713           var groupColYs = this.colYs.slice( i, i + colSpan );
32714           // and get the max value of the array
32715           colGroup[i] = Math.max.apply( Math, groupColYs );
32716         }
32717         return colGroup;
32718     },
32719     /*
32720     _manageStamp : function( stamp )
32721     {
32722         var stampSize =  stamp.getSize();
32723         var offset = stamp.getBox();
32724         // get the columns that this stamp affects
32725         var firstX = this.isOriginLeft ? offset.x : offset.right;
32726         var lastX = firstX + stampSize.width;
32727         var firstCol = Math.floor( firstX / this.columnWidth );
32728         firstCol = Math.max( 0, firstCol );
32729         
32730         var lastCol = Math.floor( lastX / this.columnWidth );
32731         // lastCol should not go over if multiple of columnWidth #425
32732         lastCol -= lastX % this.columnWidth ? 0 : 1;
32733         lastCol = Math.min( this.cols - 1, lastCol );
32734         
32735         // set colYs to bottom of the stamp
32736         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32737             stampSize.height;
32738             
32739         for ( var i = firstCol; i <= lastCol; i++ ) {
32740           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32741         }
32742     },
32743     */
32744     
32745     _getContainerSize : function()
32746     {
32747         this.maxY = Math.max.apply( Math, this.colYs );
32748         var size = {
32749             height: this.maxY
32750         };
32751       
32752         if ( this.isFitWidth ) {
32753             size.width = this._getContainerFitWidth();
32754         }
32755       
32756         return size;
32757     },
32758     
32759     _getContainerFitWidth : function()
32760     {
32761         var unusedCols = 0;
32762         // count unused columns
32763         var i = this.cols;
32764         while ( --i ) {
32765           if ( this.colYs[i] !== 0 ) {
32766             break;
32767           }
32768           unusedCols++;
32769         }
32770         // fit container to columns that have been used
32771         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32772     },
32773     
32774     needsResizeLayout : function()
32775     {
32776         var previousWidth = this.containerWidth;
32777         this.getContainerWidth();
32778         return previousWidth !== this.containerWidth;
32779     }
32780  
32781 });
32782
32783  
32784
32785  /*
32786  * - LGPL
32787  *
32788  * element
32789  * 
32790  */
32791
32792 /**
32793  * @class Roo.bootstrap.MasonryBrick
32794  * @extends Roo.bootstrap.Component
32795  * Bootstrap MasonryBrick class
32796  * 
32797  * @constructor
32798  * Create a new MasonryBrick
32799  * @param {Object} config The config object
32800  */
32801
32802 Roo.bootstrap.MasonryBrick = function(config){
32803     
32804     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32805     
32806     Roo.bootstrap.MasonryBrick.register(this);
32807     
32808     this.addEvents({
32809         // raw events
32810         /**
32811          * @event click
32812          * When a MasonryBrick is clcik
32813          * @param {Roo.bootstrap.MasonryBrick} this
32814          * @param {Roo.EventObject} e
32815          */
32816         "click" : true
32817     });
32818 };
32819
32820 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32821     
32822     /**
32823      * @cfg {String} title
32824      */   
32825     title : '',
32826     /**
32827      * @cfg {String} html
32828      */   
32829     html : '',
32830     /**
32831      * @cfg {String} bgimage
32832      */   
32833     bgimage : '',
32834     /**
32835      * @cfg {String} videourl
32836      */   
32837     videourl : '',
32838     /**
32839      * @cfg {String} cls
32840      */   
32841     cls : '',
32842     /**
32843      * @cfg {String} href
32844      */   
32845     href : '',
32846     /**
32847      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32848      */   
32849     size : 'xs',
32850     
32851     /**
32852      * @cfg {String} placetitle (center|bottom)
32853      */   
32854     placetitle : '',
32855     
32856     /**
32857      * @cfg {Boolean} isFitContainer defalut true
32858      */   
32859     isFitContainer : true, 
32860     
32861     /**
32862      * @cfg {Boolean} preventDefault defalut false
32863      */   
32864     preventDefault : false, 
32865     
32866     /**
32867      * @cfg {Boolean} inverse defalut false
32868      */   
32869     maskInverse : false, 
32870     
32871     getAutoCreate : function()
32872     {
32873         if(!this.isFitContainer){
32874             return this.getSplitAutoCreate();
32875         }
32876         
32877         var cls = 'masonry-brick masonry-brick-full';
32878         
32879         if(this.href.length){
32880             cls += ' masonry-brick-link';
32881         }
32882         
32883         if(this.bgimage.length){
32884             cls += ' masonry-brick-image';
32885         }
32886         
32887         if(this.maskInverse){
32888             cls += ' mask-inverse';
32889         }
32890         
32891         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32892             cls += ' enable-mask';
32893         }
32894         
32895         if(this.size){
32896             cls += ' masonry-' + this.size + '-brick';
32897         }
32898         
32899         if(this.placetitle.length){
32900             
32901             switch (this.placetitle) {
32902                 case 'center' :
32903                     cls += ' masonry-center-title';
32904                     break;
32905                 case 'bottom' :
32906                     cls += ' masonry-bottom-title';
32907                     break;
32908                 default:
32909                     break;
32910             }
32911             
32912         } else {
32913             if(!this.html.length && !this.bgimage.length){
32914                 cls += ' masonry-center-title';
32915             }
32916
32917             if(!this.html.length && this.bgimage.length){
32918                 cls += ' masonry-bottom-title';
32919             }
32920         }
32921         
32922         if(this.cls){
32923             cls += ' ' + this.cls;
32924         }
32925         
32926         var cfg = {
32927             tag: (this.href.length) ? 'a' : 'div',
32928             cls: cls,
32929             cn: [
32930                 {
32931                     tag: 'div',
32932                     cls: 'masonry-brick-mask'
32933                 },
32934                 {
32935                     tag: 'div',
32936                     cls: 'masonry-brick-paragraph',
32937                     cn: []
32938                 }
32939             ]
32940         };
32941         
32942         if(this.href.length){
32943             cfg.href = this.href;
32944         }
32945         
32946         var cn = cfg.cn[1].cn;
32947         
32948         if(this.title.length){
32949             cn.push({
32950                 tag: 'h4',
32951                 cls: 'masonry-brick-title',
32952                 html: this.title
32953             });
32954         }
32955         
32956         if(this.html.length){
32957             cn.push({
32958                 tag: 'p',
32959                 cls: 'masonry-brick-text',
32960                 html: this.html
32961             });
32962         }
32963         
32964         if (!this.title.length && !this.html.length) {
32965             cfg.cn[1].cls += ' hide';
32966         }
32967         
32968         if(this.bgimage.length){
32969             cfg.cn.push({
32970                 tag: 'img',
32971                 cls: 'masonry-brick-image-view',
32972                 src: this.bgimage
32973             });
32974         }
32975         
32976         if(this.videourl.length){
32977             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32978             // youtube support only?
32979             cfg.cn.push({
32980                 tag: 'iframe',
32981                 cls: 'masonry-brick-image-view',
32982                 src: vurl,
32983                 frameborder : 0,
32984                 allowfullscreen : true
32985             });
32986         }
32987         
32988         return cfg;
32989         
32990     },
32991     
32992     getSplitAutoCreate : function()
32993     {
32994         var cls = 'masonry-brick masonry-brick-split';
32995         
32996         if(this.href.length){
32997             cls += ' masonry-brick-link';
32998         }
32999         
33000         if(this.bgimage.length){
33001             cls += ' masonry-brick-image';
33002         }
33003         
33004         if(this.size){
33005             cls += ' masonry-' + this.size + '-brick';
33006         }
33007         
33008         switch (this.placetitle) {
33009             case 'center' :
33010                 cls += ' masonry-center-title';
33011                 break;
33012             case 'bottom' :
33013                 cls += ' masonry-bottom-title';
33014                 break;
33015             default:
33016                 if(!this.bgimage.length){
33017                     cls += ' masonry-center-title';
33018                 }
33019
33020                 if(this.bgimage.length){
33021                     cls += ' masonry-bottom-title';
33022                 }
33023                 break;
33024         }
33025         
33026         if(this.cls){
33027             cls += ' ' + this.cls;
33028         }
33029         
33030         var cfg = {
33031             tag: (this.href.length) ? 'a' : 'div',
33032             cls: cls,
33033             cn: [
33034                 {
33035                     tag: 'div',
33036                     cls: 'masonry-brick-split-head',
33037                     cn: [
33038                         {
33039                             tag: 'div',
33040                             cls: 'masonry-brick-paragraph',
33041                             cn: []
33042                         }
33043                     ]
33044                 },
33045                 {
33046                     tag: 'div',
33047                     cls: 'masonry-brick-split-body',
33048                     cn: []
33049                 }
33050             ]
33051         };
33052         
33053         if(this.href.length){
33054             cfg.href = this.href;
33055         }
33056         
33057         if(this.title.length){
33058             cfg.cn[0].cn[0].cn.push({
33059                 tag: 'h4',
33060                 cls: 'masonry-brick-title',
33061                 html: this.title
33062             });
33063         }
33064         
33065         if(this.html.length){
33066             cfg.cn[1].cn.push({
33067                 tag: 'p',
33068                 cls: 'masonry-brick-text',
33069                 html: this.html
33070             });
33071         }
33072
33073         if(this.bgimage.length){
33074             cfg.cn[0].cn.push({
33075                 tag: 'img',
33076                 cls: 'masonry-brick-image-view',
33077                 src: this.bgimage
33078             });
33079         }
33080         
33081         if(this.videourl.length){
33082             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33083             // youtube support only?
33084             cfg.cn[0].cn.cn.push({
33085                 tag: 'iframe',
33086                 cls: 'masonry-brick-image-view',
33087                 src: vurl,
33088                 frameborder : 0,
33089                 allowfullscreen : true
33090             });
33091         }
33092         
33093         return cfg;
33094     },
33095     
33096     initEvents: function() 
33097     {
33098         switch (this.size) {
33099             case 'xs' :
33100                 this.x = 1;
33101                 this.y = 1;
33102                 break;
33103             case 'sm' :
33104                 this.x = 2;
33105                 this.y = 2;
33106                 break;
33107             case 'md' :
33108             case 'md-left' :
33109             case 'md-right' :
33110                 this.x = 3;
33111                 this.y = 3;
33112                 break;
33113             case 'tall' :
33114                 this.x = 2;
33115                 this.y = 3;
33116                 break;
33117             case 'wide' :
33118                 this.x = 3;
33119                 this.y = 2;
33120                 break;
33121             case 'wide-thin' :
33122                 this.x = 3;
33123                 this.y = 1;
33124                 break;
33125                         
33126             default :
33127                 break;
33128         }
33129         
33130         if(Roo.isTouch){
33131             this.el.on('touchstart', this.onTouchStart, this);
33132             this.el.on('touchmove', this.onTouchMove, this);
33133             this.el.on('touchend', this.onTouchEnd, this);
33134             this.el.on('contextmenu', this.onContextMenu, this);
33135         } else {
33136             this.el.on('mouseenter'  ,this.enter, this);
33137             this.el.on('mouseleave', this.leave, this);
33138             this.el.on('click', this.onClick, this);
33139         }
33140         
33141         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33142             this.parent().bricks.push(this);   
33143         }
33144         
33145     },
33146     
33147     onClick: function(e, el)
33148     {
33149         var time = this.endTimer - this.startTimer;
33150         // Roo.log(e.preventDefault());
33151         if(Roo.isTouch){
33152             if(time > 1000){
33153                 e.preventDefault();
33154                 return;
33155             }
33156         }
33157         
33158         if(!this.preventDefault){
33159             return;
33160         }
33161         
33162         e.preventDefault();
33163         
33164         if (this.activeClass != '') {
33165             this.selectBrick();
33166         }
33167         
33168         this.fireEvent('click', this, e);
33169     },
33170     
33171     enter: function(e, el)
33172     {
33173         e.preventDefault();
33174         
33175         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33176             return;
33177         }
33178         
33179         if(this.bgimage.length && this.html.length){
33180             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33181         }
33182     },
33183     
33184     leave: function(e, el)
33185     {
33186         e.preventDefault();
33187         
33188         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33189             return;
33190         }
33191         
33192         if(this.bgimage.length && this.html.length){
33193             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33194         }
33195     },
33196     
33197     onTouchStart: function(e, el)
33198     {
33199 //        e.preventDefault();
33200         
33201         this.touchmoved = false;
33202         
33203         if(!this.isFitContainer){
33204             return;
33205         }
33206         
33207         if(!this.bgimage.length || !this.html.length){
33208             return;
33209         }
33210         
33211         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33212         
33213         this.timer = new Date().getTime();
33214         
33215     },
33216     
33217     onTouchMove: function(e, el)
33218     {
33219         this.touchmoved = true;
33220     },
33221     
33222     onContextMenu : function(e,el)
33223     {
33224         e.preventDefault();
33225         e.stopPropagation();
33226         return false;
33227     },
33228     
33229     onTouchEnd: function(e, el)
33230     {
33231 //        e.preventDefault();
33232         
33233         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33234         
33235             this.leave(e,el);
33236             
33237             return;
33238         }
33239         
33240         if(!this.bgimage.length || !this.html.length){
33241             
33242             if(this.href.length){
33243                 window.location.href = this.href;
33244             }
33245             
33246             return;
33247         }
33248         
33249         if(!this.isFitContainer){
33250             return;
33251         }
33252         
33253         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33254         
33255         window.location.href = this.href;
33256     },
33257     
33258     //selection on single brick only
33259     selectBrick : function() {
33260         
33261         if (!this.parentId) {
33262             return;
33263         }
33264         
33265         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33266         var index = m.selectedBrick.indexOf(this.id);
33267         
33268         if ( index > -1) {
33269             m.selectedBrick.splice(index,1);
33270             this.el.removeClass(this.activeClass);
33271             return;
33272         }
33273         
33274         for(var i = 0; i < m.selectedBrick.length; i++) {
33275             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33276             b.el.removeClass(b.activeClass);
33277         }
33278         
33279         m.selectedBrick = [];
33280         
33281         m.selectedBrick.push(this.id);
33282         this.el.addClass(this.activeClass);
33283         return;
33284     },
33285     
33286     isSelected : function(){
33287         return this.el.hasClass(this.activeClass);
33288         
33289     }
33290 });
33291
33292 Roo.apply(Roo.bootstrap.MasonryBrick, {
33293     
33294     //groups: {},
33295     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33296      /**
33297     * register a Masonry Brick
33298     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33299     */
33300     
33301     register : function(brick)
33302     {
33303         //this.groups[brick.id] = brick;
33304         this.groups.add(brick.id, brick);
33305     },
33306     /**
33307     * fetch a  masonry brick based on the masonry brick ID
33308     * @param {string} the masonry brick to add
33309     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33310     */
33311     
33312     get: function(brick_id) 
33313     {
33314         // if (typeof(this.groups[brick_id]) == 'undefined') {
33315         //     return false;
33316         // }
33317         // return this.groups[brick_id] ;
33318         
33319         if(this.groups.key(brick_id)) {
33320             return this.groups.key(brick_id);
33321         }
33322         
33323         return false;
33324     }
33325     
33326     
33327     
33328 });
33329
33330  /*
33331  * - LGPL
33332  *
33333  * element
33334  * 
33335  */
33336
33337 /**
33338  * @class Roo.bootstrap.Brick
33339  * @extends Roo.bootstrap.Component
33340  * Bootstrap Brick class
33341  * 
33342  * @constructor
33343  * Create a new Brick
33344  * @param {Object} config The config object
33345  */
33346
33347 Roo.bootstrap.Brick = function(config){
33348     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33349     
33350     this.addEvents({
33351         // raw events
33352         /**
33353          * @event click
33354          * When a Brick is click
33355          * @param {Roo.bootstrap.Brick} this
33356          * @param {Roo.EventObject} e
33357          */
33358         "click" : true
33359     });
33360 };
33361
33362 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33363     
33364     /**
33365      * @cfg {String} title
33366      */   
33367     title : '',
33368     /**
33369      * @cfg {String} html
33370      */   
33371     html : '',
33372     /**
33373      * @cfg {String} bgimage
33374      */   
33375     bgimage : '',
33376     /**
33377      * @cfg {String} cls
33378      */   
33379     cls : '',
33380     /**
33381      * @cfg {String} href
33382      */   
33383     href : '',
33384     /**
33385      * @cfg {String} video
33386      */   
33387     video : '',
33388     /**
33389      * @cfg {Boolean} square
33390      */   
33391     square : true,
33392     
33393     getAutoCreate : function()
33394     {
33395         var cls = 'roo-brick';
33396         
33397         if(this.href.length){
33398             cls += ' roo-brick-link';
33399         }
33400         
33401         if(this.bgimage.length){
33402             cls += ' roo-brick-image';
33403         }
33404         
33405         if(!this.html.length && !this.bgimage.length){
33406             cls += ' roo-brick-center-title';
33407         }
33408         
33409         if(!this.html.length && this.bgimage.length){
33410             cls += ' roo-brick-bottom-title';
33411         }
33412         
33413         if(this.cls){
33414             cls += ' ' + this.cls;
33415         }
33416         
33417         var cfg = {
33418             tag: (this.href.length) ? 'a' : 'div',
33419             cls: cls,
33420             cn: [
33421                 {
33422                     tag: 'div',
33423                     cls: 'roo-brick-paragraph',
33424                     cn: []
33425                 }
33426             ]
33427         };
33428         
33429         if(this.href.length){
33430             cfg.href = this.href;
33431         }
33432         
33433         var cn = cfg.cn[0].cn;
33434         
33435         if(this.title.length){
33436             cn.push({
33437                 tag: 'h4',
33438                 cls: 'roo-brick-title',
33439                 html: this.title
33440             });
33441         }
33442         
33443         if(this.html.length){
33444             cn.push({
33445                 tag: 'p',
33446                 cls: 'roo-brick-text',
33447                 html: this.html
33448             });
33449         } else {
33450             cn.cls += ' hide';
33451         }
33452         
33453         if(this.bgimage.length){
33454             cfg.cn.push({
33455                 tag: 'img',
33456                 cls: 'roo-brick-image-view',
33457                 src: this.bgimage
33458             });
33459         }
33460         
33461         return cfg;
33462     },
33463     
33464     initEvents: function() 
33465     {
33466         if(this.title.length || this.html.length){
33467             this.el.on('mouseenter'  ,this.enter, this);
33468             this.el.on('mouseleave', this.leave, this);
33469         }
33470         
33471         Roo.EventManager.onWindowResize(this.resize, this); 
33472         
33473         if(this.bgimage.length){
33474             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33475             this.imageEl.on('load', this.onImageLoad, this);
33476             return;
33477         }
33478         
33479         this.resize();
33480     },
33481     
33482     onImageLoad : function()
33483     {
33484         this.resize();
33485     },
33486     
33487     resize : function()
33488     {
33489         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33490         
33491         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33492         
33493         if(this.bgimage.length){
33494             var image = this.el.select('.roo-brick-image-view', true).first();
33495             
33496             image.setWidth(paragraph.getWidth());
33497             
33498             if(this.square){
33499                 image.setHeight(paragraph.getWidth());
33500             }
33501             
33502             this.el.setHeight(image.getHeight());
33503             paragraph.setHeight(image.getHeight());
33504             
33505         }
33506         
33507     },
33508     
33509     enter: function(e, el)
33510     {
33511         e.preventDefault();
33512         
33513         if(this.bgimage.length){
33514             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33515             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33516         }
33517     },
33518     
33519     leave: function(e, el)
33520     {
33521         e.preventDefault();
33522         
33523         if(this.bgimage.length){
33524             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33525             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33526         }
33527     }
33528     
33529 });
33530
33531  
33532
33533  /*
33534  * - LGPL
33535  *
33536  * Number field 
33537  */
33538
33539 /**
33540  * @class Roo.bootstrap.NumberField
33541  * @extends Roo.bootstrap.Input
33542  * Bootstrap NumberField class
33543  * 
33544  * 
33545  * 
33546  * 
33547  * @constructor
33548  * Create a new NumberField
33549  * @param {Object} config The config object
33550  */
33551
33552 Roo.bootstrap.NumberField = function(config){
33553     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33554 };
33555
33556 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33557     
33558     /**
33559      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33560      */
33561     allowDecimals : true,
33562     /**
33563      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33564      */
33565     decimalSeparator : ".",
33566     /**
33567      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33568      */
33569     decimalPrecision : 2,
33570     /**
33571      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33572      */
33573     allowNegative : true,
33574     
33575     /**
33576      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33577      */
33578     allowZero: true,
33579     /**
33580      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33581      */
33582     minValue : Number.NEGATIVE_INFINITY,
33583     /**
33584      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33585      */
33586     maxValue : Number.MAX_VALUE,
33587     /**
33588      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33589      */
33590     minText : "The minimum value for this field is {0}",
33591     /**
33592      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33593      */
33594     maxText : "The maximum value for this field is {0}",
33595     /**
33596      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33597      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33598      */
33599     nanText : "{0} is not a valid number",
33600     /**
33601      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33602      */
33603     thousandsDelimiter : false,
33604     /**
33605      * @cfg {String} valueAlign alignment of value
33606      */
33607     valueAlign : "left",
33608
33609     getAutoCreate : function()
33610     {
33611         var hiddenInput = {
33612             tag: 'input',
33613             type: 'hidden',
33614             id: Roo.id(),
33615             cls: 'hidden-number-input'
33616         };
33617         
33618         if (this.name) {
33619             hiddenInput.name = this.name;
33620         }
33621         
33622         this.name = '';
33623         
33624         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33625         
33626         this.name = hiddenInput.name;
33627         
33628         if(cfg.cn.length > 0) {
33629             cfg.cn.push(hiddenInput);
33630         }
33631         
33632         return cfg;
33633     },
33634
33635     // private
33636     initEvents : function()
33637     {   
33638         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33639         
33640         var allowed = "0123456789";
33641         
33642         if(this.allowDecimals){
33643             allowed += this.decimalSeparator;
33644         }
33645         
33646         if(this.allowNegative){
33647             allowed += "-";
33648         }
33649         
33650         if(this.thousandsDelimiter) {
33651             allowed += ",";
33652         }
33653         
33654         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33655         
33656         var keyPress = function(e){
33657             
33658             var k = e.getKey();
33659             
33660             var c = e.getCharCode();
33661             
33662             if(
33663                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33664                     allowed.indexOf(String.fromCharCode(c)) === -1
33665             ){
33666                 e.stopEvent();
33667                 return;
33668             }
33669             
33670             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33671                 return;
33672             }
33673             
33674             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33675                 e.stopEvent();
33676             }
33677         };
33678         
33679         this.el.on("keypress", keyPress, this);
33680     },
33681     
33682     validateValue : function(value)
33683     {
33684         
33685         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33686             return false;
33687         }
33688         
33689         var num = this.parseValue(value);
33690         
33691         if(isNaN(num)){
33692             this.markInvalid(String.format(this.nanText, value));
33693             return false;
33694         }
33695         
33696         if(num < this.minValue){
33697             this.markInvalid(String.format(this.minText, this.minValue));
33698             return false;
33699         }
33700         
33701         if(num > this.maxValue){
33702             this.markInvalid(String.format(this.maxText, this.maxValue));
33703             return false;
33704         }
33705         
33706         return true;
33707     },
33708
33709     getValue : function()
33710     {
33711         var v = this.hiddenEl().getValue();
33712         
33713         return this.fixPrecision(this.parseValue(v));
33714     },
33715
33716     parseValue : function(value)
33717     {
33718         if(this.thousandsDelimiter) {
33719             value += "";
33720             r = new RegExp(",", "g");
33721             value = value.replace(r, "");
33722         }
33723         
33724         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33725         return isNaN(value) ? '' : value;
33726     },
33727
33728     fixPrecision : function(value)
33729     {
33730         if(this.thousandsDelimiter) {
33731             value += "";
33732             r = new RegExp(",", "g");
33733             value = value.replace(r, "");
33734         }
33735         
33736         var nan = isNaN(value);
33737         
33738         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33739             return nan ? '' : value;
33740         }
33741         return parseFloat(value).toFixed(this.decimalPrecision);
33742     },
33743
33744     setValue : function(v)
33745     {
33746         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33747         
33748         this.value = v;
33749         
33750         if(this.rendered){
33751             
33752             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33753             
33754             this.inputEl().dom.value = (v == '') ? '' :
33755                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33756             
33757             if(!this.allowZero && v === '0') {
33758                 this.hiddenEl().dom.value = '';
33759                 this.inputEl().dom.value = '';
33760             }
33761             
33762             this.validate();
33763         }
33764     },
33765
33766     decimalPrecisionFcn : function(v)
33767     {
33768         return Math.floor(v);
33769     },
33770
33771     beforeBlur : function()
33772     {
33773         var v = this.parseValue(this.getRawValue());
33774         
33775         if(v || v === 0 || v === ''){
33776             this.setValue(v);
33777         }
33778     },
33779     
33780     hiddenEl : function()
33781     {
33782         return this.el.select('input.hidden-number-input',true).first();
33783     }
33784     
33785 });
33786
33787  
33788
33789 /*
33790 * Licence: LGPL
33791 */
33792
33793 /**
33794  * @class Roo.bootstrap.DocumentSlider
33795  * @extends Roo.bootstrap.Component
33796  * Bootstrap DocumentSlider class
33797  * 
33798  * @constructor
33799  * Create a new DocumentViewer
33800  * @param {Object} config The config object
33801  */
33802
33803 Roo.bootstrap.DocumentSlider = function(config){
33804     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33805     
33806     this.files = [];
33807     
33808     this.addEvents({
33809         /**
33810          * @event initial
33811          * Fire after initEvent
33812          * @param {Roo.bootstrap.DocumentSlider} this
33813          */
33814         "initial" : true,
33815         /**
33816          * @event update
33817          * Fire after update
33818          * @param {Roo.bootstrap.DocumentSlider} this
33819          */
33820         "update" : true,
33821         /**
33822          * @event click
33823          * Fire after click
33824          * @param {Roo.bootstrap.DocumentSlider} this
33825          */
33826         "click" : true
33827     });
33828 };
33829
33830 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33831     
33832     files : false,
33833     
33834     indicator : 0,
33835     
33836     getAutoCreate : function()
33837     {
33838         var cfg = {
33839             tag : 'div',
33840             cls : 'roo-document-slider',
33841             cn : [
33842                 {
33843                     tag : 'div',
33844                     cls : 'roo-document-slider-header',
33845                     cn : [
33846                         {
33847                             tag : 'div',
33848                             cls : 'roo-document-slider-header-title'
33849                         }
33850                     ]
33851                 },
33852                 {
33853                     tag : 'div',
33854                     cls : 'roo-document-slider-body',
33855                     cn : [
33856                         {
33857                             tag : 'div',
33858                             cls : 'roo-document-slider-prev',
33859                             cn : [
33860                                 {
33861                                     tag : 'i',
33862                                     cls : 'fa fa-chevron-left'
33863                                 }
33864                             ]
33865                         },
33866                         {
33867                             tag : 'div',
33868                             cls : 'roo-document-slider-thumb',
33869                             cn : [
33870                                 {
33871                                     tag : 'img',
33872                                     cls : 'roo-document-slider-image'
33873                                 }
33874                             ]
33875                         },
33876                         {
33877                             tag : 'div',
33878                             cls : 'roo-document-slider-next',
33879                             cn : [
33880                                 {
33881                                     tag : 'i',
33882                                     cls : 'fa fa-chevron-right'
33883                                 }
33884                             ]
33885                         }
33886                     ]
33887                 }
33888             ]
33889         };
33890         
33891         return cfg;
33892     },
33893     
33894     initEvents : function()
33895     {
33896         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33897         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33898         
33899         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33900         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33901         
33902         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33903         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33904         
33905         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33906         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33907         
33908         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33909         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33910         
33911         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33912         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33913         
33914         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33915         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33916         
33917         this.thumbEl.on('click', this.onClick, this);
33918         
33919         this.prevIndicator.on('click', this.prev, this);
33920         
33921         this.nextIndicator.on('click', this.next, this);
33922         
33923     },
33924     
33925     initial : function()
33926     {
33927         if(this.files.length){
33928             this.indicator = 1;
33929             this.update()
33930         }
33931         
33932         this.fireEvent('initial', this);
33933     },
33934     
33935     update : function()
33936     {
33937         this.imageEl.attr('src', this.files[this.indicator - 1]);
33938         
33939         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33940         
33941         this.prevIndicator.show();
33942         
33943         if(this.indicator == 1){
33944             this.prevIndicator.hide();
33945         }
33946         
33947         this.nextIndicator.show();
33948         
33949         if(this.indicator == this.files.length){
33950             this.nextIndicator.hide();
33951         }
33952         
33953         this.thumbEl.scrollTo('top');
33954         
33955         this.fireEvent('update', this);
33956     },
33957     
33958     onClick : function(e)
33959     {
33960         e.preventDefault();
33961         
33962         this.fireEvent('click', this);
33963     },
33964     
33965     prev : function(e)
33966     {
33967         e.preventDefault();
33968         
33969         this.indicator = Math.max(1, this.indicator - 1);
33970         
33971         this.update();
33972     },
33973     
33974     next : function(e)
33975     {
33976         e.preventDefault();
33977         
33978         this.indicator = Math.min(this.files.length, this.indicator + 1);
33979         
33980         this.update();
33981     }
33982 });
33983 /*
33984  * - LGPL
33985  *
33986  * RadioSet
33987  *
33988  *
33989  */
33990
33991 /**
33992  * @class Roo.bootstrap.RadioSet
33993  * @extends Roo.bootstrap.Input
33994  * Bootstrap RadioSet class
33995  * @cfg {String} indicatorpos (left|right) default left
33996  * @cfg {Boolean} inline (true|false) inline the element (default true)
33997  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33998  * @constructor
33999  * Create a new RadioSet
34000  * @param {Object} config The config object
34001  */
34002
34003 Roo.bootstrap.RadioSet = function(config){
34004     
34005     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34006     
34007     this.radioes = [];
34008     
34009     Roo.bootstrap.RadioSet.register(this);
34010     
34011     this.addEvents({
34012         /**
34013         * @event check
34014         * Fires when the element is checked or unchecked.
34015         * @param {Roo.bootstrap.RadioSet} this This radio
34016         * @param {Roo.bootstrap.Radio} item The checked item
34017         */
34018        check : true,
34019        /**
34020         * @event click
34021         * Fires when the element is click.
34022         * @param {Roo.bootstrap.RadioSet} this This radio set
34023         * @param {Roo.bootstrap.Radio} item The checked item
34024         * @param {Roo.EventObject} e The event object
34025         */
34026        click : true
34027     });
34028     
34029 };
34030
34031 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34032
34033     radioes : false,
34034     
34035     inline : true,
34036     
34037     weight : '',
34038     
34039     indicatorpos : 'left',
34040     
34041     getAutoCreate : function()
34042     {
34043         var label = {
34044             tag : 'label',
34045             cls : 'roo-radio-set-label',
34046             cn : [
34047                 {
34048                     tag : 'span',
34049                     html : this.fieldLabel
34050                 }
34051             ]
34052         };
34053         
34054         if(this.indicatorpos == 'left'){
34055             label.cn.unshift({
34056                 tag : 'i',
34057                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34058                 tooltip : 'This field is required'
34059             });
34060         } else {
34061             label.cn.push({
34062                 tag : 'i',
34063                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34064                 tooltip : 'This field is required'
34065             });
34066         }
34067         
34068         var items = {
34069             tag : 'div',
34070             cls : 'roo-radio-set-items'
34071         };
34072         
34073         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34074         
34075         if (align === 'left' && this.fieldLabel.length) {
34076             
34077             items = {
34078                 cls : "roo-radio-set-right", 
34079                 cn: [
34080                     items
34081                 ]
34082             };
34083             
34084             if(this.labelWidth > 12){
34085                 label.style = "width: " + this.labelWidth + 'px';
34086             }
34087             
34088             if(this.labelWidth < 13 && this.labelmd == 0){
34089                 this.labelmd = this.labelWidth;
34090             }
34091             
34092             if(this.labellg > 0){
34093                 label.cls += ' col-lg-' + this.labellg;
34094                 items.cls += ' col-lg-' + (12 - this.labellg);
34095             }
34096             
34097             if(this.labelmd > 0){
34098                 label.cls += ' col-md-' + this.labelmd;
34099                 items.cls += ' col-md-' + (12 - this.labelmd);
34100             }
34101             
34102             if(this.labelsm > 0){
34103                 label.cls += ' col-sm-' + this.labelsm;
34104                 items.cls += ' col-sm-' + (12 - this.labelsm);
34105             }
34106             
34107             if(this.labelxs > 0){
34108                 label.cls += ' col-xs-' + this.labelxs;
34109                 items.cls += ' col-xs-' + (12 - this.labelxs);
34110             }
34111         }
34112         
34113         var cfg = {
34114             tag : 'div',
34115             cls : 'roo-radio-set',
34116             cn : [
34117                 {
34118                     tag : 'input',
34119                     cls : 'roo-radio-set-input',
34120                     type : 'hidden',
34121                     name : this.name,
34122                     value : this.value ? this.value :  ''
34123                 },
34124                 label,
34125                 items
34126             ]
34127         };
34128         
34129         if(this.weight.length){
34130             cfg.cls += ' roo-radio-' + this.weight;
34131         }
34132         
34133         if(this.inline) {
34134             cfg.cls += ' roo-radio-set-inline';
34135         }
34136         
34137         var settings=this;
34138         ['xs','sm','md','lg'].map(function(size){
34139             if (settings[size]) {
34140                 cfg.cls += ' col-' + size + '-' + settings[size];
34141             }
34142         });
34143         
34144         return cfg;
34145         
34146     },
34147
34148     initEvents : function()
34149     {
34150         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34151         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34152         
34153         if(!this.fieldLabel.length){
34154             this.labelEl.hide();
34155         }
34156         
34157         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34158         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34159         
34160         this.indicator = this.indicatorEl();
34161         
34162         if(this.indicator){
34163             this.indicator.addClass('invisible');
34164         }
34165         
34166         this.originalValue = this.getValue();
34167         
34168     },
34169     
34170     inputEl: function ()
34171     {
34172         return this.el.select('.roo-radio-set-input', true).first();
34173     },
34174     
34175     getChildContainer : function()
34176     {
34177         return this.itemsEl;
34178     },
34179     
34180     register : function(item)
34181     {
34182         this.radioes.push(item);
34183         
34184     },
34185     
34186     validate : function()
34187     {   
34188         if(this.getVisibilityEl().hasClass('hidden')){
34189             return true;
34190         }
34191         
34192         var valid = false;
34193         
34194         Roo.each(this.radioes, function(i){
34195             if(!i.checked){
34196                 return;
34197             }
34198             
34199             valid = true;
34200             return false;
34201         });
34202         
34203         if(this.allowBlank) {
34204             return true;
34205         }
34206         
34207         if(this.disabled || valid){
34208             this.markValid();
34209             return true;
34210         }
34211         
34212         this.markInvalid();
34213         return false;
34214         
34215     },
34216     
34217     markValid : function()
34218     {
34219         if(this.labelEl.isVisible(true)){
34220             this.indicatorEl().removeClass('visible');
34221             this.indicatorEl().addClass('invisible');
34222         }
34223         
34224         this.el.removeClass([this.invalidClass, this.validClass]);
34225         this.el.addClass(this.validClass);
34226         
34227         this.fireEvent('valid', this);
34228     },
34229     
34230     markInvalid : function(msg)
34231     {
34232         if(this.allowBlank || this.disabled){
34233             return;
34234         }
34235         
34236         if(this.labelEl.isVisible(true)){
34237             this.indicatorEl().removeClass('invisible');
34238             this.indicatorEl().addClass('visible');
34239         }
34240         
34241         this.el.removeClass([this.invalidClass, this.validClass]);
34242         this.el.addClass(this.invalidClass);
34243         
34244         this.fireEvent('invalid', this, msg);
34245         
34246     },
34247     
34248     setValue : function(v, suppressEvent)
34249     {   
34250         if(this.value === v){
34251             return;
34252         }
34253         
34254         this.value = v;
34255         
34256         if(this.rendered){
34257             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34258         }
34259         
34260         Roo.each(this.radioes, function(i){
34261             i.checked = false;
34262             i.el.removeClass('checked');
34263         });
34264         
34265         Roo.each(this.radioes, function(i){
34266             
34267             if(i.value === v || i.value.toString() === v.toString()){
34268                 i.checked = true;
34269                 i.el.addClass('checked');
34270                 
34271                 if(suppressEvent !== true){
34272                     this.fireEvent('check', this, i);
34273                 }
34274                 
34275                 return false;
34276             }
34277             
34278         }, this);
34279         
34280         this.validate();
34281     },
34282     
34283     clearInvalid : function(){
34284         
34285         if(!this.el || this.preventMark){
34286             return;
34287         }
34288         
34289         this.el.removeClass([this.invalidClass]);
34290         
34291         this.fireEvent('valid', this);
34292     }
34293     
34294 });
34295
34296 Roo.apply(Roo.bootstrap.RadioSet, {
34297     
34298     groups: {},
34299     
34300     register : function(set)
34301     {
34302         this.groups[set.name] = set;
34303     },
34304     
34305     get: function(name) 
34306     {
34307         if (typeof(this.groups[name]) == 'undefined') {
34308             return false;
34309         }
34310         
34311         return this.groups[name] ;
34312     }
34313     
34314 });
34315 /*
34316  * Based on:
34317  * Ext JS Library 1.1.1
34318  * Copyright(c) 2006-2007, Ext JS, LLC.
34319  *
34320  * Originally Released Under LGPL - original licence link has changed is not relivant.
34321  *
34322  * Fork - LGPL
34323  * <script type="text/javascript">
34324  */
34325
34326
34327 /**
34328  * @class Roo.bootstrap.SplitBar
34329  * @extends Roo.util.Observable
34330  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34331  * <br><br>
34332  * Usage:
34333  * <pre><code>
34334 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34335                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34336 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34337 split.minSize = 100;
34338 split.maxSize = 600;
34339 split.animate = true;
34340 split.on('moved', splitterMoved);
34341 </code></pre>
34342  * @constructor
34343  * Create a new SplitBar
34344  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34345  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34346  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34347  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34348                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34349                         position of the SplitBar).
34350  */
34351 Roo.bootstrap.SplitBar = function(cfg){
34352     
34353     /** @private */
34354     
34355     //{
34356     //  dragElement : elm
34357     //  resizingElement: el,
34358         // optional..
34359     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34360     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34361         // existingProxy ???
34362     //}
34363     
34364     this.el = Roo.get(cfg.dragElement, true);
34365     this.el.dom.unselectable = "on";
34366     /** @private */
34367     this.resizingEl = Roo.get(cfg.resizingElement, true);
34368
34369     /**
34370      * @private
34371      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34372      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34373      * @type Number
34374      */
34375     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34376     
34377     /**
34378      * The minimum size of the resizing element. (Defaults to 0)
34379      * @type Number
34380      */
34381     this.minSize = 0;
34382     
34383     /**
34384      * The maximum size of the resizing element. (Defaults to 2000)
34385      * @type Number
34386      */
34387     this.maxSize = 2000;
34388     
34389     /**
34390      * Whether to animate the transition to the new size
34391      * @type Boolean
34392      */
34393     this.animate = false;
34394     
34395     /**
34396      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34397      * @type Boolean
34398      */
34399     this.useShim = false;
34400     
34401     /** @private */
34402     this.shim = null;
34403     
34404     if(!cfg.existingProxy){
34405         /** @private */
34406         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34407     }else{
34408         this.proxy = Roo.get(cfg.existingProxy).dom;
34409     }
34410     /** @private */
34411     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34412     
34413     /** @private */
34414     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34415     
34416     /** @private */
34417     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34418     
34419     /** @private */
34420     this.dragSpecs = {};
34421     
34422     /**
34423      * @private The adapter to use to positon and resize elements
34424      */
34425     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34426     this.adapter.init(this);
34427     
34428     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34429         /** @private */
34430         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34431         this.el.addClass("roo-splitbar-h");
34432     }else{
34433         /** @private */
34434         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34435         this.el.addClass("roo-splitbar-v");
34436     }
34437     
34438     this.addEvents({
34439         /**
34440          * @event resize
34441          * Fires when the splitter is moved (alias for {@link #event-moved})
34442          * @param {Roo.bootstrap.SplitBar} this
34443          * @param {Number} newSize the new width or height
34444          */
34445         "resize" : true,
34446         /**
34447          * @event moved
34448          * Fires when the splitter is moved
34449          * @param {Roo.bootstrap.SplitBar} this
34450          * @param {Number} newSize the new width or height
34451          */
34452         "moved" : true,
34453         /**
34454          * @event beforeresize
34455          * Fires before the splitter is dragged
34456          * @param {Roo.bootstrap.SplitBar} this
34457          */
34458         "beforeresize" : true,
34459
34460         "beforeapply" : true
34461     });
34462
34463     Roo.util.Observable.call(this);
34464 };
34465
34466 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34467     onStartProxyDrag : function(x, y){
34468         this.fireEvent("beforeresize", this);
34469         if(!this.overlay){
34470             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34471             o.unselectable();
34472             o.enableDisplayMode("block");
34473             // all splitbars share the same overlay
34474             Roo.bootstrap.SplitBar.prototype.overlay = o;
34475         }
34476         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34477         this.overlay.show();
34478         Roo.get(this.proxy).setDisplayed("block");
34479         var size = this.adapter.getElementSize(this);
34480         this.activeMinSize = this.getMinimumSize();;
34481         this.activeMaxSize = this.getMaximumSize();;
34482         var c1 = size - this.activeMinSize;
34483         var c2 = Math.max(this.activeMaxSize - size, 0);
34484         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34485             this.dd.resetConstraints();
34486             this.dd.setXConstraint(
34487                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34488                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34489             );
34490             this.dd.setYConstraint(0, 0);
34491         }else{
34492             this.dd.resetConstraints();
34493             this.dd.setXConstraint(0, 0);
34494             this.dd.setYConstraint(
34495                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34496                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34497             );
34498          }
34499         this.dragSpecs.startSize = size;
34500         this.dragSpecs.startPoint = [x, y];
34501         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34502     },
34503     
34504     /** 
34505      * @private Called after the drag operation by the DDProxy
34506      */
34507     onEndProxyDrag : function(e){
34508         Roo.get(this.proxy).setDisplayed(false);
34509         var endPoint = Roo.lib.Event.getXY(e);
34510         if(this.overlay){
34511             this.overlay.hide();
34512         }
34513         var newSize;
34514         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34515             newSize = this.dragSpecs.startSize + 
34516                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34517                     endPoint[0] - this.dragSpecs.startPoint[0] :
34518                     this.dragSpecs.startPoint[0] - endPoint[0]
34519                 );
34520         }else{
34521             newSize = this.dragSpecs.startSize + 
34522                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34523                     endPoint[1] - this.dragSpecs.startPoint[1] :
34524                     this.dragSpecs.startPoint[1] - endPoint[1]
34525                 );
34526         }
34527         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34528         if(newSize != this.dragSpecs.startSize){
34529             if(this.fireEvent('beforeapply', this, newSize) !== false){
34530                 this.adapter.setElementSize(this, newSize);
34531                 this.fireEvent("moved", this, newSize);
34532                 this.fireEvent("resize", this, newSize);
34533             }
34534         }
34535     },
34536     
34537     /**
34538      * Get the adapter this SplitBar uses
34539      * @return The adapter object
34540      */
34541     getAdapter : function(){
34542         return this.adapter;
34543     },
34544     
34545     /**
34546      * Set the adapter this SplitBar uses
34547      * @param {Object} adapter A SplitBar adapter object
34548      */
34549     setAdapter : function(adapter){
34550         this.adapter = adapter;
34551         this.adapter.init(this);
34552     },
34553     
34554     /**
34555      * Gets the minimum size for the resizing element
34556      * @return {Number} The minimum size
34557      */
34558     getMinimumSize : function(){
34559         return this.minSize;
34560     },
34561     
34562     /**
34563      * Sets the minimum size for the resizing element
34564      * @param {Number} minSize The minimum size
34565      */
34566     setMinimumSize : function(minSize){
34567         this.minSize = minSize;
34568     },
34569     
34570     /**
34571      * Gets the maximum size for the resizing element
34572      * @return {Number} The maximum size
34573      */
34574     getMaximumSize : function(){
34575         return this.maxSize;
34576     },
34577     
34578     /**
34579      * Sets the maximum size for the resizing element
34580      * @param {Number} maxSize The maximum size
34581      */
34582     setMaximumSize : function(maxSize){
34583         this.maxSize = maxSize;
34584     },
34585     
34586     /**
34587      * Sets the initialize size for the resizing element
34588      * @param {Number} size The initial size
34589      */
34590     setCurrentSize : function(size){
34591         var oldAnimate = this.animate;
34592         this.animate = false;
34593         this.adapter.setElementSize(this, size);
34594         this.animate = oldAnimate;
34595     },
34596     
34597     /**
34598      * Destroy this splitbar. 
34599      * @param {Boolean} removeEl True to remove the element
34600      */
34601     destroy : function(removeEl){
34602         if(this.shim){
34603             this.shim.remove();
34604         }
34605         this.dd.unreg();
34606         this.proxy.parentNode.removeChild(this.proxy);
34607         if(removeEl){
34608             this.el.remove();
34609         }
34610     }
34611 });
34612
34613 /**
34614  * @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.
34615  */
34616 Roo.bootstrap.SplitBar.createProxy = function(dir){
34617     var proxy = new Roo.Element(document.createElement("div"));
34618     proxy.unselectable();
34619     var cls = 'roo-splitbar-proxy';
34620     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34621     document.body.appendChild(proxy.dom);
34622     return proxy.dom;
34623 };
34624
34625 /** 
34626  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34627  * Default Adapter. It assumes the splitter and resizing element are not positioned
34628  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34629  */
34630 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34631 };
34632
34633 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34634     // do nothing for now
34635     init : function(s){
34636     
34637     },
34638     /**
34639      * Called before drag operations to get the current size of the resizing element. 
34640      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34641      */
34642      getElementSize : function(s){
34643         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34644             return s.resizingEl.getWidth();
34645         }else{
34646             return s.resizingEl.getHeight();
34647         }
34648     },
34649     
34650     /**
34651      * Called after drag operations to set the size of the resizing element.
34652      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34653      * @param {Number} newSize The new size to set
34654      * @param {Function} onComplete A function to be invoked when resizing is complete
34655      */
34656     setElementSize : function(s, newSize, onComplete){
34657         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34658             if(!s.animate){
34659                 s.resizingEl.setWidth(newSize);
34660                 if(onComplete){
34661                     onComplete(s, newSize);
34662                 }
34663             }else{
34664                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34665             }
34666         }else{
34667             
34668             if(!s.animate){
34669                 s.resizingEl.setHeight(newSize);
34670                 if(onComplete){
34671                     onComplete(s, newSize);
34672                 }
34673             }else{
34674                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34675             }
34676         }
34677     }
34678 };
34679
34680 /** 
34681  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34682  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34683  * Adapter that  moves the splitter element to align with the resized sizing element. 
34684  * Used with an absolute positioned SplitBar.
34685  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34686  * document.body, make sure you assign an id to the body element.
34687  */
34688 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34689     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34690     this.container = Roo.get(container);
34691 };
34692
34693 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34694     init : function(s){
34695         this.basic.init(s);
34696     },
34697     
34698     getElementSize : function(s){
34699         return this.basic.getElementSize(s);
34700     },
34701     
34702     setElementSize : function(s, newSize, onComplete){
34703         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34704     },
34705     
34706     moveSplitter : function(s){
34707         var yes = Roo.bootstrap.SplitBar;
34708         switch(s.placement){
34709             case yes.LEFT:
34710                 s.el.setX(s.resizingEl.getRight());
34711                 break;
34712             case yes.RIGHT:
34713                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34714                 break;
34715             case yes.TOP:
34716                 s.el.setY(s.resizingEl.getBottom());
34717                 break;
34718             case yes.BOTTOM:
34719                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34720                 break;
34721         }
34722     }
34723 };
34724
34725 /**
34726  * Orientation constant - Create a vertical SplitBar
34727  * @static
34728  * @type Number
34729  */
34730 Roo.bootstrap.SplitBar.VERTICAL = 1;
34731
34732 /**
34733  * Orientation constant - Create a horizontal SplitBar
34734  * @static
34735  * @type Number
34736  */
34737 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34738
34739 /**
34740  * Placement constant - The resizing element is to the left of the splitter element
34741  * @static
34742  * @type Number
34743  */
34744 Roo.bootstrap.SplitBar.LEFT = 1;
34745
34746 /**
34747  * Placement constant - The resizing element is to the right of the splitter element
34748  * @static
34749  * @type Number
34750  */
34751 Roo.bootstrap.SplitBar.RIGHT = 2;
34752
34753 /**
34754  * Placement constant - The resizing element is positioned above the splitter element
34755  * @static
34756  * @type Number
34757  */
34758 Roo.bootstrap.SplitBar.TOP = 3;
34759
34760 /**
34761  * Placement constant - The resizing element is positioned under splitter element
34762  * @static
34763  * @type Number
34764  */
34765 Roo.bootstrap.SplitBar.BOTTOM = 4;
34766 Roo.namespace("Roo.bootstrap.layout");/*
34767  * Based on:
34768  * Ext JS Library 1.1.1
34769  * Copyright(c) 2006-2007, Ext JS, LLC.
34770  *
34771  * Originally Released Under LGPL - original licence link has changed is not relivant.
34772  *
34773  * Fork - LGPL
34774  * <script type="text/javascript">
34775  */
34776
34777 /**
34778  * @class Roo.bootstrap.layout.Manager
34779  * @extends Roo.bootstrap.Component
34780  * Base class for layout managers.
34781  */
34782 Roo.bootstrap.layout.Manager = function(config)
34783 {
34784     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34785
34786
34787
34788
34789
34790     /** false to disable window resize monitoring @type Boolean */
34791     this.monitorWindowResize = true;
34792     this.regions = {};
34793     this.addEvents({
34794         /**
34795          * @event layout
34796          * Fires when a layout is performed.
34797          * @param {Roo.LayoutManager} this
34798          */
34799         "layout" : true,
34800         /**
34801          * @event regionresized
34802          * Fires when the user resizes a region.
34803          * @param {Roo.LayoutRegion} region The resized region
34804          * @param {Number} newSize The new size (width for east/west, height for north/south)
34805          */
34806         "regionresized" : true,
34807         /**
34808          * @event regioncollapsed
34809          * Fires when a region is collapsed.
34810          * @param {Roo.LayoutRegion} region The collapsed region
34811          */
34812         "regioncollapsed" : true,
34813         /**
34814          * @event regionexpanded
34815          * Fires when a region is expanded.
34816          * @param {Roo.LayoutRegion} region The expanded region
34817          */
34818         "regionexpanded" : true
34819     });
34820     this.updating = false;
34821
34822     if (config.el) {
34823         this.el = Roo.get(config.el);
34824         this.initEvents();
34825     }
34826
34827 };
34828
34829 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34830
34831
34832     regions : null,
34833
34834     monitorWindowResize : true,
34835
34836
34837     updating : false,
34838
34839
34840     onRender : function(ct, position)
34841     {
34842         if(!this.el){
34843             this.el = Roo.get(ct);
34844             this.initEvents();
34845         }
34846         //this.fireEvent('render',this);
34847     },
34848
34849
34850     initEvents: function()
34851     {
34852
34853
34854         // ie scrollbar fix
34855         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34856             document.body.scroll = "no";
34857         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34858             this.el.position('relative');
34859         }
34860         this.id = this.el.id;
34861         this.el.addClass("roo-layout-container");
34862         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34863         if(this.el.dom != document.body ) {
34864             this.el.on('resize', this.layout,this);
34865             this.el.on('show', this.layout,this);
34866         }
34867
34868     },
34869
34870     /**
34871      * Returns true if this layout is currently being updated
34872      * @return {Boolean}
34873      */
34874     isUpdating : function(){
34875         return this.updating;
34876     },
34877
34878     /**
34879      * Suspend the LayoutManager from doing auto-layouts while
34880      * making multiple add or remove calls
34881      */
34882     beginUpdate : function(){
34883         this.updating = true;
34884     },
34885
34886     /**
34887      * Restore auto-layouts and optionally disable the manager from performing a layout
34888      * @param {Boolean} noLayout true to disable a layout update
34889      */
34890     endUpdate : function(noLayout){
34891         this.updating = false;
34892         if(!noLayout){
34893             this.layout();
34894         }
34895     },
34896
34897     layout: function(){
34898         // abstract...
34899     },
34900
34901     onRegionResized : function(region, newSize){
34902         this.fireEvent("regionresized", region, newSize);
34903         this.layout();
34904     },
34905
34906     onRegionCollapsed : function(region){
34907         this.fireEvent("regioncollapsed", region);
34908     },
34909
34910     onRegionExpanded : function(region){
34911         this.fireEvent("regionexpanded", region);
34912     },
34913
34914     /**
34915      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34916      * performs box-model adjustments.
34917      * @return {Object} The size as an object {width: (the width), height: (the height)}
34918      */
34919     getViewSize : function()
34920     {
34921         var size;
34922         if(this.el.dom != document.body){
34923             size = this.el.getSize();
34924         }else{
34925             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34926         }
34927         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34928         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34929         return size;
34930     },
34931
34932     /**
34933      * Returns the Element this layout is bound to.
34934      * @return {Roo.Element}
34935      */
34936     getEl : function(){
34937         return this.el;
34938     },
34939
34940     /**
34941      * Returns the specified region.
34942      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34943      * @return {Roo.LayoutRegion}
34944      */
34945     getRegion : function(target){
34946         return this.regions[target.toLowerCase()];
34947     },
34948
34949     onWindowResize : function(){
34950         if(this.monitorWindowResize){
34951             this.layout();
34952         }
34953     }
34954 });
34955 /*
34956  * Based on:
34957  * Ext JS Library 1.1.1
34958  * Copyright(c) 2006-2007, Ext JS, LLC.
34959  *
34960  * Originally Released Under LGPL - original licence link has changed is not relivant.
34961  *
34962  * Fork - LGPL
34963  * <script type="text/javascript">
34964  */
34965 /**
34966  * @class Roo.bootstrap.layout.Border
34967  * @extends Roo.bootstrap.layout.Manager
34968  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34969  * please see: examples/bootstrap/nested.html<br><br>
34970  
34971 <b>The container the layout is rendered into can be either the body element or any other element.
34972 If it is not the body element, the container needs to either be an absolute positioned element,
34973 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34974 the container size if it is not the body element.</b>
34975
34976 * @constructor
34977 * Create a new Border
34978 * @param {Object} config Configuration options
34979  */
34980 Roo.bootstrap.layout.Border = function(config){
34981     config = config || {};
34982     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34983     
34984     
34985     
34986     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34987         if(config[region]){
34988             config[region].region = region;
34989             this.addRegion(config[region]);
34990         }
34991     },this);
34992     
34993 };
34994
34995 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34996
34997 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34998     /**
34999      * Creates and adds a new region if it doesn't already exist.
35000      * @param {String} target The target region key (north, south, east, west or center).
35001      * @param {Object} config The regions config object
35002      * @return {BorderLayoutRegion} The new region
35003      */
35004     addRegion : function(config)
35005     {
35006         if(!this.regions[config.region]){
35007             var r = this.factory(config);
35008             this.bindRegion(r);
35009         }
35010         return this.regions[config.region];
35011     },
35012
35013     // private (kinda)
35014     bindRegion : function(r){
35015         this.regions[r.config.region] = r;
35016         
35017         r.on("visibilitychange",    this.layout, this);
35018         r.on("paneladded",          this.layout, this);
35019         r.on("panelremoved",        this.layout, this);
35020         r.on("invalidated",         this.layout, this);
35021         r.on("resized",             this.onRegionResized, this);
35022         r.on("collapsed",           this.onRegionCollapsed, this);
35023         r.on("expanded",            this.onRegionExpanded, this);
35024     },
35025
35026     /**
35027      * Performs a layout update.
35028      */
35029     layout : function()
35030     {
35031         if(this.updating) {
35032             return;
35033         }
35034         
35035         // render all the rebions if they have not been done alreayd?
35036         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35037             if(this.regions[region] && !this.regions[region].bodyEl){
35038                 this.regions[region].onRender(this.el)
35039             }
35040         },this);
35041         
35042         var size = this.getViewSize();
35043         var w = size.width;
35044         var h = size.height;
35045         var centerW = w;
35046         var centerH = h;
35047         var centerY = 0;
35048         var centerX = 0;
35049         //var x = 0, y = 0;
35050
35051         var rs = this.regions;
35052         var north = rs["north"];
35053         var south = rs["south"]; 
35054         var west = rs["west"];
35055         var east = rs["east"];
35056         var center = rs["center"];
35057         //if(this.hideOnLayout){ // not supported anymore
35058             //c.el.setStyle("display", "none");
35059         //}
35060         if(north && north.isVisible()){
35061             var b = north.getBox();
35062             var m = north.getMargins();
35063             b.width = w - (m.left+m.right);
35064             b.x = m.left;
35065             b.y = m.top;
35066             centerY = b.height + b.y + m.bottom;
35067             centerH -= centerY;
35068             north.updateBox(this.safeBox(b));
35069         }
35070         if(south && south.isVisible()){
35071             var b = south.getBox();
35072             var m = south.getMargins();
35073             b.width = w - (m.left+m.right);
35074             b.x = m.left;
35075             var totalHeight = (b.height + m.top + m.bottom);
35076             b.y = h - totalHeight + m.top;
35077             centerH -= totalHeight;
35078             south.updateBox(this.safeBox(b));
35079         }
35080         if(west && west.isVisible()){
35081             var b = west.getBox();
35082             var m = west.getMargins();
35083             b.height = centerH - (m.top+m.bottom);
35084             b.x = m.left;
35085             b.y = centerY + m.top;
35086             var totalWidth = (b.width + m.left + m.right);
35087             centerX += totalWidth;
35088             centerW -= totalWidth;
35089             west.updateBox(this.safeBox(b));
35090         }
35091         if(east && east.isVisible()){
35092             var b = east.getBox();
35093             var m = east.getMargins();
35094             b.height = centerH - (m.top+m.bottom);
35095             var totalWidth = (b.width + m.left + m.right);
35096             b.x = w - totalWidth + m.left;
35097             b.y = centerY + m.top;
35098             centerW -= totalWidth;
35099             east.updateBox(this.safeBox(b));
35100         }
35101         if(center){
35102             var m = center.getMargins();
35103             var centerBox = {
35104                 x: centerX + m.left,
35105                 y: centerY + m.top,
35106                 width: centerW - (m.left+m.right),
35107                 height: centerH - (m.top+m.bottom)
35108             };
35109             //if(this.hideOnLayout){
35110                 //center.el.setStyle("display", "block");
35111             //}
35112             center.updateBox(this.safeBox(centerBox));
35113         }
35114         this.el.repaint();
35115         this.fireEvent("layout", this);
35116     },
35117
35118     // private
35119     safeBox : function(box){
35120         box.width = Math.max(0, box.width);
35121         box.height = Math.max(0, box.height);
35122         return box;
35123     },
35124
35125     /**
35126      * Adds a ContentPanel (or subclass) to this layout.
35127      * @param {String} target The target region key (north, south, east, west or center).
35128      * @param {Roo.ContentPanel} panel The panel to add
35129      * @return {Roo.ContentPanel} The added panel
35130      */
35131     add : function(target, panel){
35132          
35133         target = target.toLowerCase();
35134         return this.regions[target].add(panel);
35135     },
35136
35137     /**
35138      * Remove a ContentPanel (or subclass) to this layout.
35139      * @param {String} target The target region key (north, south, east, west or center).
35140      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35141      * @return {Roo.ContentPanel} The removed panel
35142      */
35143     remove : function(target, panel){
35144         target = target.toLowerCase();
35145         return this.regions[target].remove(panel);
35146     },
35147
35148     /**
35149      * Searches all regions for a panel with the specified id
35150      * @param {String} panelId
35151      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35152      */
35153     findPanel : function(panelId){
35154         var rs = this.regions;
35155         for(var target in rs){
35156             if(typeof rs[target] != "function"){
35157                 var p = rs[target].getPanel(panelId);
35158                 if(p){
35159                     return p;
35160                 }
35161             }
35162         }
35163         return null;
35164     },
35165
35166     /**
35167      * Searches all regions for a panel with the specified id and activates (shows) it.
35168      * @param {String/ContentPanel} panelId The panels id or the panel itself
35169      * @return {Roo.ContentPanel} The shown panel or null
35170      */
35171     showPanel : function(panelId) {
35172       var rs = this.regions;
35173       for(var target in rs){
35174          var r = rs[target];
35175          if(typeof r != "function"){
35176             if(r.hasPanel(panelId)){
35177                return r.showPanel(panelId);
35178             }
35179          }
35180       }
35181       return null;
35182    },
35183
35184    /**
35185      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35186      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35187      */
35188    /*
35189     restoreState : function(provider){
35190         if(!provider){
35191             provider = Roo.state.Manager;
35192         }
35193         var sm = new Roo.LayoutStateManager();
35194         sm.init(this, provider);
35195     },
35196 */
35197  
35198  
35199     /**
35200      * Adds a xtype elements to the layout.
35201      * <pre><code>
35202
35203 layout.addxtype({
35204        xtype : 'ContentPanel',
35205        region: 'west',
35206        items: [ .... ]
35207    }
35208 );
35209
35210 layout.addxtype({
35211         xtype : 'NestedLayoutPanel',
35212         region: 'west',
35213         layout: {
35214            center: { },
35215            west: { }   
35216         },
35217         items : [ ... list of content panels or nested layout panels.. ]
35218    }
35219 );
35220 </code></pre>
35221      * @param {Object} cfg Xtype definition of item to add.
35222      */
35223     addxtype : function(cfg)
35224     {
35225         // basically accepts a pannel...
35226         // can accept a layout region..!?!?
35227         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35228         
35229         
35230         // theory?  children can only be panels??
35231         
35232         //if (!cfg.xtype.match(/Panel$/)) {
35233         //    return false;
35234         //}
35235         var ret = false;
35236         
35237         if (typeof(cfg.region) == 'undefined') {
35238             Roo.log("Failed to add Panel, region was not set");
35239             Roo.log(cfg);
35240             return false;
35241         }
35242         var region = cfg.region;
35243         delete cfg.region;
35244         
35245           
35246         var xitems = [];
35247         if (cfg.items) {
35248             xitems = cfg.items;
35249             delete cfg.items;
35250         }
35251         var nb = false;
35252         
35253         switch(cfg.xtype) 
35254         {
35255             case 'Content':  // ContentPanel (el, cfg)
35256             case 'Scroll':  // ContentPanel (el, cfg)
35257             case 'View': 
35258                 cfg.autoCreate = true;
35259                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35260                 //} else {
35261                 //    var el = this.el.createChild();
35262                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35263                 //}
35264                 
35265                 this.add(region, ret);
35266                 break;
35267             
35268             /*
35269             case 'TreePanel': // our new panel!
35270                 cfg.el = this.el.createChild();
35271                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35272                 this.add(region, ret);
35273                 break;
35274             */
35275             
35276             case 'Nest': 
35277                 // create a new Layout (which is  a Border Layout...
35278                 
35279                 var clayout = cfg.layout;
35280                 clayout.el  = this.el.createChild();
35281                 clayout.items   = clayout.items  || [];
35282                 
35283                 delete cfg.layout;
35284                 
35285                 // replace this exitems with the clayout ones..
35286                 xitems = clayout.items;
35287                  
35288                 // force background off if it's in center...
35289                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35290                     cfg.background = false;
35291                 }
35292                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35293                 
35294                 
35295                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35296                 //console.log('adding nested layout panel '  + cfg.toSource());
35297                 this.add(region, ret);
35298                 nb = {}; /// find first...
35299                 break;
35300             
35301             case 'Grid':
35302                 
35303                 // needs grid and region
35304                 
35305                 //var el = this.getRegion(region).el.createChild();
35306                 /*
35307                  *var el = this.el.createChild();
35308                 // create the grid first...
35309                 cfg.grid.container = el;
35310                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35311                 */
35312                 
35313                 if (region == 'center' && this.active ) {
35314                     cfg.background = false;
35315                 }
35316                 
35317                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35318                 
35319                 this.add(region, ret);
35320                 /*
35321                 if (cfg.background) {
35322                     // render grid on panel activation (if panel background)
35323                     ret.on('activate', function(gp) {
35324                         if (!gp.grid.rendered) {
35325                     //        gp.grid.render(el);
35326                         }
35327                     });
35328                 } else {
35329                   //  cfg.grid.render(el);
35330                 }
35331                 */
35332                 break;
35333            
35334            
35335             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35336                 // it was the old xcomponent building that caused this before.
35337                 // espeically if border is the top element in the tree.
35338                 ret = this;
35339                 break; 
35340                 
35341                     
35342                 
35343                 
35344                 
35345             default:
35346                 /*
35347                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35348                     
35349                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35350                     this.add(region, ret);
35351                 } else {
35352                 */
35353                     Roo.log(cfg);
35354                     throw "Can not add '" + cfg.xtype + "' to Border";
35355                     return null;
35356              
35357                                 
35358              
35359         }
35360         this.beginUpdate();
35361         // add children..
35362         var region = '';
35363         var abn = {};
35364         Roo.each(xitems, function(i)  {
35365             region = nb && i.region ? i.region : false;
35366             
35367             var add = ret.addxtype(i);
35368            
35369             if (region) {
35370                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35371                 if (!i.background) {
35372                     abn[region] = nb[region] ;
35373                 }
35374             }
35375             
35376         });
35377         this.endUpdate();
35378
35379         // make the last non-background panel active..
35380         //if (nb) { Roo.log(abn); }
35381         if (nb) {
35382             
35383             for(var r in abn) {
35384                 region = this.getRegion(r);
35385                 if (region) {
35386                     // tried using nb[r], but it does not work..
35387                      
35388                     region.showPanel(abn[r]);
35389                    
35390                 }
35391             }
35392         }
35393         return ret;
35394         
35395     },
35396     
35397     
35398 // private
35399     factory : function(cfg)
35400     {
35401         
35402         var validRegions = Roo.bootstrap.layout.Border.regions;
35403
35404         var target = cfg.region;
35405         cfg.mgr = this;
35406         
35407         var r = Roo.bootstrap.layout;
35408         Roo.log(target);
35409         switch(target){
35410             case "north":
35411                 return new r.North(cfg);
35412             case "south":
35413                 return new r.South(cfg);
35414             case "east":
35415                 return new r.East(cfg);
35416             case "west":
35417                 return new r.West(cfg);
35418             case "center":
35419                 return new r.Center(cfg);
35420         }
35421         throw 'Layout region "'+target+'" not supported.';
35422     }
35423     
35424     
35425 });
35426  /*
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.Basic
35439  * @extends Roo.util.Observable
35440  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35441  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35442  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35443  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35444  * @cfg {string}   region  the region that it inhabits..
35445  * @cfg {bool}   skipConfig skip config?
35446  * 
35447
35448  */
35449 Roo.bootstrap.layout.Basic = function(config){
35450     
35451     this.mgr = config.mgr;
35452     
35453     this.position = config.region;
35454     
35455     var skipConfig = config.skipConfig;
35456     
35457     this.events = {
35458         /**
35459          * @scope Roo.BasicLayoutRegion
35460          */
35461         
35462         /**
35463          * @event beforeremove
35464          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35465          * @param {Roo.LayoutRegion} this
35466          * @param {Roo.ContentPanel} panel The panel
35467          * @param {Object} e The cancel event object
35468          */
35469         "beforeremove" : true,
35470         /**
35471          * @event invalidated
35472          * Fires when the layout for this region is changed.
35473          * @param {Roo.LayoutRegion} this
35474          */
35475         "invalidated" : true,
35476         /**
35477          * @event visibilitychange
35478          * Fires when this region is shown or hidden 
35479          * @param {Roo.LayoutRegion} this
35480          * @param {Boolean} visibility true or false
35481          */
35482         "visibilitychange" : true,
35483         /**
35484          * @event paneladded
35485          * Fires when a panel is added. 
35486          * @param {Roo.LayoutRegion} this
35487          * @param {Roo.ContentPanel} panel The panel
35488          */
35489         "paneladded" : true,
35490         /**
35491          * @event panelremoved
35492          * Fires when a panel is removed. 
35493          * @param {Roo.LayoutRegion} this
35494          * @param {Roo.ContentPanel} panel The panel
35495          */
35496         "panelremoved" : true,
35497         /**
35498          * @event beforecollapse
35499          * Fires when this region before collapse.
35500          * @param {Roo.LayoutRegion} this
35501          */
35502         "beforecollapse" : true,
35503         /**
35504          * @event collapsed
35505          * Fires when this region is collapsed.
35506          * @param {Roo.LayoutRegion} this
35507          */
35508         "collapsed" : true,
35509         /**
35510          * @event expanded
35511          * Fires when this region is expanded.
35512          * @param {Roo.LayoutRegion} this
35513          */
35514         "expanded" : true,
35515         /**
35516          * @event slideshow
35517          * Fires when this region is slid into view.
35518          * @param {Roo.LayoutRegion} this
35519          */
35520         "slideshow" : true,
35521         /**
35522          * @event slidehide
35523          * Fires when this region slides out of view. 
35524          * @param {Roo.LayoutRegion} this
35525          */
35526         "slidehide" : true,
35527         /**
35528          * @event panelactivated
35529          * Fires when a panel is activated. 
35530          * @param {Roo.LayoutRegion} this
35531          * @param {Roo.ContentPanel} panel The activated panel
35532          */
35533         "panelactivated" : true,
35534         /**
35535          * @event resized
35536          * Fires when the user resizes this region. 
35537          * @param {Roo.LayoutRegion} this
35538          * @param {Number} newSize The new size (width for east/west, height for north/south)
35539          */
35540         "resized" : true
35541     };
35542     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35543     this.panels = new Roo.util.MixedCollection();
35544     this.panels.getKey = this.getPanelId.createDelegate(this);
35545     this.box = null;
35546     this.activePanel = null;
35547     // ensure listeners are added...
35548     
35549     if (config.listeners || config.events) {
35550         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35551             listeners : config.listeners || {},
35552             events : config.events || {}
35553         });
35554     }
35555     
35556     if(skipConfig !== true){
35557         this.applyConfig(config);
35558     }
35559 };
35560
35561 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35562 {
35563     getPanelId : function(p){
35564         return p.getId();
35565     },
35566     
35567     applyConfig : function(config){
35568         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35569         this.config = config;
35570         
35571     },
35572     
35573     /**
35574      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35575      * the width, for horizontal (north, south) the height.
35576      * @param {Number} newSize The new width or height
35577      */
35578     resizeTo : function(newSize){
35579         var el = this.el ? this.el :
35580                  (this.activePanel ? this.activePanel.getEl() : null);
35581         if(el){
35582             switch(this.position){
35583                 case "east":
35584                 case "west":
35585                     el.setWidth(newSize);
35586                     this.fireEvent("resized", this, newSize);
35587                 break;
35588                 case "north":
35589                 case "south":
35590                     el.setHeight(newSize);
35591                     this.fireEvent("resized", this, newSize);
35592                 break;                
35593             }
35594         }
35595     },
35596     
35597     getBox : function(){
35598         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35599     },
35600     
35601     getMargins : function(){
35602         return this.margins;
35603     },
35604     
35605     updateBox : function(box){
35606         this.box = box;
35607         var el = this.activePanel.getEl();
35608         el.dom.style.left = box.x + "px";
35609         el.dom.style.top = box.y + "px";
35610         this.activePanel.setSize(box.width, box.height);
35611     },
35612     
35613     /**
35614      * Returns the container element for this region.
35615      * @return {Roo.Element}
35616      */
35617     getEl : function(){
35618         return this.activePanel;
35619     },
35620     
35621     /**
35622      * Returns true if this region is currently visible.
35623      * @return {Boolean}
35624      */
35625     isVisible : function(){
35626         return this.activePanel ? true : false;
35627     },
35628     
35629     setActivePanel : function(panel){
35630         panel = this.getPanel(panel);
35631         if(this.activePanel && this.activePanel != panel){
35632             this.activePanel.setActiveState(false);
35633             this.activePanel.getEl().setLeftTop(-10000,-10000);
35634         }
35635         this.activePanel = panel;
35636         panel.setActiveState(true);
35637         if(this.box){
35638             panel.setSize(this.box.width, this.box.height);
35639         }
35640         this.fireEvent("panelactivated", this, panel);
35641         this.fireEvent("invalidated");
35642     },
35643     
35644     /**
35645      * Show the specified panel.
35646      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35647      * @return {Roo.ContentPanel} The shown panel or null
35648      */
35649     showPanel : function(panel){
35650         panel = this.getPanel(panel);
35651         if(panel){
35652             this.setActivePanel(panel);
35653         }
35654         return panel;
35655     },
35656     
35657     /**
35658      * Get the active panel for this region.
35659      * @return {Roo.ContentPanel} The active panel or null
35660      */
35661     getActivePanel : function(){
35662         return this.activePanel;
35663     },
35664     
35665     /**
35666      * Add the passed ContentPanel(s)
35667      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35668      * @return {Roo.ContentPanel} The panel added (if only one was added)
35669      */
35670     add : function(panel){
35671         if(arguments.length > 1){
35672             for(var i = 0, len = arguments.length; i < len; i++) {
35673                 this.add(arguments[i]);
35674             }
35675             return null;
35676         }
35677         if(this.hasPanel(panel)){
35678             this.showPanel(panel);
35679             return panel;
35680         }
35681         var el = panel.getEl();
35682         if(el.dom.parentNode != this.mgr.el.dom){
35683             this.mgr.el.dom.appendChild(el.dom);
35684         }
35685         if(panel.setRegion){
35686             panel.setRegion(this);
35687         }
35688         this.panels.add(panel);
35689         el.setStyle("position", "absolute");
35690         if(!panel.background){
35691             this.setActivePanel(panel);
35692             if(this.config.initialSize && this.panels.getCount()==1){
35693                 this.resizeTo(this.config.initialSize);
35694             }
35695         }
35696         this.fireEvent("paneladded", this, panel);
35697         return panel;
35698     },
35699     
35700     /**
35701      * Returns true if the panel is in this region.
35702      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35703      * @return {Boolean}
35704      */
35705     hasPanel : function(panel){
35706         if(typeof panel == "object"){ // must be panel obj
35707             panel = panel.getId();
35708         }
35709         return this.getPanel(panel) ? true : false;
35710     },
35711     
35712     /**
35713      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35714      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35715      * @param {Boolean} preservePanel Overrides the config preservePanel option
35716      * @return {Roo.ContentPanel} The panel that was removed
35717      */
35718     remove : function(panel, preservePanel){
35719         panel = this.getPanel(panel);
35720         if(!panel){
35721             return null;
35722         }
35723         var e = {};
35724         this.fireEvent("beforeremove", this, panel, e);
35725         if(e.cancel === true){
35726             return null;
35727         }
35728         var panelId = panel.getId();
35729         this.panels.removeKey(panelId);
35730         return panel;
35731     },
35732     
35733     /**
35734      * Returns the panel specified or null if it's not in this region.
35735      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35736      * @return {Roo.ContentPanel}
35737      */
35738     getPanel : function(id){
35739         if(typeof id == "object"){ // must be panel obj
35740             return id;
35741         }
35742         return this.panels.get(id);
35743     },
35744     
35745     /**
35746      * Returns this regions position (north/south/east/west/center).
35747      * @return {String} 
35748      */
35749     getPosition: function(){
35750         return this.position;    
35751     }
35752 });/*
35753  * Based on:
35754  * Ext JS Library 1.1.1
35755  * Copyright(c) 2006-2007, Ext JS, LLC.
35756  *
35757  * Originally Released Under LGPL - original licence link has changed is not relivant.
35758  *
35759  * Fork - LGPL
35760  * <script type="text/javascript">
35761  */
35762  
35763 /**
35764  * @class Roo.bootstrap.layout.Region
35765  * @extends Roo.bootstrap.layout.Basic
35766  * This class represents a region in a layout manager.
35767  
35768  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35769  * @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})
35770  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35771  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35772  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35773  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35774  * @cfg {String}    title           The title for the region (overrides panel titles)
35775  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35776  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35777  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35778  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35779  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35780  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35781  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35782  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35783  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35784  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35785
35786  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35787  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35788  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35789  * @cfg {Number}    width           For East/West panels
35790  * @cfg {Number}    height          For North/South panels
35791  * @cfg {Boolean}   split           To show the splitter
35792  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35793  * 
35794  * @cfg {string}   cls             Extra CSS classes to add to region
35795  * 
35796  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35797  * @cfg {string}   region  the region that it inhabits..
35798  *
35799
35800  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35801  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35802
35803  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35804  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35805  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35806  */
35807 Roo.bootstrap.layout.Region = function(config)
35808 {
35809     this.applyConfig(config);
35810
35811     var mgr = config.mgr;
35812     var pos = config.region;
35813     config.skipConfig = true;
35814     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35815     
35816     if (mgr.el) {
35817         this.onRender(mgr.el);   
35818     }
35819      
35820     this.visible = true;
35821     this.collapsed = false;
35822     this.unrendered_panels = [];
35823 };
35824
35825 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35826
35827     position: '', // set by wrapper (eg. north/south etc..)
35828     unrendered_panels : null,  // unrendered panels.
35829     createBody : function(){
35830         /** This region's body element 
35831         * @type Roo.Element */
35832         this.bodyEl = this.el.createChild({
35833                 tag: "div",
35834                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35835         });
35836     },
35837
35838     onRender: function(ctr, pos)
35839     {
35840         var dh = Roo.DomHelper;
35841         /** This region's container element 
35842         * @type Roo.Element */
35843         this.el = dh.append(ctr.dom, {
35844                 tag: "div",
35845                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35846             }, true);
35847         /** This region's title element 
35848         * @type Roo.Element */
35849     
35850         this.titleEl = dh.append(this.el.dom,
35851             {
35852                     tag: "div",
35853                     unselectable: "on",
35854                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35855                     children:[
35856                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35857                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35858                     ]}, true);
35859         
35860         this.titleEl.enableDisplayMode();
35861         /** This region's title text element 
35862         * @type HTMLElement */
35863         this.titleTextEl = this.titleEl.dom.firstChild;
35864         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35865         /*
35866         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35867         this.closeBtn.enableDisplayMode();
35868         this.closeBtn.on("click", this.closeClicked, this);
35869         this.closeBtn.hide();
35870     */
35871         this.createBody(this.config);
35872         if(this.config.hideWhenEmpty){
35873             this.hide();
35874             this.on("paneladded", this.validateVisibility, this);
35875             this.on("panelremoved", this.validateVisibility, this);
35876         }
35877         if(this.autoScroll){
35878             this.bodyEl.setStyle("overflow", "auto");
35879         }else{
35880             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35881         }
35882         //if(c.titlebar !== false){
35883             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35884                 this.titleEl.hide();
35885             }else{
35886                 this.titleEl.show();
35887                 if(this.config.title){
35888                     this.titleTextEl.innerHTML = this.config.title;
35889                 }
35890             }
35891         //}
35892         if(this.config.collapsed){
35893             this.collapse(true);
35894         }
35895         if(this.config.hidden){
35896             this.hide();
35897         }
35898         
35899         if (this.unrendered_panels && this.unrendered_panels.length) {
35900             for (var i =0;i< this.unrendered_panels.length; i++) {
35901                 this.add(this.unrendered_panels[i]);
35902             }
35903             this.unrendered_panels = null;
35904             
35905         }
35906         
35907     },
35908     
35909     applyConfig : function(c)
35910     {
35911         /*
35912          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35913             var dh = Roo.DomHelper;
35914             if(c.titlebar !== false){
35915                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35916                 this.collapseBtn.on("click", this.collapse, this);
35917                 this.collapseBtn.enableDisplayMode();
35918                 /*
35919                 if(c.showPin === true || this.showPin){
35920                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35921                     this.stickBtn.enableDisplayMode();
35922                     this.stickBtn.on("click", this.expand, this);
35923                     this.stickBtn.hide();
35924                 }
35925                 
35926             }
35927             */
35928             /** This region's collapsed element
35929             * @type Roo.Element */
35930             /*
35931              *
35932             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35933                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35934             ]}, true);
35935             
35936             if(c.floatable !== false){
35937                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35938                this.collapsedEl.on("click", this.collapseClick, this);
35939             }
35940
35941             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35942                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35943                    id: "message", unselectable: "on", style:{"float":"left"}});
35944                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35945              }
35946             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35947             this.expandBtn.on("click", this.expand, this);
35948             
35949         }
35950         
35951         if(this.collapseBtn){
35952             this.collapseBtn.setVisible(c.collapsible == true);
35953         }
35954         
35955         this.cmargins = c.cmargins || this.cmargins ||
35956                          (this.position == "west" || this.position == "east" ?
35957                              {top: 0, left: 2, right:2, bottom: 0} :
35958                              {top: 2, left: 0, right:0, bottom: 2});
35959         */
35960         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35961         
35962         
35963         this.bottomTabs = c.tabPosition != "top";
35964         
35965         this.autoScroll = c.autoScroll || false;
35966         
35967         
35968        
35969         
35970         this.duration = c.duration || .30;
35971         this.slideDuration = c.slideDuration || .45;
35972         this.config = c;
35973        
35974     },
35975     /**
35976      * Returns true if this region is currently visible.
35977      * @return {Boolean}
35978      */
35979     isVisible : function(){
35980         return this.visible;
35981     },
35982
35983     /**
35984      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35985      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35986      */
35987     //setCollapsedTitle : function(title){
35988     //    title = title || "&#160;";
35989      //   if(this.collapsedTitleTextEl){
35990       //      this.collapsedTitleTextEl.innerHTML = title;
35991        // }
35992     //},
35993
35994     getBox : function(){
35995         var b;
35996       //  if(!this.collapsed){
35997             b = this.el.getBox(false, true);
35998        // }else{
35999           //  b = this.collapsedEl.getBox(false, true);
36000         //}
36001         return b;
36002     },
36003
36004     getMargins : function(){
36005         return this.margins;
36006         //return this.collapsed ? this.cmargins : this.margins;
36007     },
36008 /*
36009     highlight : function(){
36010         this.el.addClass("x-layout-panel-dragover");
36011     },
36012
36013     unhighlight : function(){
36014         this.el.removeClass("x-layout-panel-dragover");
36015     },
36016 */
36017     updateBox : function(box)
36018     {
36019         if (!this.bodyEl) {
36020             return; // not rendered yet..
36021         }
36022         
36023         this.box = box;
36024         if(!this.collapsed){
36025             this.el.dom.style.left = box.x + "px";
36026             this.el.dom.style.top = box.y + "px";
36027             this.updateBody(box.width, box.height);
36028         }else{
36029             this.collapsedEl.dom.style.left = box.x + "px";
36030             this.collapsedEl.dom.style.top = box.y + "px";
36031             this.collapsedEl.setSize(box.width, box.height);
36032         }
36033         if(this.tabs){
36034             this.tabs.autoSizeTabs();
36035         }
36036     },
36037
36038     updateBody : function(w, h)
36039     {
36040         if(w !== null){
36041             this.el.setWidth(w);
36042             w -= this.el.getBorderWidth("rl");
36043             if(this.config.adjustments){
36044                 w += this.config.adjustments[0];
36045             }
36046         }
36047         if(h !== null && h > 0){
36048             this.el.setHeight(h);
36049             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36050             h -= this.el.getBorderWidth("tb");
36051             if(this.config.adjustments){
36052                 h += this.config.adjustments[1];
36053             }
36054             this.bodyEl.setHeight(h);
36055             if(this.tabs){
36056                 h = this.tabs.syncHeight(h);
36057             }
36058         }
36059         if(this.panelSize){
36060             w = w !== null ? w : this.panelSize.width;
36061             h = h !== null ? h : this.panelSize.height;
36062         }
36063         if(this.activePanel){
36064             var el = this.activePanel.getEl();
36065             w = w !== null ? w : el.getWidth();
36066             h = h !== null ? h : el.getHeight();
36067             this.panelSize = {width: w, height: h};
36068             this.activePanel.setSize(w, h);
36069         }
36070         if(Roo.isIE && this.tabs){
36071             this.tabs.el.repaint();
36072         }
36073     },
36074
36075     /**
36076      * Returns the container element for this region.
36077      * @return {Roo.Element}
36078      */
36079     getEl : function(){
36080         return this.el;
36081     },
36082
36083     /**
36084      * Hides this region.
36085      */
36086     hide : function(){
36087         //if(!this.collapsed){
36088             this.el.dom.style.left = "-2000px";
36089             this.el.hide();
36090         //}else{
36091          //   this.collapsedEl.dom.style.left = "-2000px";
36092          //   this.collapsedEl.hide();
36093        // }
36094         this.visible = false;
36095         this.fireEvent("visibilitychange", this, false);
36096     },
36097
36098     /**
36099      * Shows this region if it was previously hidden.
36100      */
36101     show : function(){
36102         //if(!this.collapsed){
36103             this.el.show();
36104         //}else{
36105         //    this.collapsedEl.show();
36106        // }
36107         this.visible = true;
36108         this.fireEvent("visibilitychange", this, true);
36109     },
36110 /*
36111     closeClicked : function(){
36112         if(this.activePanel){
36113             this.remove(this.activePanel);
36114         }
36115     },
36116
36117     collapseClick : function(e){
36118         if(this.isSlid){
36119            e.stopPropagation();
36120            this.slideIn();
36121         }else{
36122            e.stopPropagation();
36123            this.slideOut();
36124         }
36125     },
36126 */
36127     /**
36128      * Collapses this region.
36129      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36130      */
36131     /*
36132     collapse : function(skipAnim, skipCheck = false){
36133         if(this.collapsed) {
36134             return;
36135         }
36136         
36137         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36138             
36139             this.collapsed = true;
36140             if(this.split){
36141                 this.split.el.hide();
36142             }
36143             if(this.config.animate && skipAnim !== true){
36144                 this.fireEvent("invalidated", this);
36145                 this.animateCollapse();
36146             }else{
36147                 this.el.setLocation(-20000,-20000);
36148                 this.el.hide();
36149                 this.collapsedEl.show();
36150                 this.fireEvent("collapsed", this);
36151                 this.fireEvent("invalidated", this);
36152             }
36153         }
36154         
36155     },
36156 */
36157     animateCollapse : function(){
36158         // overridden
36159     },
36160
36161     /**
36162      * Expands this region if it was previously collapsed.
36163      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36164      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36165      */
36166     /*
36167     expand : function(e, skipAnim){
36168         if(e) {
36169             e.stopPropagation();
36170         }
36171         if(!this.collapsed || this.el.hasActiveFx()) {
36172             return;
36173         }
36174         if(this.isSlid){
36175             this.afterSlideIn();
36176             skipAnim = true;
36177         }
36178         this.collapsed = false;
36179         if(this.config.animate && skipAnim !== true){
36180             this.animateExpand();
36181         }else{
36182             this.el.show();
36183             if(this.split){
36184                 this.split.el.show();
36185             }
36186             this.collapsedEl.setLocation(-2000,-2000);
36187             this.collapsedEl.hide();
36188             this.fireEvent("invalidated", this);
36189             this.fireEvent("expanded", this);
36190         }
36191     },
36192 */
36193     animateExpand : function(){
36194         // overridden
36195     },
36196
36197     initTabs : function()
36198     {
36199         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36200         
36201         var ts = new Roo.bootstrap.panel.Tabs({
36202                 el: this.bodyEl.dom,
36203                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36204                 disableTooltips: this.config.disableTabTips,
36205                 toolbar : this.config.toolbar
36206             });
36207         
36208         if(this.config.hideTabs){
36209             ts.stripWrap.setDisplayed(false);
36210         }
36211         this.tabs = ts;
36212         ts.resizeTabs = this.config.resizeTabs === true;
36213         ts.minTabWidth = this.config.minTabWidth || 40;
36214         ts.maxTabWidth = this.config.maxTabWidth || 250;
36215         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36216         ts.monitorResize = false;
36217         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36218         ts.bodyEl.addClass('roo-layout-tabs-body');
36219         this.panels.each(this.initPanelAsTab, this);
36220     },
36221
36222     initPanelAsTab : function(panel){
36223         var ti = this.tabs.addTab(
36224             panel.getEl().id,
36225             panel.getTitle(),
36226             null,
36227             this.config.closeOnTab && panel.isClosable(),
36228             panel.tpl
36229         );
36230         if(panel.tabTip !== undefined){
36231             ti.setTooltip(panel.tabTip);
36232         }
36233         ti.on("activate", function(){
36234               this.setActivePanel(panel);
36235         }, this);
36236         
36237         if(this.config.closeOnTab){
36238             ti.on("beforeclose", function(t, e){
36239                 e.cancel = true;
36240                 this.remove(panel);
36241             }, this);
36242         }
36243         
36244         panel.tabItem = ti;
36245         
36246         return ti;
36247     },
36248
36249     updatePanelTitle : function(panel, title)
36250     {
36251         if(this.activePanel == panel){
36252             this.updateTitle(title);
36253         }
36254         if(this.tabs){
36255             var ti = this.tabs.getTab(panel.getEl().id);
36256             ti.setText(title);
36257             if(panel.tabTip !== undefined){
36258                 ti.setTooltip(panel.tabTip);
36259             }
36260         }
36261     },
36262
36263     updateTitle : function(title){
36264         if(this.titleTextEl && !this.config.title){
36265             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36266         }
36267     },
36268
36269     setActivePanel : function(panel)
36270     {
36271         panel = this.getPanel(panel);
36272         if(this.activePanel && this.activePanel != panel){
36273             if(this.activePanel.setActiveState(false) === false){
36274                 return;
36275             }
36276         }
36277         this.activePanel = panel;
36278         panel.setActiveState(true);
36279         if(this.panelSize){
36280             panel.setSize(this.panelSize.width, this.panelSize.height);
36281         }
36282         if(this.closeBtn){
36283             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36284         }
36285         this.updateTitle(panel.getTitle());
36286         if(this.tabs){
36287             this.fireEvent("invalidated", this);
36288         }
36289         this.fireEvent("panelactivated", this, panel);
36290     },
36291
36292     /**
36293      * Shows the specified panel.
36294      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36295      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36296      */
36297     showPanel : function(panel)
36298     {
36299         panel = this.getPanel(panel);
36300         if(panel){
36301             if(this.tabs){
36302                 var tab = this.tabs.getTab(panel.getEl().id);
36303                 if(tab.isHidden()){
36304                     this.tabs.unhideTab(tab.id);
36305                 }
36306                 tab.activate();
36307             }else{
36308                 this.setActivePanel(panel);
36309             }
36310         }
36311         return panel;
36312     },
36313
36314     /**
36315      * Get the active panel for this region.
36316      * @return {Roo.ContentPanel} The active panel or null
36317      */
36318     getActivePanel : function(){
36319         return this.activePanel;
36320     },
36321
36322     validateVisibility : function(){
36323         if(this.panels.getCount() < 1){
36324             this.updateTitle("&#160;");
36325             this.closeBtn.hide();
36326             this.hide();
36327         }else{
36328             if(!this.isVisible()){
36329                 this.show();
36330             }
36331         }
36332     },
36333
36334     /**
36335      * Adds the passed ContentPanel(s) to this region.
36336      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36337      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36338      */
36339     add : function(panel)
36340     {
36341         if(arguments.length > 1){
36342             for(var i = 0, len = arguments.length; i < len; i++) {
36343                 this.add(arguments[i]);
36344             }
36345             return null;
36346         }
36347         
36348         // if we have not been rendered yet, then we can not really do much of this..
36349         if (!this.bodyEl) {
36350             this.unrendered_panels.push(panel);
36351             return panel;
36352         }
36353         
36354         
36355         
36356         
36357         if(this.hasPanel(panel)){
36358             this.showPanel(panel);
36359             return panel;
36360         }
36361         panel.setRegion(this);
36362         this.panels.add(panel);
36363        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36364             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36365             // and hide them... ???
36366             this.bodyEl.dom.appendChild(panel.getEl().dom);
36367             if(panel.background !== true){
36368                 this.setActivePanel(panel);
36369             }
36370             this.fireEvent("paneladded", this, panel);
36371             return panel;
36372         }
36373         */
36374         if(!this.tabs){
36375             this.initTabs();
36376         }else{
36377             this.initPanelAsTab(panel);
36378         }
36379         
36380         
36381         if(panel.background !== true){
36382             this.tabs.activate(panel.getEl().id);
36383         }
36384         this.fireEvent("paneladded", this, panel);
36385         return panel;
36386     },
36387
36388     /**
36389      * Hides the tab for the specified panel.
36390      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36391      */
36392     hidePanel : function(panel){
36393         if(this.tabs && (panel = this.getPanel(panel))){
36394             this.tabs.hideTab(panel.getEl().id);
36395         }
36396     },
36397
36398     /**
36399      * Unhides the tab for a previously hidden panel.
36400      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36401      */
36402     unhidePanel : function(panel){
36403         if(this.tabs && (panel = this.getPanel(panel))){
36404             this.tabs.unhideTab(panel.getEl().id);
36405         }
36406     },
36407
36408     clearPanels : function(){
36409         while(this.panels.getCount() > 0){
36410              this.remove(this.panels.first());
36411         }
36412     },
36413
36414     /**
36415      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36416      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36417      * @param {Boolean} preservePanel Overrides the config preservePanel option
36418      * @return {Roo.ContentPanel} The panel that was removed
36419      */
36420     remove : function(panel, preservePanel)
36421     {
36422         panel = this.getPanel(panel);
36423         if(!panel){
36424             return null;
36425         }
36426         var e = {};
36427         this.fireEvent("beforeremove", this, panel, e);
36428         if(e.cancel === true){
36429             return null;
36430         }
36431         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36432         var panelId = panel.getId();
36433         this.panels.removeKey(panelId);
36434         if(preservePanel){
36435             document.body.appendChild(panel.getEl().dom);
36436         }
36437         if(this.tabs){
36438             this.tabs.removeTab(panel.getEl().id);
36439         }else if (!preservePanel){
36440             this.bodyEl.dom.removeChild(panel.getEl().dom);
36441         }
36442         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36443             var p = this.panels.first();
36444             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36445             tempEl.appendChild(p.getEl().dom);
36446             this.bodyEl.update("");
36447             this.bodyEl.dom.appendChild(p.getEl().dom);
36448             tempEl = null;
36449             this.updateTitle(p.getTitle());
36450             this.tabs = null;
36451             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36452             this.setActivePanel(p);
36453         }
36454         panel.setRegion(null);
36455         if(this.activePanel == panel){
36456             this.activePanel = null;
36457         }
36458         if(this.config.autoDestroy !== false && preservePanel !== true){
36459             try{panel.destroy();}catch(e){}
36460         }
36461         this.fireEvent("panelremoved", this, panel);
36462         return panel;
36463     },
36464
36465     /**
36466      * Returns the TabPanel component used by this region
36467      * @return {Roo.TabPanel}
36468      */
36469     getTabs : function(){
36470         return this.tabs;
36471     },
36472
36473     createTool : function(parentEl, className){
36474         var btn = Roo.DomHelper.append(parentEl, {
36475             tag: "div",
36476             cls: "x-layout-tools-button",
36477             children: [ {
36478                 tag: "div",
36479                 cls: "roo-layout-tools-button-inner " + className,
36480                 html: "&#160;"
36481             }]
36482         }, true);
36483         btn.addClassOnOver("roo-layout-tools-button-over");
36484         return btn;
36485     }
36486 });/*
36487  * Based on:
36488  * Ext JS Library 1.1.1
36489  * Copyright(c) 2006-2007, Ext JS, LLC.
36490  *
36491  * Originally Released Under LGPL - original licence link has changed is not relivant.
36492  *
36493  * Fork - LGPL
36494  * <script type="text/javascript">
36495  */
36496  
36497
36498
36499 /**
36500  * @class Roo.SplitLayoutRegion
36501  * @extends Roo.LayoutRegion
36502  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36503  */
36504 Roo.bootstrap.layout.Split = function(config){
36505     this.cursor = config.cursor;
36506     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36507 };
36508
36509 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36510 {
36511     splitTip : "Drag to resize.",
36512     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36513     useSplitTips : false,
36514
36515     applyConfig : function(config){
36516         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36517     },
36518     
36519     onRender : function(ctr,pos) {
36520         
36521         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36522         if(!this.config.split){
36523             return;
36524         }
36525         if(!this.split){
36526             
36527             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36528                             tag: "div",
36529                             id: this.el.id + "-split",
36530                             cls: "roo-layout-split roo-layout-split-"+this.position,
36531                             html: "&#160;"
36532             });
36533             /** The SplitBar for this region 
36534             * @type Roo.SplitBar */
36535             // does not exist yet...
36536             Roo.log([this.position, this.orientation]);
36537             
36538             this.split = new Roo.bootstrap.SplitBar({
36539                 dragElement : splitEl,
36540                 resizingElement: this.el,
36541                 orientation : this.orientation
36542             });
36543             
36544             this.split.on("moved", this.onSplitMove, this);
36545             this.split.useShim = this.config.useShim === true;
36546             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36547             if(this.useSplitTips){
36548                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36549             }
36550             //if(config.collapsible){
36551             //    this.split.el.on("dblclick", this.collapse,  this);
36552             //}
36553         }
36554         if(typeof this.config.minSize != "undefined"){
36555             this.split.minSize = this.config.minSize;
36556         }
36557         if(typeof this.config.maxSize != "undefined"){
36558             this.split.maxSize = this.config.maxSize;
36559         }
36560         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36561             this.hideSplitter();
36562         }
36563         
36564     },
36565
36566     getHMaxSize : function(){
36567          var cmax = this.config.maxSize || 10000;
36568          var center = this.mgr.getRegion("center");
36569          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36570     },
36571
36572     getVMaxSize : function(){
36573          var cmax = this.config.maxSize || 10000;
36574          var center = this.mgr.getRegion("center");
36575          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36576     },
36577
36578     onSplitMove : function(split, newSize){
36579         this.fireEvent("resized", this, newSize);
36580     },
36581     
36582     /** 
36583      * Returns the {@link Roo.SplitBar} for this region.
36584      * @return {Roo.SplitBar}
36585      */
36586     getSplitBar : function(){
36587         return this.split;
36588     },
36589     
36590     hide : function(){
36591         this.hideSplitter();
36592         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36593     },
36594
36595     hideSplitter : function(){
36596         if(this.split){
36597             this.split.el.setLocation(-2000,-2000);
36598             this.split.el.hide();
36599         }
36600     },
36601
36602     show : function(){
36603         if(this.split){
36604             this.split.el.show();
36605         }
36606         Roo.bootstrap.layout.Split.superclass.show.call(this);
36607     },
36608     
36609     beforeSlide: function(){
36610         if(Roo.isGecko){// firefox overflow auto bug workaround
36611             this.bodyEl.clip();
36612             if(this.tabs) {
36613                 this.tabs.bodyEl.clip();
36614             }
36615             if(this.activePanel){
36616                 this.activePanel.getEl().clip();
36617                 
36618                 if(this.activePanel.beforeSlide){
36619                     this.activePanel.beforeSlide();
36620                 }
36621             }
36622         }
36623     },
36624     
36625     afterSlide : function(){
36626         if(Roo.isGecko){// firefox overflow auto bug workaround
36627             this.bodyEl.unclip();
36628             if(this.tabs) {
36629                 this.tabs.bodyEl.unclip();
36630             }
36631             if(this.activePanel){
36632                 this.activePanel.getEl().unclip();
36633                 if(this.activePanel.afterSlide){
36634                     this.activePanel.afterSlide();
36635                 }
36636             }
36637         }
36638     },
36639
36640     initAutoHide : function(){
36641         if(this.autoHide !== false){
36642             if(!this.autoHideHd){
36643                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36644                 this.autoHideHd = {
36645                     "mouseout": function(e){
36646                         if(!e.within(this.el, true)){
36647                             st.delay(500);
36648                         }
36649                     },
36650                     "mouseover" : function(e){
36651                         st.cancel();
36652                     },
36653                     scope : this
36654                 };
36655             }
36656             this.el.on(this.autoHideHd);
36657         }
36658     },
36659
36660     clearAutoHide : function(){
36661         if(this.autoHide !== false){
36662             this.el.un("mouseout", this.autoHideHd.mouseout);
36663             this.el.un("mouseover", this.autoHideHd.mouseover);
36664         }
36665     },
36666
36667     clearMonitor : function(){
36668         Roo.get(document).un("click", this.slideInIf, this);
36669     },
36670
36671     // these names are backwards but not changed for compat
36672     slideOut : function(){
36673         if(this.isSlid || this.el.hasActiveFx()){
36674             return;
36675         }
36676         this.isSlid = true;
36677         if(this.collapseBtn){
36678             this.collapseBtn.hide();
36679         }
36680         this.closeBtnState = this.closeBtn.getStyle('display');
36681         this.closeBtn.hide();
36682         if(this.stickBtn){
36683             this.stickBtn.show();
36684         }
36685         this.el.show();
36686         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36687         this.beforeSlide();
36688         this.el.setStyle("z-index", 10001);
36689         this.el.slideIn(this.getSlideAnchor(), {
36690             callback: function(){
36691                 this.afterSlide();
36692                 this.initAutoHide();
36693                 Roo.get(document).on("click", this.slideInIf, this);
36694                 this.fireEvent("slideshow", this);
36695             },
36696             scope: this,
36697             block: true
36698         });
36699     },
36700
36701     afterSlideIn : function(){
36702         this.clearAutoHide();
36703         this.isSlid = false;
36704         this.clearMonitor();
36705         this.el.setStyle("z-index", "");
36706         if(this.collapseBtn){
36707             this.collapseBtn.show();
36708         }
36709         this.closeBtn.setStyle('display', this.closeBtnState);
36710         if(this.stickBtn){
36711             this.stickBtn.hide();
36712         }
36713         this.fireEvent("slidehide", this);
36714     },
36715
36716     slideIn : function(cb){
36717         if(!this.isSlid || this.el.hasActiveFx()){
36718             Roo.callback(cb);
36719             return;
36720         }
36721         this.isSlid = false;
36722         this.beforeSlide();
36723         this.el.slideOut(this.getSlideAnchor(), {
36724             callback: function(){
36725                 this.el.setLeftTop(-10000, -10000);
36726                 this.afterSlide();
36727                 this.afterSlideIn();
36728                 Roo.callback(cb);
36729             },
36730             scope: this,
36731             block: true
36732         });
36733     },
36734     
36735     slideInIf : function(e){
36736         if(!e.within(this.el)){
36737             this.slideIn();
36738         }
36739     },
36740
36741     animateCollapse : function(){
36742         this.beforeSlide();
36743         this.el.setStyle("z-index", 20000);
36744         var anchor = this.getSlideAnchor();
36745         this.el.slideOut(anchor, {
36746             callback : function(){
36747                 this.el.setStyle("z-index", "");
36748                 this.collapsedEl.slideIn(anchor, {duration:.3});
36749                 this.afterSlide();
36750                 this.el.setLocation(-10000,-10000);
36751                 this.el.hide();
36752                 this.fireEvent("collapsed", this);
36753             },
36754             scope: this,
36755             block: true
36756         });
36757     },
36758
36759     animateExpand : function(){
36760         this.beforeSlide();
36761         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36762         this.el.setStyle("z-index", 20000);
36763         this.collapsedEl.hide({
36764             duration:.1
36765         });
36766         this.el.slideIn(this.getSlideAnchor(), {
36767             callback : function(){
36768                 this.el.setStyle("z-index", "");
36769                 this.afterSlide();
36770                 if(this.split){
36771                     this.split.el.show();
36772                 }
36773                 this.fireEvent("invalidated", this);
36774                 this.fireEvent("expanded", this);
36775             },
36776             scope: this,
36777             block: true
36778         });
36779     },
36780
36781     anchors : {
36782         "west" : "left",
36783         "east" : "right",
36784         "north" : "top",
36785         "south" : "bottom"
36786     },
36787
36788     sanchors : {
36789         "west" : "l",
36790         "east" : "r",
36791         "north" : "t",
36792         "south" : "b"
36793     },
36794
36795     canchors : {
36796         "west" : "tl-tr",
36797         "east" : "tr-tl",
36798         "north" : "tl-bl",
36799         "south" : "bl-tl"
36800     },
36801
36802     getAnchor : function(){
36803         return this.anchors[this.position];
36804     },
36805
36806     getCollapseAnchor : function(){
36807         return this.canchors[this.position];
36808     },
36809
36810     getSlideAnchor : function(){
36811         return this.sanchors[this.position];
36812     },
36813
36814     getAlignAdj : function(){
36815         var cm = this.cmargins;
36816         switch(this.position){
36817             case "west":
36818                 return [0, 0];
36819             break;
36820             case "east":
36821                 return [0, 0];
36822             break;
36823             case "north":
36824                 return [0, 0];
36825             break;
36826             case "south":
36827                 return [0, 0];
36828             break;
36829         }
36830     },
36831
36832     getExpandAdj : function(){
36833         var c = this.collapsedEl, cm = this.cmargins;
36834         switch(this.position){
36835             case "west":
36836                 return [-(cm.right+c.getWidth()+cm.left), 0];
36837             break;
36838             case "east":
36839                 return [cm.right+c.getWidth()+cm.left, 0];
36840             break;
36841             case "north":
36842                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36843             break;
36844             case "south":
36845                 return [0, cm.top+cm.bottom+c.getHeight()];
36846             break;
36847         }
36848     }
36849 });/*
36850  * Based on:
36851  * Ext JS Library 1.1.1
36852  * Copyright(c) 2006-2007, Ext JS, LLC.
36853  *
36854  * Originally Released Under LGPL - original licence link has changed is not relivant.
36855  *
36856  * Fork - LGPL
36857  * <script type="text/javascript">
36858  */
36859 /*
36860  * These classes are private internal classes
36861  */
36862 Roo.bootstrap.layout.Center = function(config){
36863     config.region = "center";
36864     Roo.bootstrap.layout.Region.call(this, config);
36865     this.visible = true;
36866     this.minWidth = config.minWidth || 20;
36867     this.minHeight = config.minHeight || 20;
36868 };
36869
36870 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36871     hide : function(){
36872         // center panel can't be hidden
36873     },
36874     
36875     show : function(){
36876         // center panel can't be hidden
36877     },
36878     
36879     getMinWidth: function(){
36880         return this.minWidth;
36881     },
36882     
36883     getMinHeight: function(){
36884         return this.minHeight;
36885     }
36886 });
36887
36888
36889
36890
36891  
36892
36893
36894
36895
36896
36897 Roo.bootstrap.layout.North = function(config)
36898 {
36899     config.region = 'north';
36900     config.cursor = 'n-resize';
36901     
36902     Roo.bootstrap.layout.Split.call(this, config);
36903     
36904     
36905     if(this.split){
36906         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36907         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36908         this.split.el.addClass("roo-layout-split-v");
36909     }
36910     var size = config.initialSize || config.height;
36911     if(typeof size != "undefined"){
36912         this.el.setHeight(size);
36913     }
36914 };
36915 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36916 {
36917     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36918     
36919     
36920     
36921     getBox : function(){
36922         if(this.collapsed){
36923             return this.collapsedEl.getBox();
36924         }
36925         var box = this.el.getBox();
36926         if(this.split){
36927             box.height += this.split.el.getHeight();
36928         }
36929         return box;
36930     },
36931     
36932     updateBox : function(box){
36933         if(this.split && !this.collapsed){
36934             box.height -= this.split.el.getHeight();
36935             this.split.el.setLeft(box.x);
36936             this.split.el.setTop(box.y+box.height);
36937             this.split.el.setWidth(box.width);
36938         }
36939         if(this.collapsed){
36940             this.updateBody(box.width, null);
36941         }
36942         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36943     }
36944 });
36945
36946
36947
36948
36949
36950 Roo.bootstrap.layout.South = function(config){
36951     config.region = 'south';
36952     config.cursor = 's-resize';
36953     Roo.bootstrap.layout.Split.call(this, config);
36954     if(this.split){
36955         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36956         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36957         this.split.el.addClass("roo-layout-split-v");
36958     }
36959     var size = config.initialSize || config.height;
36960     if(typeof size != "undefined"){
36961         this.el.setHeight(size);
36962     }
36963 };
36964
36965 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36966     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36967     getBox : function(){
36968         if(this.collapsed){
36969             return this.collapsedEl.getBox();
36970         }
36971         var box = this.el.getBox();
36972         if(this.split){
36973             var sh = this.split.el.getHeight();
36974             box.height += sh;
36975             box.y -= sh;
36976         }
36977         return box;
36978     },
36979     
36980     updateBox : function(box){
36981         if(this.split && !this.collapsed){
36982             var sh = this.split.el.getHeight();
36983             box.height -= sh;
36984             box.y += sh;
36985             this.split.el.setLeft(box.x);
36986             this.split.el.setTop(box.y-sh);
36987             this.split.el.setWidth(box.width);
36988         }
36989         if(this.collapsed){
36990             this.updateBody(box.width, null);
36991         }
36992         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36993     }
36994 });
36995
36996 Roo.bootstrap.layout.East = function(config){
36997     config.region = "east";
36998     config.cursor = "e-resize";
36999     Roo.bootstrap.layout.Split.call(this, config);
37000     if(this.split){
37001         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37002         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37003         this.split.el.addClass("roo-layout-split-h");
37004     }
37005     var size = config.initialSize || config.width;
37006     if(typeof size != "undefined"){
37007         this.el.setWidth(size);
37008     }
37009 };
37010 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37011     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37012     getBox : function(){
37013         if(this.collapsed){
37014             return this.collapsedEl.getBox();
37015         }
37016         var box = this.el.getBox();
37017         if(this.split){
37018             var sw = this.split.el.getWidth();
37019             box.width += sw;
37020             box.x -= sw;
37021         }
37022         return box;
37023     },
37024
37025     updateBox : function(box){
37026         if(this.split && !this.collapsed){
37027             var sw = this.split.el.getWidth();
37028             box.width -= sw;
37029             this.split.el.setLeft(box.x);
37030             this.split.el.setTop(box.y);
37031             this.split.el.setHeight(box.height);
37032             box.x += sw;
37033         }
37034         if(this.collapsed){
37035             this.updateBody(null, box.height);
37036         }
37037         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37038     }
37039 });
37040
37041 Roo.bootstrap.layout.West = function(config){
37042     config.region = "west";
37043     config.cursor = "w-resize";
37044     
37045     Roo.bootstrap.layout.Split.call(this, config);
37046     if(this.split){
37047         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37048         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37049         this.split.el.addClass("roo-layout-split-h");
37050     }
37051     
37052 };
37053 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37054     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37055     
37056     onRender: function(ctr, pos)
37057     {
37058         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37059         var size = this.config.initialSize || this.config.width;
37060         if(typeof size != "undefined"){
37061             this.el.setWidth(size);
37062         }
37063     },
37064     
37065     getBox : function(){
37066         if(this.collapsed){
37067             return this.collapsedEl.getBox();
37068         }
37069         var box = this.el.getBox();
37070         if(this.split){
37071             box.width += this.split.el.getWidth();
37072         }
37073         return box;
37074     },
37075     
37076     updateBox : function(box){
37077         if(this.split && !this.collapsed){
37078             var sw = this.split.el.getWidth();
37079             box.width -= sw;
37080             this.split.el.setLeft(box.x+box.width);
37081             this.split.el.setTop(box.y);
37082             this.split.el.setHeight(box.height);
37083         }
37084         if(this.collapsed){
37085             this.updateBody(null, box.height);
37086         }
37087         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37088     }
37089 });
37090 Roo.namespace("Roo.bootstrap.panel");/*
37091  * Based on:
37092  * Ext JS Library 1.1.1
37093  * Copyright(c) 2006-2007, Ext JS, LLC.
37094  *
37095  * Originally Released Under LGPL - original licence link has changed is not relivant.
37096  *
37097  * Fork - LGPL
37098  * <script type="text/javascript">
37099  */
37100 /**
37101  * @class Roo.ContentPanel
37102  * @extends Roo.util.Observable
37103  * A basic ContentPanel element.
37104  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37105  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37106  * @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
37107  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37108  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37109  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37110  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37111  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37112  * @cfg {String} title          The title for this panel
37113  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37114  * @cfg {String} url            Calls {@link #setUrl} with this value
37115  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37116  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37117  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37118  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37119  * @cfg {Boolean} badges render the badges
37120
37121  * @constructor
37122  * Create a new ContentPanel.
37123  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37124  * @param {String/Object} config A string to set only the title or a config object
37125  * @param {String} content (optional) Set the HTML content for this panel
37126  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37127  */
37128 Roo.bootstrap.panel.Content = function( config){
37129     
37130     this.tpl = config.tpl || false;
37131     
37132     var el = config.el;
37133     var content = config.content;
37134
37135     if(config.autoCreate){ // xtype is available if this is called from factory
37136         el = Roo.id();
37137     }
37138     this.el = Roo.get(el);
37139     if(!this.el && config && config.autoCreate){
37140         if(typeof config.autoCreate == "object"){
37141             if(!config.autoCreate.id){
37142                 config.autoCreate.id = config.id||el;
37143             }
37144             this.el = Roo.DomHelper.append(document.body,
37145                         config.autoCreate, true);
37146         }else{
37147             var elcfg =  {   tag: "div",
37148                             cls: "roo-layout-inactive-content",
37149                             id: config.id||el
37150                             };
37151             if (config.html) {
37152                 elcfg.html = config.html;
37153                 
37154             }
37155                         
37156             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37157         }
37158     } 
37159     this.closable = false;
37160     this.loaded = false;
37161     this.active = false;
37162    
37163       
37164     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37165         
37166         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37167         
37168         this.wrapEl = this.el; //this.el.wrap();
37169         var ti = [];
37170         if (config.toolbar.items) {
37171             ti = config.toolbar.items ;
37172             delete config.toolbar.items ;
37173         }
37174         
37175         var nitems = [];
37176         this.toolbar.render(this.wrapEl, 'before');
37177         for(var i =0;i < ti.length;i++) {
37178           //  Roo.log(['add child', items[i]]);
37179             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37180         }
37181         this.toolbar.items = nitems;
37182         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37183         delete config.toolbar;
37184         
37185     }
37186     /*
37187     // xtype created footer. - not sure if will work as we normally have to render first..
37188     if (this.footer && !this.footer.el && this.footer.xtype) {
37189         if (!this.wrapEl) {
37190             this.wrapEl = this.el.wrap();
37191         }
37192     
37193         this.footer.container = this.wrapEl.createChild();
37194          
37195         this.footer = Roo.factory(this.footer, Roo);
37196         
37197     }
37198     */
37199     
37200      if(typeof config == "string"){
37201         this.title = config;
37202     }else{
37203         Roo.apply(this, config);
37204     }
37205     
37206     if(this.resizeEl){
37207         this.resizeEl = Roo.get(this.resizeEl, true);
37208     }else{
37209         this.resizeEl = this.el;
37210     }
37211     // handle view.xtype
37212     
37213  
37214     
37215     
37216     this.addEvents({
37217         /**
37218          * @event activate
37219          * Fires when this panel is activated. 
37220          * @param {Roo.ContentPanel} this
37221          */
37222         "activate" : true,
37223         /**
37224          * @event deactivate
37225          * Fires when this panel is activated. 
37226          * @param {Roo.ContentPanel} this
37227          */
37228         "deactivate" : true,
37229
37230         /**
37231          * @event resize
37232          * Fires when this panel is resized if fitToFrame is true.
37233          * @param {Roo.ContentPanel} this
37234          * @param {Number} width The width after any component adjustments
37235          * @param {Number} height The height after any component adjustments
37236          */
37237         "resize" : true,
37238         
37239          /**
37240          * @event render
37241          * Fires when this tab is created
37242          * @param {Roo.ContentPanel} this
37243          */
37244         "render" : true
37245         
37246         
37247         
37248     });
37249     
37250
37251     
37252     
37253     if(this.autoScroll){
37254         this.resizeEl.setStyle("overflow", "auto");
37255     } else {
37256         // fix randome scrolling
37257         //this.el.on('scroll', function() {
37258         //    Roo.log('fix random scolling');
37259         //    this.scrollTo('top',0); 
37260         //});
37261     }
37262     content = content || this.content;
37263     if(content){
37264         this.setContent(content);
37265     }
37266     if(config && config.url){
37267         this.setUrl(this.url, this.params, this.loadOnce);
37268     }
37269     
37270     
37271     
37272     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37273     
37274     if (this.view && typeof(this.view.xtype) != 'undefined') {
37275         this.view.el = this.el.appendChild(document.createElement("div"));
37276         this.view = Roo.factory(this.view); 
37277         this.view.render  &&  this.view.render(false, '');  
37278     }
37279     
37280     
37281     this.fireEvent('render', this);
37282 };
37283
37284 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37285     
37286     tabTip : '',
37287     
37288     setRegion : function(region){
37289         this.region = region;
37290         this.setActiveClass(region && !this.background);
37291     },
37292     
37293     
37294     setActiveClass: function(state)
37295     {
37296         if(state){
37297            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37298            this.el.setStyle('position','relative');
37299         }else{
37300            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37301            this.el.setStyle('position', 'absolute');
37302         } 
37303     },
37304     
37305     /**
37306      * Returns the toolbar for this Panel if one was configured. 
37307      * @return {Roo.Toolbar} 
37308      */
37309     getToolbar : function(){
37310         return this.toolbar;
37311     },
37312     
37313     setActiveState : function(active)
37314     {
37315         this.active = active;
37316         this.setActiveClass(active);
37317         if(!active){
37318             if(this.fireEvent("deactivate", this) === false){
37319                 return false;
37320             }
37321             return true;
37322         }
37323         this.fireEvent("activate", this);
37324         return true;
37325     },
37326     /**
37327      * Updates this panel's element
37328      * @param {String} content The new content
37329      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37330     */
37331     setContent : function(content, loadScripts){
37332         this.el.update(content, loadScripts);
37333     },
37334
37335     ignoreResize : function(w, h){
37336         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37337             return true;
37338         }else{
37339             this.lastSize = {width: w, height: h};
37340             return false;
37341         }
37342     },
37343     /**
37344      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37345      * @return {Roo.UpdateManager} The UpdateManager
37346      */
37347     getUpdateManager : function(){
37348         return this.el.getUpdateManager();
37349     },
37350      /**
37351      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37352      * @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:
37353 <pre><code>
37354 panel.load({
37355     url: "your-url.php",
37356     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37357     callback: yourFunction,
37358     scope: yourObject, //(optional scope)
37359     discardUrl: false,
37360     nocache: false,
37361     text: "Loading...",
37362     timeout: 30,
37363     scripts: false
37364 });
37365 </code></pre>
37366      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37367      * 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.
37368      * @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}
37369      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37370      * @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.
37371      * @return {Roo.ContentPanel} this
37372      */
37373     load : function(){
37374         var um = this.el.getUpdateManager();
37375         um.update.apply(um, arguments);
37376         return this;
37377     },
37378
37379
37380     /**
37381      * 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.
37382      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37383      * @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)
37384      * @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)
37385      * @return {Roo.UpdateManager} The UpdateManager
37386      */
37387     setUrl : function(url, params, loadOnce){
37388         if(this.refreshDelegate){
37389             this.removeListener("activate", this.refreshDelegate);
37390         }
37391         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37392         this.on("activate", this.refreshDelegate);
37393         return this.el.getUpdateManager();
37394     },
37395     
37396     _handleRefresh : function(url, params, loadOnce){
37397         if(!loadOnce || !this.loaded){
37398             var updater = this.el.getUpdateManager();
37399             updater.update(url, params, this._setLoaded.createDelegate(this));
37400         }
37401     },
37402     
37403     _setLoaded : function(){
37404         this.loaded = true;
37405     }, 
37406     
37407     /**
37408      * Returns this panel's id
37409      * @return {String} 
37410      */
37411     getId : function(){
37412         return this.el.id;
37413     },
37414     
37415     /** 
37416      * Returns this panel's element - used by regiosn to add.
37417      * @return {Roo.Element} 
37418      */
37419     getEl : function(){
37420         return this.wrapEl || this.el;
37421     },
37422     
37423    
37424     
37425     adjustForComponents : function(width, height)
37426     {
37427         //Roo.log('adjustForComponents ');
37428         if(this.resizeEl != this.el){
37429             width -= this.el.getFrameWidth('lr');
37430             height -= this.el.getFrameWidth('tb');
37431         }
37432         if(this.toolbar){
37433             var te = this.toolbar.getEl();
37434             te.setWidth(width);
37435             height -= te.getHeight();
37436         }
37437         if(this.footer){
37438             var te = this.footer.getEl();
37439             te.setWidth(width);
37440             height -= te.getHeight();
37441         }
37442         
37443         
37444         if(this.adjustments){
37445             width += this.adjustments[0];
37446             height += this.adjustments[1];
37447         }
37448         return {"width": width, "height": height};
37449     },
37450     
37451     setSize : function(width, height){
37452         if(this.fitToFrame && !this.ignoreResize(width, height)){
37453             if(this.fitContainer && this.resizeEl != this.el){
37454                 this.el.setSize(width, height);
37455             }
37456             var size = this.adjustForComponents(width, height);
37457             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37458             this.fireEvent('resize', this, size.width, size.height);
37459         }
37460     },
37461     
37462     /**
37463      * Returns this panel's title
37464      * @return {String} 
37465      */
37466     getTitle : function(){
37467         
37468         if (typeof(this.title) != 'object') {
37469             return this.title;
37470         }
37471         
37472         var t = '';
37473         for (var k in this.title) {
37474             if (!this.title.hasOwnProperty(k)) {
37475                 continue;
37476             }
37477             
37478             if (k.indexOf('-') >= 0) {
37479                 var s = k.split('-');
37480                 for (var i = 0; i<s.length; i++) {
37481                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37482                 }
37483             } else {
37484                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37485             }
37486         }
37487         return t;
37488     },
37489     
37490     /**
37491      * Set this panel's title
37492      * @param {String} title
37493      */
37494     setTitle : function(title){
37495         this.title = title;
37496         if(this.region){
37497             this.region.updatePanelTitle(this, title);
37498         }
37499     },
37500     
37501     /**
37502      * Returns true is this panel was configured to be closable
37503      * @return {Boolean} 
37504      */
37505     isClosable : function(){
37506         return this.closable;
37507     },
37508     
37509     beforeSlide : function(){
37510         this.el.clip();
37511         this.resizeEl.clip();
37512     },
37513     
37514     afterSlide : function(){
37515         this.el.unclip();
37516         this.resizeEl.unclip();
37517     },
37518     
37519     /**
37520      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37521      *   Will fail silently if the {@link #setUrl} method has not been called.
37522      *   This does not activate the panel, just updates its content.
37523      */
37524     refresh : function(){
37525         if(this.refreshDelegate){
37526            this.loaded = false;
37527            this.refreshDelegate();
37528         }
37529     },
37530     
37531     /**
37532      * Destroys this panel
37533      */
37534     destroy : function(){
37535         this.el.removeAllListeners();
37536         var tempEl = document.createElement("span");
37537         tempEl.appendChild(this.el.dom);
37538         tempEl.innerHTML = "";
37539         this.el.remove();
37540         this.el = null;
37541     },
37542     
37543     /**
37544      * form - if the content panel contains a form - this is a reference to it.
37545      * @type {Roo.form.Form}
37546      */
37547     form : false,
37548     /**
37549      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37550      *    This contains a reference to it.
37551      * @type {Roo.View}
37552      */
37553     view : false,
37554     
37555       /**
37556      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37557      * <pre><code>
37558
37559 layout.addxtype({
37560        xtype : 'Form',
37561        items: [ .... ]
37562    }
37563 );
37564
37565 </code></pre>
37566      * @param {Object} cfg Xtype definition of item to add.
37567      */
37568     
37569     
37570     getChildContainer: function () {
37571         return this.getEl();
37572     }
37573     
37574     
37575     /*
37576         var  ret = new Roo.factory(cfg);
37577         return ret;
37578         
37579         
37580         // add form..
37581         if (cfg.xtype.match(/^Form$/)) {
37582             
37583             var el;
37584             //if (this.footer) {
37585             //    el = this.footer.container.insertSibling(false, 'before');
37586             //} else {
37587                 el = this.el.createChild();
37588             //}
37589
37590             this.form = new  Roo.form.Form(cfg);
37591             
37592             
37593             if ( this.form.allItems.length) {
37594                 this.form.render(el.dom);
37595             }
37596             return this.form;
37597         }
37598         // should only have one of theses..
37599         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37600             // views.. should not be just added - used named prop 'view''
37601             
37602             cfg.el = this.el.appendChild(document.createElement("div"));
37603             // factory?
37604             
37605             var ret = new Roo.factory(cfg);
37606              
37607              ret.render && ret.render(false, ''); // render blank..
37608             this.view = ret;
37609             return ret;
37610         }
37611         return false;
37612     }
37613     \*/
37614 });
37615  
37616 /**
37617  * @class Roo.bootstrap.panel.Grid
37618  * @extends Roo.bootstrap.panel.Content
37619  * @constructor
37620  * Create a new GridPanel.
37621  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37622  * @param {Object} config A the config object
37623   
37624  */
37625
37626
37627
37628 Roo.bootstrap.panel.Grid = function(config)
37629 {
37630     
37631       
37632     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37633         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37634
37635     config.el = this.wrapper;
37636     //this.el = this.wrapper;
37637     
37638       if (config.container) {
37639         // ctor'ed from a Border/panel.grid
37640         
37641         
37642         this.wrapper.setStyle("overflow", "hidden");
37643         this.wrapper.addClass('roo-grid-container');
37644
37645     }
37646     
37647     
37648     if(config.toolbar){
37649         var tool_el = this.wrapper.createChild();    
37650         this.toolbar = Roo.factory(config.toolbar);
37651         var ti = [];
37652         if (config.toolbar.items) {
37653             ti = config.toolbar.items ;
37654             delete config.toolbar.items ;
37655         }
37656         
37657         var nitems = [];
37658         this.toolbar.render(tool_el);
37659         for(var i =0;i < ti.length;i++) {
37660           //  Roo.log(['add child', items[i]]);
37661             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37662         }
37663         this.toolbar.items = nitems;
37664         
37665         delete config.toolbar;
37666     }
37667     
37668     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37669     config.grid.scrollBody = true;;
37670     config.grid.monitorWindowResize = false; // turn off autosizing
37671     config.grid.autoHeight = false;
37672     config.grid.autoWidth = false;
37673     
37674     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37675     
37676     if (config.background) {
37677         // render grid on panel activation (if panel background)
37678         this.on('activate', function(gp) {
37679             if (!gp.grid.rendered) {
37680                 gp.grid.render(this.wrapper);
37681                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37682             }
37683         });
37684             
37685     } else {
37686         this.grid.render(this.wrapper);
37687         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37688
37689     }
37690     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37691     // ??? needed ??? config.el = this.wrapper;
37692     
37693     
37694     
37695   
37696     // xtype created footer. - not sure if will work as we normally have to render first..
37697     if (this.footer && !this.footer.el && this.footer.xtype) {
37698         
37699         var ctr = this.grid.getView().getFooterPanel(true);
37700         this.footer.dataSource = this.grid.dataSource;
37701         this.footer = Roo.factory(this.footer, Roo);
37702         this.footer.render(ctr);
37703         
37704     }
37705     
37706     
37707     
37708     
37709      
37710 };
37711
37712 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37713     getId : function(){
37714         return this.grid.id;
37715     },
37716     
37717     /**
37718      * Returns the grid for this panel
37719      * @return {Roo.bootstrap.Table} 
37720      */
37721     getGrid : function(){
37722         return this.grid;    
37723     },
37724     
37725     setSize : function(width, height){
37726         if(!this.ignoreResize(width, height)){
37727             var grid = this.grid;
37728             var size = this.adjustForComponents(width, height);
37729             var gridel = grid.getGridEl();
37730             gridel.setSize(size.width, size.height);
37731             /*
37732             var thd = grid.getGridEl().select('thead',true).first();
37733             var tbd = grid.getGridEl().select('tbody', true).first();
37734             if (tbd) {
37735                 tbd.setSize(width, height - thd.getHeight());
37736             }
37737             */
37738             grid.autoSize();
37739         }
37740     },
37741      
37742     
37743     
37744     beforeSlide : function(){
37745         this.grid.getView().scroller.clip();
37746     },
37747     
37748     afterSlide : function(){
37749         this.grid.getView().scroller.unclip();
37750     },
37751     
37752     destroy : function(){
37753         this.grid.destroy();
37754         delete this.grid;
37755         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37756     }
37757 });
37758
37759 /**
37760  * @class Roo.bootstrap.panel.Nest
37761  * @extends Roo.bootstrap.panel.Content
37762  * @constructor
37763  * Create a new Panel, that can contain a layout.Border.
37764  * 
37765  * 
37766  * @param {Roo.BorderLayout} layout The layout for this panel
37767  * @param {String/Object} config A string to set only the title or a config object
37768  */
37769 Roo.bootstrap.panel.Nest = function(config)
37770 {
37771     // construct with only one argument..
37772     /* FIXME - implement nicer consturctors
37773     if (layout.layout) {
37774         config = layout;
37775         layout = config.layout;
37776         delete config.layout;
37777     }
37778     if (layout.xtype && !layout.getEl) {
37779         // then layout needs constructing..
37780         layout = Roo.factory(layout, Roo);
37781     }
37782     */
37783     
37784     config.el =  config.layout.getEl();
37785     
37786     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37787     
37788     config.layout.monitorWindowResize = false; // turn off autosizing
37789     this.layout = config.layout;
37790     this.layout.getEl().addClass("roo-layout-nested-layout");
37791     
37792     
37793     
37794     
37795 };
37796
37797 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37798
37799     setSize : function(width, height){
37800         if(!this.ignoreResize(width, height)){
37801             var size = this.adjustForComponents(width, height);
37802             var el = this.layout.getEl();
37803             if (size.height < 1) {
37804                 el.setWidth(size.width);   
37805             } else {
37806                 el.setSize(size.width, size.height);
37807             }
37808             var touch = el.dom.offsetWidth;
37809             this.layout.layout();
37810             // ie requires a double layout on the first pass
37811             if(Roo.isIE && !this.initialized){
37812                 this.initialized = true;
37813                 this.layout.layout();
37814             }
37815         }
37816     },
37817     
37818     // activate all subpanels if not currently active..
37819     
37820     setActiveState : function(active){
37821         this.active = active;
37822         this.setActiveClass(active);
37823         
37824         if(!active){
37825             this.fireEvent("deactivate", this);
37826             return;
37827         }
37828         
37829         this.fireEvent("activate", this);
37830         // not sure if this should happen before or after..
37831         if (!this.layout) {
37832             return; // should not happen..
37833         }
37834         var reg = false;
37835         for (var r in this.layout.regions) {
37836             reg = this.layout.getRegion(r);
37837             if (reg.getActivePanel()) {
37838                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37839                 reg.setActivePanel(reg.getActivePanel());
37840                 continue;
37841             }
37842             if (!reg.panels.length) {
37843                 continue;
37844             }
37845             reg.showPanel(reg.getPanel(0));
37846         }
37847         
37848         
37849         
37850         
37851     },
37852     
37853     /**
37854      * Returns the nested BorderLayout for this panel
37855      * @return {Roo.BorderLayout} 
37856      */
37857     getLayout : function(){
37858         return this.layout;
37859     },
37860     
37861      /**
37862      * Adds a xtype elements to the layout of the nested panel
37863      * <pre><code>
37864
37865 panel.addxtype({
37866        xtype : 'ContentPanel',
37867        region: 'west',
37868        items: [ .... ]
37869    }
37870 );
37871
37872 panel.addxtype({
37873         xtype : 'NestedLayoutPanel',
37874         region: 'west',
37875         layout: {
37876            center: { },
37877            west: { }   
37878         },
37879         items : [ ... list of content panels or nested layout panels.. ]
37880    }
37881 );
37882 </code></pre>
37883      * @param {Object} cfg Xtype definition of item to add.
37884      */
37885     addxtype : function(cfg) {
37886         return this.layout.addxtype(cfg);
37887     
37888     }
37889 });        /*
37890  * Based on:
37891  * Ext JS Library 1.1.1
37892  * Copyright(c) 2006-2007, Ext JS, LLC.
37893  *
37894  * Originally Released Under LGPL - original licence link has changed is not relivant.
37895  *
37896  * Fork - LGPL
37897  * <script type="text/javascript">
37898  */
37899 /**
37900  * @class Roo.TabPanel
37901  * @extends Roo.util.Observable
37902  * A lightweight tab container.
37903  * <br><br>
37904  * Usage:
37905  * <pre><code>
37906 // basic tabs 1, built from existing content
37907 var tabs = new Roo.TabPanel("tabs1");
37908 tabs.addTab("script", "View Script");
37909 tabs.addTab("markup", "View Markup");
37910 tabs.activate("script");
37911
37912 // more advanced tabs, built from javascript
37913 var jtabs = new Roo.TabPanel("jtabs");
37914 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37915
37916 // set up the UpdateManager
37917 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37918 var updater = tab2.getUpdateManager();
37919 updater.setDefaultUrl("ajax1.htm");
37920 tab2.on('activate', updater.refresh, updater, true);
37921
37922 // Use setUrl for Ajax loading
37923 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37924 tab3.setUrl("ajax2.htm", null, true);
37925
37926 // Disabled tab
37927 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37928 tab4.disable();
37929
37930 jtabs.activate("jtabs-1");
37931  * </code></pre>
37932  * @constructor
37933  * Create a new TabPanel.
37934  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37935  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37936  */
37937 Roo.bootstrap.panel.Tabs = function(config){
37938     /**
37939     * The container element for this TabPanel.
37940     * @type Roo.Element
37941     */
37942     this.el = Roo.get(config.el);
37943     delete config.el;
37944     if(config){
37945         if(typeof config == "boolean"){
37946             this.tabPosition = config ? "bottom" : "top";
37947         }else{
37948             Roo.apply(this, config);
37949         }
37950     }
37951     
37952     if(this.tabPosition == "bottom"){
37953         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37954         this.el.addClass("roo-tabs-bottom");
37955     }
37956     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37957     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37958     this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
37959     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37960     if(Roo.isIE){
37961         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37962     }
37963     if(this.tabPosition != "bottom"){
37964         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37965          * @type Roo.Element
37966          */
37967         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37968         this.el.addClass("roo-tabs-top");
37969     }
37970     this.items = [];
37971
37972     this.bodyEl.setStyle("position", "relative");
37973
37974     this.active = null;
37975     this.activateDelegate = this.activate.createDelegate(this);
37976
37977     this.addEvents({
37978         /**
37979          * @event tabchange
37980          * Fires when the active tab changes
37981          * @param {Roo.TabPanel} this
37982          * @param {Roo.TabPanelItem} activePanel The new active tab
37983          */
37984         "tabchange": true,
37985         /**
37986          * @event beforetabchange
37987          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37988          * @param {Roo.TabPanel} this
37989          * @param {Object} e Set cancel to true on this object to cancel the tab change
37990          * @param {Roo.TabPanelItem} tab The tab being changed to
37991          */
37992         "beforetabchange" : true
37993     });
37994
37995     Roo.EventManager.onWindowResize(this.onResize, this);
37996     this.cpad = this.el.getPadding("lr");
37997     this.hiddenCount = 0;
37998
37999
38000     // toolbar on the tabbar support...
38001     if (this.toolbar) {
38002         alert("no toolbar support yet");
38003         this.toolbar  = false;
38004         /*
38005         var tcfg = this.toolbar;
38006         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38007         this.toolbar = new Roo.Toolbar(tcfg);
38008         if (Roo.isSafari) {
38009             var tbl = tcfg.container.child('table', true);
38010             tbl.setAttribute('width', '100%');
38011         }
38012         */
38013         
38014     }
38015    
38016
38017
38018     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38019 };
38020
38021 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38022     /*
38023      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38024      */
38025     tabPosition : "top",
38026     /*
38027      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38028      */
38029     currentTabWidth : 0,
38030     /*
38031      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38032      */
38033     minTabWidth : 40,
38034     /*
38035      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38036      */
38037     maxTabWidth : 250,
38038     /*
38039      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38040      */
38041     preferredTabWidth : 175,
38042     /*
38043      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38044      */
38045     resizeTabs : false,
38046     /*
38047      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38048      */
38049     monitorResize : true,
38050     /*
38051      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38052      */
38053     toolbar : false,
38054
38055     /**
38056      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38057      * @param {String} id The id of the div to use <b>or create</b>
38058      * @param {String} text The text for the tab
38059      * @param {String} content (optional) Content to put in the TabPanelItem body
38060      * @param {Boolean} closable (optional) True to create a close icon on the tab
38061      * @return {Roo.TabPanelItem} The created TabPanelItem
38062      */
38063     addTab : function(id, text, content, closable, tpl)
38064     {
38065         var item = new Roo.bootstrap.panel.TabItem({
38066             panel: this,
38067             id : id,
38068             text : text,
38069             closable : closable,
38070             tpl : tpl
38071         });
38072         this.addTabItem(item);
38073         if(content){
38074             item.setContent(content);
38075         }
38076         return item;
38077     },
38078
38079     /**
38080      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38081      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38082      * @return {Roo.TabPanelItem}
38083      */
38084     getTab : function(id){
38085         return this.items[id];
38086     },
38087
38088     /**
38089      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38090      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38091      */
38092     hideTab : function(id){
38093         var t = this.items[id];
38094         if(!t.isHidden()){
38095            t.setHidden(true);
38096            this.hiddenCount++;
38097            this.autoSizeTabs();
38098         }
38099     },
38100
38101     /**
38102      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38103      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38104      */
38105     unhideTab : function(id){
38106         var t = this.items[id];
38107         if(t.isHidden()){
38108            t.setHidden(false);
38109            this.hiddenCount--;
38110            this.autoSizeTabs();
38111         }
38112     },
38113
38114     /**
38115      * Adds an existing {@link Roo.TabPanelItem}.
38116      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38117      */
38118     addTabItem : function(item)
38119     {
38120         this.items[item.id] = item;
38121         this.items.push(item);
38122         this.autoSizeTabs();
38123       //  if(this.resizeTabs){
38124     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38125   //         this.autoSizeTabs();
38126 //        }else{
38127 //            item.autoSize();
38128        // }
38129     },
38130
38131     /**
38132      * Removes a {@link Roo.TabPanelItem}.
38133      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38134      */
38135     removeTab : function(id){
38136         var items = this.items;
38137         var tab = items[id];
38138         if(!tab) { return; }
38139         var index = items.indexOf(tab);
38140         if(this.active == tab && items.length > 1){
38141             var newTab = this.getNextAvailable(index);
38142             if(newTab) {
38143                 newTab.activate();
38144             }
38145         }
38146         this.stripEl.dom.removeChild(tab.pnode.dom);
38147         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38148             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38149         }
38150         items.splice(index, 1);
38151         delete this.items[tab.id];
38152         tab.fireEvent("close", tab);
38153         tab.purgeListeners();
38154         this.autoSizeTabs();
38155     },
38156
38157     getNextAvailable : function(start){
38158         var items = this.items;
38159         var index = start;
38160         // look for a next tab that will slide over to
38161         // replace the one being removed
38162         while(index < items.length){
38163             var item = items[++index];
38164             if(item && !item.isHidden()){
38165                 return item;
38166             }
38167         }
38168         // if one isn't found select the previous tab (on the left)
38169         index = start;
38170         while(index >= 0){
38171             var item = items[--index];
38172             if(item && !item.isHidden()){
38173                 return item;
38174             }
38175         }
38176         return null;
38177     },
38178
38179     /**
38180      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38181      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38182      */
38183     disableTab : function(id){
38184         var tab = this.items[id];
38185         if(tab && this.active != tab){
38186             tab.disable();
38187         }
38188     },
38189
38190     /**
38191      * Enables a {@link Roo.TabPanelItem} that is disabled.
38192      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38193      */
38194     enableTab : function(id){
38195         var tab = this.items[id];
38196         tab.enable();
38197     },
38198
38199     /**
38200      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38201      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38202      * @return {Roo.TabPanelItem} The TabPanelItem.
38203      */
38204     activate : function(id)
38205     {
38206         var tab = this.items[id];
38207         if(!tab){
38208             return null;
38209         }
38210         if(tab == this.active || tab.disabled){
38211             return tab;
38212         }
38213         var e = {};
38214         this.fireEvent("beforetabchange", this, e, tab);
38215         if(e.cancel !== true && !tab.disabled){
38216             if(this.active){
38217                 this.active.hide();
38218             }
38219             this.active = this.items[id];
38220             this.active.show();
38221             this.fireEvent("tabchange", this, this.active);
38222         }
38223         return tab;
38224     },
38225
38226     /**
38227      * Gets the active {@link Roo.TabPanelItem}.
38228      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38229      */
38230     getActiveTab : function(){
38231         return this.active;
38232     },
38233
38234     /**
38235      * Updates the tab body element to fit the height of the container element
38236      * for overflow scrolling
38237      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38238      */
38239     syncHeight : function(targetHeight){
38240         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38241         var bm = this.bodyEl.getMargins();
38242         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38243         this.bodyEl.setHeight(newHeight);
38244         return newHeight;
38245     },
38246
38247     onResize : function(){
38248         if(this.monitorResize){
38249             this.autoSizeTabs();
38250         }
38251     },
38252
38253     /**
38254      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38255      */
38256     beginUpdate : function(){
38257         this.updating = true;
38258     },
38259
38260     /**
38261      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38262      */
38263     endUpdate : function(){
38264         this.updating = false;
38265         this.autoSizeTabs();
38266     },
38267
38268     /**
38269      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38270      */
38271     autoSizeTabs : function()
38272     {
38273         var count = this.items.length;
38274         var vcount = count - this.hiddenCount;
38275         
38276         if (vcount < 2) {
38277             this.stripEl.hide();
38278         } else {
38279             this.stripEl.show();
38280         }
38281         
38282         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38283             return;
38284         }
38285         
38286         
38287         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38288         var availWidth = Math.floor(w / vcount);
38289         var b = this.stripBody;
38290         if(b.getWidth() > w){
38291             var tabs = this.items;
38292             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38293             if(availWidth < this.minTabWidth){
38294                 /*if(!this.sleft){    // incomplete scrolling code
38295                     this.createScrollButtons();
38296                 }
38297                 this.showScroll();
38298                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38299             }
38300         }else{
38301             if(this.currentTabWidth < this.preferredTabWidth){
38302                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38303             }
38304         }
38305     },
38306
38307     /**
38308      * Returns the number of tabs in this TabPanel.
38309      * @return {Number}
38310      */
38311      getCount : function(){
38312          return this.items.length;
38313      },
38314
38315     /**
38316      * Resizes all the tabs to the passed width
38317      * @param {Number} The new width
38318      */
38319     setTabWidth : function(width){
38320         this.currentTabWidth = width;
38321         for(var i = 0, len = this.items.length; i < len; i++) {
38322                 if(!this.items[i].isHidden()) {
38323                 this.items[i].setWidth(width);
38324             }
38325         }
38326     },
38327
38328     /**
38329      * Destroys this TabPanel
38330      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38331      */
38332     destroy : function(removeEl){
38333         Roo.EventManager.removeResizeListener(this.onResize, this);
38334         for(var i = 0, len = this.items.length; i < len; i++){
38335             this.items[i].purgeListeners();
38336         }
38337         if(removeEl === true){
38338             this.el.update("");
38339             this.el.remove();
38340         }
38341     },
38342     
38343     createStrip : function(container)
38344     {
38345         var strip = document.createElement("nav");
38346         strip.className = Roo.bootstrap.version == 4 ?
38347             "navbar-light bg-light" : 
38348             "navbar navbar-default"; //"x-tabs-wrap";
38349         container.appendChild(strip);
38350         return strip;
38351     },
38352     
38353     createStripList : function(strip)
38354     {
38355         // div wrapper for retard IE
38356         // returns the "tr" element.
38357         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38358         //'<div class="x-tabs-strip-wrap">'+
38359           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38360           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38361         return strip.firstChild; //.firstChild.firstChild.firstChild;
38362     },
38363     createBody : function(container)
38364     {
38365         var body = document.createElement("div");
38366         Roo.id(body, "tab-body");
38367         //Roo.fly(body).addClass("x-tabs-body");
38368         Roo.fly(body).addClass("tab-content");
38369         container.appendChild(body);
38370         return body;
38371     },
38372     createItemBody :function(bodyEl, id){
38373         var body = Roo.getDom(id);
38374         if(!body){
38375             body = document.createElement("div");
38376             body.id = id;
38377         }
38378         //Roo.fly(body).addClass("x-tabs-item-body");
38379         Roo.fly(body).addClass("tab-pane");
38380          bodyEl.insertBefore(body, bodyEl.firstChild);
38381         return body;
38382     },
38383     /** @private */
38384     createStripElements :  function(stripEl, text, closable, tpl)
38385     {
38386         var td = document.createElement("li"); // was td..
38387         td.className = 'nav-item';
38388         
38389         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38390         
38391         
38392         stripEl.appendChild(td);
38393         /*if(closable){
38394             td.className = "x-tabs-closable";
38395             if(!this.closeTpl){
38396                 this.closeTpl = new Roo.Template(
38397                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38398                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38399                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38400                 );
38401             }
38402             var el = this.closeTpl.overwrite(td, {"text": text});
38403             var close = el.getElementsByTagName("div")[0];
38404             var inner = el.getElementsByTagName("em")[0];
38405             return {"el": el, "close": close, "inner": inner};
38406         } else {
38407         */
38408         // not sure what this is..
38409 //            if(!this.tabTpl){
38410                 //this.tabTpl = new Roo.Template(
38411                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38412                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38413                 //);
38414 //                this.tabTpl = new Roo.Template(
38415 //                   '<a href="#">' +
38416 //                   '<span unselectable="on"' +
38417 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38418 //                            ' >{text}</span></a>'
38419 //                );
38420 //                
38421 //            }
38422
38423
38424             var template = tpl || this.tabTpl || false;
38425             
38426             if(!template){
38427                 template =  new Roo.Template(
38428                         Roo.bootstrap.version == 4 ? 
38429                             (
38430                                 '<a class="nav-link" href="#" unselectable="on"' +
38431                                      (this.disableTooltips ? '' : ' title="{text}"') +
38432                                      ' >{text}</a>'
38433                             ) : (
38434                                 '<a class="nav-link" href="#">' +
38435                                 '<span unselectable="on"' +
38436                                          (this.disableTooltips ? '' : ' title="{text}"') +
38437                                     ' >{text}</span></a>'
38438                             )
38439                 );
38440             }
38441             
38442             switch (typeof(template)) {
38443                 case 'object' :
38444                     break;
38445                 case 'string' :
38446                     template = new Roo.Template(template);
38447                     break;
38448                 default :
38449                     break;
38450             }
38451             
38452             var el = template.overwrite(td, {"text": text});
38453             
38454             var inner = el.getElementsByTagName("span")[0];
38455             
38456             return {"el": el, "inner": inner};
38457             
38458     }
38459         
38460     
38461 });
38462
38463 /**
38464  * @class Roo.TabPanelItem
38465  * @extends Roo.util.Observable
38466  * Represents an individual item (tab plus body) in a TabPanel.
38467  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38468  * @param {String} id The id of this TabPanelItem
38469  * @param {String} text The text for the tab of this TabPanelItem
38470  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38471  */
38472 Roo.bootstrap.panel.TabItem = function(config){
38473     /**
38474      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38475      * @type Roo.TabPanel
38476      */
38477     this.tabPanel = config.panel;
38478     /**
38479      * The id for this TabPanelItem
38480      * @type String
38481      */
38482     this.id = config.id;
38483     /** @private */
38484     this.disabled = false;
38485     /** @private */
38486     this.text = config.text;
38487     /** @private */
38488     this.loaded = false;
38489     this.closable = config.closable;
38490
38491     /**
38492      * The body element for this TabPanelItem.
38493      * @type Roo.Element
38494      */
38495     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38496     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38497     this.bodyEl.setStyle("display", "block");
38498     this.bodyEl.setStyle("zoom", "1");
38499     //this.hideAction();
38500
38501     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38502     /** @private */
38503     this.el = Roo.get(els.el);
38504     this.inner = Roo.get(els.inner, true);
38505      this.textEl = Roo.bootstrap.version == 4 ?
38506         this.el : Roo.get(this.el.dom.firstChild, true);
38507
38508     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38509     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38510
38511     
38512 //    this.el.on("mousedown", this.onTabMouseDown, this);
38513     this.el.on("click", this.onTabClick, this);
38514     /** @private */
38515     if(config.closable){
38516         var c = Roo.get(els.close, true);
38517         c.dom.title = this.closeText;
38518         c.addClassOnOver("close-over");
38519         c.on("click", this.closeClick, this);
38520      }
38521
38522     this.addEvents({
38523          /**
38524          * @event activate
38525          * Fires when this tab becomes the active tab.
38526          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38527          * @param {Roo.TabPanelItem} this
38528          */
38529         "activate": true,
38530         /**
38531          * @event beforeclose
38532          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38533          * @param {Roo.TabPanelItem} this
38534          * @param {Object} e Set cancel to true on this object to cancel the close.
38535          */
38536         "beforeclose": true,
38537         /**
38538          * @event close
38539          * Fires when this tab is closed.
38540          * @param {Roo.TabPanelItem} this
38541          */
38542          "close": true,
38543         /**
38544          * @event deactivate
38545          * Fires when this tab is no longer the active tab.
38546          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38547          * @param {Roo.TabPanelItem} this
38548          */
38549          "deactivate" : true
38550     });
38551     this.hidden = false;
38552
38553     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38554 };
38555
38556 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38557            {
38558     purgeListeners : function(){
38559        Roo.util.Observable.prototype.purgeListeners.call(this);
38560        this.el.removeAllListeners();
38561     },
38562     /**
38563      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38564      */
38565     show : function(){
38566         this.status_node.addClass("active");
38567         this.showAction();
38568         if(Roo.isOpera){
38569             this.tabPanel.stripWrap.repaint();
38570         }
38571         this.fireEvent("activate", this.tabPanel, this);
38572     },
38573
38574     /**
38575      * Returns true if this tab is the active tab.
38576      * @return {Boolean}
38577      */
38578     isActive : function(){
38579         return this.tabPanel.getActiveTab() == this;
38580     },
38581
38582     /**
38583      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38584      */
38585     hide : function(){
38586         this.status_node.removeClass("active");
38587         this.hideAction();
38588         this.fireEvent("deactivate", this.tabPanel, this);
38589     },
38590
38591     hideAction : function(){
38592         this.bodyEl.hide();
38593         this.bodyEl.setStyle("position", "absolute");
38594         this.bodyEl.setLeft("-20000px");
38595         this.bodyEl.setTop("-20000px");
38596     },
38597
38598     showAction : function(){
38599         this.bodyEl.setStyle("position", "relative");
38600         this.bodyEl.setTop("");
38601         this.bodyEl.setLeft("");
38602         this.bodyEl.show();
38603     },
38604
38605     /**
38606      * Set the tooltip for the tab.
38607      * @param {String} tooltip The tab's tooltip
38608      */
38609     setTooltip : function(text){
38610         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38611             this.textEl.dom.qtip = text;
38612             this.textEl.dom.removeAttribute('title');
38613         }else{
38614             this.textEl.dom.title = text;
38615         }
38616     },
38617
38618     onTabClick : function(e){
38619         e.preventDefault();
38620         this.tabPanel.activate(this.id);
38621     },
38622
38623     onTabMouseDown : function(e){
38624         e.preventDefault();
38625         this.tabPanel.activate(this.id);
38626     },
38627 /*
38628     getWidth : function(){
38629         return this.inner.getWidth();
38630     },
38631
38632     setWidth : function(width){
38633         var iwidth = width - this.linode.getPadding("lr");
38634         this.inner.setWidth(iwidth);
38635         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38636         this.linode.setWidth(width);
38637     },
38638 */
38639     /**
38640      * Show or hide the tab
38641      * @param {Boolean} hidden True to hide or false to show.
38642      */
38643     setHidden : function(hidden){
38644         this.hidden = hidden;
38645         this.linode.setStyle("display", hidden ? "none" : "");
38646     },
38647
38648     /**
38649      * Returns true if this tab is "hidden"
38650      * @return {Boolean}
38651      */
38652     isHidden : function(){
38653         return this.hidden;
38654     },
38655
38656     /**
38657      * Returns the text for this tab
38658      * @return {String}
38659      */
38660     getText : function(){
38661         return this.text;
38662     },
38663     /*
38664     autoSize : function(){
38665         //this.el.beginMeasure();
38666         this.textEl.setWidth(1);
38667         /*
38668          *  #2804 [new] Tabs in Roojs
38669          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38670          */
38671         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38672         //this.el.endMeasure();
38673     //},
38674
38675     /**
38676      * Sets the text for the tab (Note: this also sets the tooltip text)
38677      * @param {String} text The tab's text and tooltip
38678      */
38679     setText : function(text){
38680         this.text = text;
38681         this.textEl.update(text);
38682         this.setTooltip(text);
38683         //if(!this.tabPanel.resizeTabs){
38684         //    this.autoSize();
38685         //}
38686     },
38687     /**
38688      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38689      */
38690     activate : function(){
38691         this.tabPanel.activate(this.id);
38692     },
38693
38694     /**
38695      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38696      */
38697     disable : function(){
38698         if(this.tabPanel.active != this){
38699             this.disabled = true;
38700             this.status_node.addClass("disabled");
38701         }
38702     },
38703
38704     /**
38705      * Enables this TabPanelItem if it was previously disabled.
38706      */
38707     enable : function(){
38708         this.disabled = false;
38709         this.status_node.removeClass("disabled");
38710     },
38711
38712     /**
38713      * Sets the content for this TabPanelItem.
38714      * @param {String} content The content
38715      * @param {Boolean} loadScripts true to look for and load scripts
38716      */
38717     setContent : function(content, loadScripts){
38718         this.bodyEl.update(content, loadScripts);
38719     },
38720
38721     /**
38722      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38723      * @return {Roo.UpdateManager} The UpdateManager
38724      */
38725     getUpdateManager : function(){
38726         return this.bodyEl.getUpdateManager();
38727     },
38728
38729     /**
38730      * Set a URL to be used to load the content for this TabPanelItem.
38731      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38732      * @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)
38733      * @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)
38734      * @return {Roo.UpdateManager} The UpdateManager
38735      */
38736     setUrl : function(url, params, loadOnce){
38737         if(this.refreshDelegate){
38738             this.un('activate', this.refreshDelegate);
38739         }
38740         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38741         this.on("activate", this.refreshDelegate);
38742         return this.bodyEl.getUpdateManager();
38743     },
38744
38745     /** @private */
38746     _handleRefresh : function(url, params, loadOnce){
38747         if(!loadOnce || !this.loaded){
38748             var updater = this.bodyEl.getUpdateManager();
38749             updater.update(url, params, this._setLoaded.createDelegate(this));
38750         }
38751     },
38752
38753     /**
38754      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38755      *   Will fail silently if the setUrl method has not been called.
38756      *   This does not activate the panel, just updates its content.
38757      */
38758     refresh : function(){
38759         if(this.refreshDelegate){
38760            this.loaded = false;
38761            this.refreshDelegate();
38762         }
38763     },
38764
38765     /** @private */
38766     _setLoaded : function(){
38767         this.loaded = true;
38768     },
38769
38770     /** @private */
38771     closeClick : function(e){
38772         var o = {};
38773         e.stopEvent();
38774         this.fireEvent("beforeclose", this, o);
38775         if(o.cancel !== true){
38776             this.tabPanel.removeTab(this.id);
38777         }
38778     },
38779     /**
38780      * The text displayed in the tooltip for the close icon.
38781      * @type String
38782      */
38783     closeText : "Close this tab"
38784 });
38785 /**
38786 *    This script refer to:
38787 *    Title: International Telephone Input
38788 *    Author: Jack O'Connor
38789 *    Code version:  v12.1.12
38790 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38791 **/
38792
38793 Roo.bootstrap.PhoneInputData = function() {
38794     var d = [
38795       [
38796         "Afghanistan (‫افغانستان‬‎)",
38797         "af",
38798         "93"
38799       ],
38800       [
38801         "Albania (Shqipëri)",
38802         "al",
38803         "355"
38804       ],
38805       [
38806         "Algeria (‫الجزائر‬‎)",
38807         "dz",
38808         "213"
38809       ],
38810       [
38811         "American Samoa",
38812         "as",
38813         "1684"
38814       ],
38815       [
38816         "Andorra",
38817         "ad",
38818         "376"
38819       ],
38820       [
38821         "Angola",
38822         "ao",
38823         "244"
38824       ],
38825       [
38826         "Anguilla",
38827         "ai",
38828         "1264"
38829       ],
38830       [
38831         "Antigua and Barbuda",
38832         "ag",
38833         "1268"
38834       ],
38835       [
38836         "Argentina",
38837         "ar",
38838         "54"
38839       ],
38840       [
38841         "Armenia (Հայաստան)",
38842         "am",
38843         "374"
38844       ],
38845       [
38846         "Aruba",
38847         "aw",
38848         "297"
38849       ],
38850       [
38851         "Australia",
38852         "au",
38853         "61",
38854         0
38855       ],
38856       [
38857         "Austria (Österreich)",
38858         "at",
38859         "43"
38860       ],
38861       [
38862         "Azerbaijan (Azərbaycan)",
38863         "az",
38864         "994"
38865       ],
38866       [
38867         "Bahamas",
38868         "bs",
38869         "1242"
38870       ],
38871       [
38872         "Bahrain (‫البحرين‬‎)",
38873         "bh",
38874         "973"
38875       ],
38876       [
38877         "Bangladesh (বাংলাদেশ)",
38878         "bd",
38879         "880"
38880       ],
38881       [
38882         "Barbados",
38883         "bb",
38884         "1246"
38885       ],
38886       [
38887         "Belarus (Беларусь)",
38888         "by",
38889         "375"
38890       ],
38891       [
38892         "Belgium (België)",
38893         "be",
38894         "32"
38895       ],
38896       [
38897         "Belize",
38898         "bz",
38899         "501"
38900       ],
38901       [
38902         "Benin (Bénin)",
38903         "bj",
38904         "229"
38905       ],
38906       [
38907         "Bermuda",
38908         "bm",
38909         "1441"
38910       ],
38911       [
38912         "Bhutan (འབྲུག)",
38913         "bt",
38914         "975"
38915       ],
38916       [
38917         "Bolivia",
38918         "bo",
38919         "591"
38920       ],
38921       [
38922         "Bosnia and Herzegovina (Босна и Херцеговина)",
38923         "ba",
38924         "387"
38925       ],
38926       [
38927         "Botswana",
38928         "bw",
38929         "267"
38930       ],
38931       [
38932         "Brazil (Brasil)",
38933         "br",
38934         "55"
38935       ],
38936       [
38937         "British Indian Ocean Territory",
38938         "io",
38939         "246"
38940       ],
38941       [
38942         "British Virgin Islands",
38943         "vg",
38944         "1284"
38945       ],
38946       [
38947         "Brunei",
38948         "bn",
38949         "673"
38950       ],
38951       [
38952         "Bulgaria (България)",
38953         "bg",
38954         "359"
38955       ],
38956       [
38957         "Burkina Faso",
38958         "bf",
38959         "226"
38960       ],
38961       [
38962         "Burundi (Uburundi)",
38963         "bi",
38964         "257"
38965       ],
38966       [
38967         "Cambodia (កម្ពុជា)",
38968         "kh",
38969         "855"
38970       ],
38971       [
38972         "Cameroon (Cameroun)",
38973         "cm",
38974         "237"
38975       ],
38976       [
38977         "Canada",
38978         "ca",
38979         "1",
38980         1,
38981         ["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"]
38982       ],
38983       [
38984         "Cape Verde (Kabu Verdi)",
38985         "cv",
38986         "238"
38987       ],
38988       [
38989         "Caribbean Netherlands",
38990         "bq",
38991         "599",
38992         1
38993       ],
38994       [
38995         "Cayman Islands",
38996         "ky",
38997         "1345"
38998       ],
38999       [
39000         "Central African Republic (République centrafricaine)",
39001         "cf",
39002         "236"
39003       ],
39004       [
39005         "Chad (Tchad)",
39006         "td",
39007         "235"
39008       ],
39009       [
39010         "Chile",
39011         "cl",
39012         "56"
39013       ],
39014       [
39015         "China (中国)",
39016         "cn",
39017         "86"
39018       ],
39019       [
39020         "Christmas Island",
39021         "cx",
39022         "61",
39023         2
39024       ],
39025       [
39026         "Cocos (Keeling) Islands",
39027         "cc",
39028         "61",
39029         1
39030       ],
39031       [
39032         "Colombia",
39033         "co",
39034         "57"
39035       ],
39036       [
39037         "Comoros (‫جزر القمر‬‎)",
39038         "km",
39039         "269"
39040       ],
39041       [
39042         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39043         "cd",
39044         "243"
39045       ],
39046       [
39047         "Congo (Republic) (Congo-Brazzaville)",
39048         "cg",
39049         "242"
39050       ],
39051       [
39052         "Cook Islands",
39053         "ck",
39054         "682"
39055       ],
39056       [
39057         "Costa Rica",
39058         "cr",
39059         "506"
39060       ],
39061       [
39062         "Côte d’Ivoire",
39063         "ci",
39064         "225"
39065       ],
39066       [
39067         "Croatia (Hrvatska)",
39068         "hr",
39069         "385"
39070       ],
39071       [
39072         "Cuba",
39073         "cu",
39074         "53"
39075       ],
39076       [
39077         "Curaçao",
39078         "cw",
39079         "599",
39080         0
39081       ],
39082       [
39083         "Cyprus (Κύπρος)",
39084         "cy",
39085         "357"
39086       ],
39087       [
39088         "Czech Republic (Česká republika)",
39089         "cz",
39090         "420"
39091       ],
39092       [
39093         "Denmark (Danmark)",
39094         "dk",
39095         "45"
39096       ],
39097       [
39098         "Djibouti",
39099         "dj",
39100         "253"
39101       ],
39102       [
39103         "Dominica",
39104         "dm",
39105         "1767"
39106       ],
39107       [
39108         "Dominican Republic (República Dominicana)",
39109         "do",
39110         "1",
39111         2,
39112         ["809", "829", "849"]
39113       ],
39114       [
39115         "Ecuador",
39116         "ec",
39117         "593"
39118       ],
39119       [
39120         "Egypt (‫مصر‬‎)",
39121         "eg",
39122         "20"
39123       ],
39124       [
39125         "El Salvador",
39126         "sv",
39127         "503"
39128       ],
39129       [
39130         "Equatorial Guinea (Guinea Ecuatorial)",
39131         "gq",
39132         "240"
39133       ],
39134       [
39135         "Eritrea",
39136         "er",
39137         "291"
39138       ],
39139       [
39140         "Estonia (Eesti)",
39141         "ee",
39142         "372"
39143       ],
39144       [
39145         "Ethiopia",
39146         "et",
39147         "251"
39148       ],
39149       [
39150         "Falkland Islands (Islas Malvinas)",
39151         "fk",
39152         "500"
39153       ],
39154       [
39155         "Faroe Islands (Føroyar)",
39156         "fo",
39157         "298"
39158       ],
39159       [
39160         "Fiji",
39161         "fj",
39162         "679"
39163       ],
39164       [
39165         "Finland (Suomi)",
39166         "fi",
39167         "358",
39168         0
39169       ],
39170       [
39171         "France",
39172         "fr",
39173         "33"
39174       ],
39175       [
39176         "French Guiana (Guyane française)",
39177         "gf",
39178         "594"
39179       ],
39180       [
39181         "French Polynesia (Polynésie française)",
39182         "pf",
39183         "689"
39184       ],
39185       [
39186         "Gabon",
39187         "ga",
39188         "241"
39189       ],
39190       [
39191         "Gambia",
39192         "gm",
39193         "220"
39194       ],
39195       [
39196         "Georgia (საქართველო)",
39197         "ge",
39198         "995"
39199       ],
39200       [
39201         "Germany (Deutschland)",
39202         "de",
39203         "49"
39204       ],
39205       [
39206         "Ghana (Gaana)",
39207         "gh",
39208         "233"
39209       ],
39210       [
39211         "Gibraltar",
39212         "gi",
39213         "350"
39214       ],
39215       [
39216         "Greece (Ελλάδα)",
39217         "gr",
39218         "30"
39219       ],
39220       [
39221         "Greenland (Kalaallit Nunaat)",
39222         "gl",
39223         "299"
39224       ],
39225       [
39226         "Grenada",
39227         "gd",
39228         "1473"
39229       ],
39230       [
39231         "Guadeloupe",
39232         "gp",
39233         "590",
39234         0
39235       ],
39236       [
39237         "Guam",
39238         "gu",
39239         "1671"
39240       ],
39241       [
39242         "Guatemala",
39243         "gt",
39244         "502"
39245       ],
39246       [
39247         "Guernsey",
39248         "gg",
39249         "44",
39250         1
39251       ],
39252       [
39253         "Guinea (Guinée)",
39254         "gn",
39255         "224"
39256       ],
39257       [
39258         "Guinea-Bissau (Guiné Bissau)",
39259         "gw",
39260         "245"
39261       ],
39262       [
39263         "Guyana",
39264         "gy",
39265         "592"
39266       ],
39267       [
39268         "Haiti",
39269         "ht",
39270         "509"
39271       ],
39272       [
39273         "Honduras",
39274         "hn",
39275         "504"
39276       ],
39277       [
39278         "Hong Kong (香港)",
39279         "hk",
39280         "852"
39281       ],
39282       [
39283         "Hungary (Magyarország)",
39284         "hu",
39285         "36"
39286       ],
39287       [
39288         "Iceland (Ísland)",
39289         "is",
39290         "354"
39291       ],
39292       [
39293         "India (भारत)",
39294         "in",
39295         "91"
39296       ],
39297       [
39298         "Indonesia",
39299         "id",
39300         "62"
39301       ],
39302       [
39303         "Iran (‫ایران‬‎)",
39304         "ir",
39305         "98"
39306       ],
39307       [
39308         "Iraq (‫العراق‬‎)",
39309         "iq",
39310         "964"
39311       ],
39312       [
39313         "Ireland",
39314         "ie",
39315         "353"
39316       ],
39317       [
39318         "Isle of Man",
39319         "im",
39320         "44",
39321         2
39322       ],
39323       [
39324         "Israel (‫ישראל‬‎)",
39325         "il",
39326         "972"
39327       ],
39328       [
39329         "Italy (Italia)",
39330         "it",
39331         "39",
39332         0
39333       ],
39334       [
39335         "Jamaica",
39336         "jm",
39337         "1876"
39338       ],
39339       [
39340         "Japan (日本)",
39341         "jp",
39342         "81"
39343       ],
39344       [
39345         "Jersey",
39346         "je",
39347         "44",
39348         3
39349       ],
39350       [
39351         "Jordan (‫الأردن‬‎)",
39352         "jo",
39353         "962"
39354       ],
39355       [
39356         "Kazakhstan (Казахстан)",
39357         "kz",
39358         "7",
39359         1
39360       ],
39361       [
39362         "Kenya",
39363         "ke",
39364         "254"
39365       ],
39366       [
39367         "Kiribati",
39368         "ki",
39369         "686"
39370       ],
39371       [
39372         "Kosovo",
39373         "xk",
39374         "383"
39375       ],
39376       [
39377         "Kuwait (‫الكويت‬‎)",
39378         "kw",
39379         "965"
39380       ],
39381       [
39382         "Kyrgyzstan (Кыргызстан)",
39383         "kg",
39384         "996"
39385       ],
39386       [
39387         "Laos (ລາວ)",
39388         "la",
39389         "856"
39390       ],
39391       [
39392         "Latvia (Latvija)",
39393         "lv",
39394         "371"
39395       ],
39396       [
39397         "Lebanon (‫لبنان‬‎)",
39398         "lb",
39399         "961"
39400       ],
39401       [
39402         "Lesotho",
39403         "ls",
39404         "266"
39405       ],
39406       [
39407         "Liberia",
39408         "lr",
39409         "231"
39410       ],
39411       [
39412         "Libya (‫ليبيا‬‎)",
39413         "ly",
39414         "218"
39415       ],
39416       [
39417         "Liechtenstein",
39418         "li",
39419         "423"
39420       ],
39421       [
39422         "Lithuania (Lietuva)",
39423         "lt",
39424         "370"
39425       ],
39426       [
39427         "Luxembourg",
39428         "lu",
39429         "352"
39430       ],
39431       [
39432         "Macau (澳門)",
39433         "mo",
39434         "853"
39435       ],
39436       [
39437         "Macedonia (FYROM) (Македонија)",
39438         "mk",
39439         "389"
39440       ],
39441       [
39442         "Madagascar (Madagasikara)",
39443         "mg",
39444         "261"
39445       ],
39446       [
39447         "Malawi",
39448         "mw",
39449         "265"
39450       ],
39451       [
39452         "Malaysia",
39453         "my",
39454         "60"
39455       ],
39456       [
39457         "Maldives",
39458         "mv",
39459         "960"
39460       ],
39461       [
39462         "Mali",
39463         "ml",
39464         "223"
39465       ],
39466       [
39467         "Malta",
39468         "mt",
39469         "356"
39470       ],
39471       [
39472         "Marshall Islands",
39473         "mh",
39474         "692"
39475       ],
39476       [
39477         "Martinique",
39478         "mq",
39479         "596"
39480       ],
39481       [
39482         "Mauritania (‫موريتانيا‬‎)",
39483         "mr",
39484         "222"
39485       ],
39486       [
39487         "Mauritius (Moris)",
39488         "mu",
39489         "230"
39490       ],
39491       [
39492         "Mayotte",
39493         "yt",
39494         "262",
39495         1
39496       ],
39497       [
39498         "Mexico (México)",
39499         "mx",
39500         "52"
39501       ],
39502       [
39503         "Micronesia",
39504         "fm",
39505         "691"
39506       ],
39507       [
39508         "Moldova (Republica Moldova)",
39509         "md",
39510         "373"
39511       ],
39512       [
39513         "Monaco",
39514         "mc",
39515         "377"
39516       ],
39517       [
39518         "Mongolia (Монгол)",
39519         "mn",
39520         "976"
39521       ],
39522       [
39523         "Montenegro (Crna Gora)",
39524         "me",
39525         "382"
39526       ],
39527       [
39528         "Montserrat",
39529         "ms",
39530         "1664"
39531       ],
39532       [
39533         "Morocco (‫المغرب‬‎)",
39534         "ma",
39535         "212",
39536         0
39537       ],
39538       [
39539         "Mozambique (Moçambique)",
39540         "mz",
39541         "258"
39542       ],
39543       [
39544         "Myanmar (Burma) (မြန်မာ)",
39545         "mm",
39546         "95"
39547       ],
39548       [
39549         "Namibia (Namibië)",
39550         "na",
39551         "264"
39552       ],
39553       [
39554         "Nauru",
39555         "nr",
39556         "674"
39557       ],
39558       [
39559         "Nepal (नेपाल)",
39560         "np",
39561         "977"
39562       ],
39563       [
39564         "Netherlands (Nederland)",
39565         "nl",
39566         "31"
39567       ],
39568       [
39569         "New Caledonia (Nouvelle-Calédonie)",
39570         "nc",
39571         "687"
39572       ],
39573       [
39574         "New Zealand",
39575         "nz",
39576         "64"
39577       ],
39578       [
39579         "Nicaragua",
39580         "ni",
39581         "505"
39582       ],
39583       [
39584         "Niger (Nijar)",
39585         "ne",
39586         "227"
39587       ],
39588       [
39589         "Nigeria",
39590         "ng",
39591         "234"
39592       ],
39593       [
39594         "Niue",
39595         "nu",
39596         "683"
39597       ],
39598       [
39599         "Norfolk Island",
39600         "nf",
39601         "672"
39602       ],
39603       [
39604         "North Korea (조선 민주주의 인민 공화국)",
39605         "kp",
39606         "850"
39607       ],
39608       [
39609         "Northern Mariana Islands",
39610         "mp",
39611         "1670"
39612       ],
39613       [
39614         "Norway (Norge)",
39615         "no",
39616         "47",
39617         0
39618       ],
39619       [
39620         "Oman (‫عُمان‬‎)",
39621         "om",
39622         "968"
39623       ],
39624       [
39625         "Pakistan (‫پاکستان‬‎)",
39626         "pk",
39627         "92"
39628       ],
39629       [
39630         "Palau",
39631         "pw",
39632         "680"
39633       ],
39634       [
39635         "Palestine (‫فلسطين‬‎)",
39636         "ps",
39637         "970"
39638       ],
39639       [
39640         "Panama (Panamá)",
39641         "pa",
39642         "507"
39643       ],
39644       [
39645         "Papua New Guinea",
39646         "pg",
39647         "675"
39648       ],
39649       [
39650         "Paraguay",
39651         "py",
39652         "595"
39653       ],
39654       [
39655         "Peru (Perú)",
39656         "pe",
39657         "51"
39658       ],
39659       [
39660         "Philippines",
39661         "ph",
39662         "63"
39663       ],
39664       [
39665         "Poland (Polska)",
39666         "pl",
39667         "48"
39668       ],
39669       [
39670         "Portugal",
39671         "pt",
39672         "351"
39673       ],
39674       [
39675         "Puerto Rico",
39676         "pr",
39677         "1",
39678         3,
39679         ["787", "939"]
39680       ],
39681       [
39682         "Qatar (‫قطر‬‎)",
39683         "qa",
39684         "974"
39685       ],
39686       [
39687         "Réunion (La Réunion)",
39688         "re",
39689         "262",
39690         0
39691       ],
39692       [
39693         "Romania (România)",
39694         "ro",
39695         "40"
39696       ],
39697       [
39698         "Russia (Россия)",
39699         "ru",
39700         "7",
39701         0
39702       ],
39703       [
39704         "Rwanda",
39705         "rw",
39706         "250"
39707       ],
39708       [
39709         "Saint Barthélemy",
39710         "bl",
39711         "590",
39712         1
39713       ],
39714       [
39715         "Saint Helena",
39716         "sh",
39717         "290"
39718       ],
39719       [
39720         "Saint Kitts and Nevis",
39721         "kn",
39722         "1869"
39723       ],
39724       [
39725         "Saint Lucia",
39726         "lc",
39727         "1758"
39728       ],
39729       [
39730         "Saint Martin (Saint-Martin (partie française))",
39731         "mf",
39732         "590",
39733         2
39734       ],
39735       [
39736         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39737         "pm",
39738         "508"
39739       ],
39740       [
39741         "Saint Vincent and the Grenadines",
39742         "vc",
39743         "1784"
39744       ],
39745       [
39746         "Samoa",
39747         "ws",
39748         "685"
39749       ],
39750       [
39751         "San Marino",
39752         "sm",
39753         "378"
39754       ],
39755       [
39756         "São Tomé and Príncipe (São Tomé e Príncipe)",
39757         "st",
39758         "239"
39759       ],
39760       [
39761         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39762         "sa",
39763         "966"
39764       ],
39765       [
39766         "Senegal (Sénégal)",
39767         "sn",
39768         "221"
39769       ],
39770       [
39771         "Serbia (Србија)",
39772         "rs",
39773         "381"
39774       ],
39775       [
39776         "Seychelles",
39777         "sc",
39778         "248"
39779       ],
39780       [
39781         "Sierra Leone",
39782         "sl",
39783         "232"
39784       ],
39785       [
39786         "Singapore",
39787         "sg",
39788         "65"
39789       ],
39790       [
39791         "Sint Maarten",
39792         "sx",
39793         "1721"
39794       ],
39795       [
39796         "Slovakia (Slovensko)",
39797         "sk",
39798         "421"
39799       ],
39800       [
39801         "Slovenia (Slovenija)",
39802         "si",
39803         "386"
39804       ],
39805       [
39806         "Solomon Islands",
39807         "sb",
39808         "677"
39809       ],
39810       [
39811         "Somalia (Soomaaliya)",
39812         "so",
39813         "252"
39814       ],
39815       [
39816         "South Africa",
39817         "za",
39818         "27"
39819       ],
39820       [
39821         "South Korea (대한민국)",
39822         "kr",
39823         "82"
39824       ],
39825       [
39826         "South Sudan (‫جنوب السودان‬‎)",
39827         "ss",
39828         "211"
39829       ],
39830       [
39831         "Spain (España)",
39832         "es",
39833         "34"
39834       ],
39835       [
39836         "Sri Lanka (ශ්‍රී ලංකාව)",
39837         "lk",
39838         "94"
39839       ],
39840       [
39841         "Sudan (‫السودان‬‎)",
39842         "sd",
39843         "249"
39844       ],
39845       [
39846         "Suriname",
39847         "sr",
39848         "597"
39849       ],
39850       [
39851         "Svalbard and Jan Mayen",
39852         "sj",
39853         "47",
39854         1
39855       ],
39856       [
39857         "Swaziland",
39858         "sz",
39859         "268"
39860       ],
39861       [
39862         "Sweden (Sverige)",
39863         "se",
39864         "46"
39865       ],
39866       [
39867         "Switzerland (Schweiz)",
39868         "ch",
39869         "41"
39870       ],
39871       [
39872         "Syria (‫سوريا‬‎)",
39873         "sy",
39874         "963"
39875       ],
39876       [
39877         "Taiwan (台灣)",
39878         "tw",
39879         "886"
39880       ],
39881       [
39882         "Tajikistan",
39883         "tj",
39884         "992"
39885       ],
39886       [
39887         "Tanzania",
39888         "tz",
39889         "255"
39890       ],
39891       [
39892         "Thailand (ไทย)",
39893         "th",
39894         "66"
39895       ],
39896       [
39897         "Timor-Leste",
39898         "tl",
39899         "670"
39900       ],
39901       [
39902         "Togo",
39903         "tg",
39904         "228"
39905       ],
39906       [
39907         "Tokelau",
39908         "tk",
39909         "690"
39910       ],
39911       [
39912         "Tonga",
39913         "to",
39914         "676"
39915       ],
39916       [
39917         "Trinidad and Tobago",
39918         "tt",
39919         "1868"
39920       ],
39921       [
39922         "Tunisia (‫تونس‬‎)",
39923         "tn",
39924         "216"
39925       ],
39926       [
39927         "Turkey (Türkiye)",
39928         "tr",
39929         "90"
39930       ],
39931       [
39932         "Turkmenistan",
39933         "tm",
39934         "993"
39935       ],
39936       [
39937         "Turks and Caicos Islands",
39938         "tc",
39939         "1649"
39940       ],
39941       [
39942         "Tuvalu",
39943         "tv",
39944         "688"
39945       ],
39946       [
39947         "U.S. Virgin Islands",
39948         "vi",
39949         "1340"
39950       ],
39951       [
39952         "Uganda",
39953         "ug",
39954         "256"
39955       ],
39956       [
39957         "Ukraine (Україна)",
39958         "ua",
39959         "380"
39960       ],
39961       [
39962         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39963         "ae",
39964         "971"
39965       ],
39966       [
39967         "United Kingdom",
39968         "gb",
39969         "44",
39970         0
39971       ],
39972       [
39973         "United States",
39974         "us",
39975         "1",
39976         0
39977       ],
39978       [
39979         "Uruguay",
39980         "uy",
39981         "598"
39982       ],
39983       [
39984         "Uzbekistan (Oʻzbekiston)",
39985         "uz",
39986         "998"
39987       ],
39988       [
39989         "Vanuatu",
39990         "vu",
39991         "678"
39992       ],
39993       [
39994         "Vatican City (Città del Vaticano)",
39995         "va",
39996         "39",
39997         1
39998       ],
39999       [
40000         "Venezuela",
40001         "ve",
40002         "58"
40003       ],
40004       [
40005         "Vietnam (Việt Nam)",
40006         "vn",
40007         "84"
40008       ],
40009       [
40010         "Wallis and Futuna (Wallis-et-Futuna)",
40011         "wf",
40012         "681"
40013       ],
40014       [
40015         "Western Sahara (‫الصحراء الغربية‬‎)",
40016         "eh",
40017         "212",
40018         1
40019       ],
40020       [
40021         "Yemen (‫اليمن‬‎)",
40022         "ye",
40023         "967"
40024       ],
40025       [
40026         "Zambia",
40027         "zm",
40028         "260"
40029       ],
40030       [
40031         "Zimbabwe",
40032         "zw",
40033         "263"
40034       ],
40035       [
40036         "Åland Islands",
40037         "ax",
40038         "358",
40039         1
40040       ]
40041   ];
40042   
40043   return d;
40044 }/**
40045 *    This script refer to:
40046 *    Title: International Telephone Input
40047 *    Author: Jack O'Connor
40048 *    Code version:  v12.1.12
40049 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40050 **/
40051
40052 /**
40053  * @class Roo.bootstrap.PhoneInput
40054  * @extends Roo.bootstrap.TriggerField
40055  * An input with International dial-code selection
40056  
40057  * @cfg {String} defaultDialCode default '+852'
40058  * @cfg {Array} preferedCountries default []
40059   
40060  * @constructor
40061  * Create a new PhoneInput.
40062  * @param {Object} config Configuration options
40063  */
40064
40065 Roo.bootstrap.PhoneInput = function(config) {
40066     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40067 };
40068
40069 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40070         
40071         listWidth: undefined,
40072         
40073         selectedClass: 'active',
40074         
40075         invalidClass : "has-warning",
40076         
40077         validClass: 'has-success',
40078         
40079         allowed: '0123456789',
40080         
40081         max_length: 15,
40082         
40083         /**
40084          * @cfg {String} defaultDialCode The default dial code when initializing the input
40085          */
40086         defaultDialCode: '+852',
40087         
40088         /**
40089          * @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
40090          */
40091         preferedCountries: false,
40092         
40093         getAutoCreate : function()
40094         {
40095             var data = Roo.bootstrap.PhoneInputData();
40096             var align = this.labelAlign || this.parentLabelAlign();
40097             var id = Roo.id();
40098             
40099             this.allCountries = [];
40100             this.dialCodeMapping = [];
40101             
40102             for (var i = 0; i < data.length; i++) {
40103               var c = data[i];
40104               this.allCountries[i] = {
40105                 name: c[0],
40106                 iso2: c[1],
40107                 dialCode: c[2],
40108                 priority: c[3] || 0,
40109                 areaCodes: c[4] || null
40110               };
40111               this.dialCodeMapping[c[2]] = {
40112                   name: c[0],
40113                   iso2: c[1],
40114                   priority: c[3] || 0,
40115                   areaCodes: c[4] || null
40116               };
40117             }
40118             
40119             var cfg = {
40120                 cls: 'form-group',
40121                 cn: []
40122             };
40123             
40124             var input =  {
40125                 tag: 'input',
40126                 id : id,
40127                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40128                 maxlength: this.max_length,
40129                 cls : 'form-control tel-input',
40130                 autocomplete: 'new-password'
40131             };
40132             
40133             var hiddenInput = {
40134                 tag: 'input',
40135                 type: 'hidden',
40136                 cls: 'hidden-tel-input'
40137             };
40138             
40139             if (this.name) {
40140                 hiddenInput.name = this.name;
40141             }
40142             
40143             if (this.disabled) {
40144                 input.disabled = true;
40145             }
40146             
40147             var flag_container = {
40148                 tag: 'div',
40149                 cls: 'flag-box',
40150                 cn: [
40151                     {
40152                         tag: 'div',
40153                         cls: 'flag'
40154                     },
40155                     {
40156                         tag: 'div',
40157                         cls: 'caret'
40158                     }
40159                 ]
40160             };
40161             
40162             var box = {
40163                 tag: 'div',
40164                 cls: this.hasFeedback ? 'has-feedback' : '',
40165                 cn: [
40166                     hiddenInput,
40167                     input,
40168                     {
40169                         tag: 'input',
40170                         cls: 'dial-code-holder',
40171                         disabled: true
40172                     }
40173                 ]
40174             };
40175             
40176             var container = {
40177                 cls: 'roo-select2-container input-group',
40178                 cn: [
40179                     flag_container,
40180                     box
40181                 ]
40182             };
40183             
40184             if (this.fieldLabel.length) {
40185                 var indicator = {
40186                     tag: 'i',
40187                     tooltip: 'This field is required'
40188                 };
40189                 
40190                 var label = {
40191                     tag: 'label',
40192                     'for':  id,
40193                     cls: 'control-label',
40194                     cn: []
40195                 };
40196                 
40197                 var label_text = {
40198                     tag: 'span',
40199                     html: this.fieldLabel
40200                 };
40201                 
40202                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40203                 label.cn = [
40204                     indicator,
40205                     label_text
40206                 ];
40207                 
40208                 if(this.indicatorpos == 'right') {
40209                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40210                     label.cn = [
40211                         label_text,
40212                         indicator
40213                     ];
40214                 }
40215                 
40216                 if(align == 'left') {
40217                     container = {
40218                         tag: 'div',
40219                         cn: [
40220                             container
40221                         ]
40222                     };
40223                     
40224                     if(this.labelWidth > 12){
40225                         label.style = "width: " + this.labelWidth + 'px';
40226                     }
40227                     if(this.labelWidth < 13 && this.labelmd == 0){
40228                         this.labelmd = this.labelWidth;
40229                     }
40230                     if(this.labellg > 0){
40231                         label.cls += ' col-lg-' + this.labellg;
40232                         input.cls += ' col-lg-' + (12 - this.labellg);
40233                     }
40234                     if(this.labelmd > 0){
40235                         label.cls += ' col-md-' + this.labelmd;
40236                         container.cls += ' col-md-' + (12 - this.labelmd);
40237                     }
40238                     if(this.labelsm > 0){
40239                         label.cls += ' col-sm-' + this.labelsm;
40240                         container.cls += ' col-sm-' + (12 - this.labelsm);
40241                     }
40242                     if(this.labelxs > 0){
40243                         label.cls += ' col-xs-' + this.labelxs;
40244                         container.cls += ' col-xs-' + (12 - this.labelxs);
40245                     }
40246                 }
40247             }
40248             
40249             cfg.cn = [
40250                 label,
40251                 container
40252             ];
40253             
40254             var settings = this;
40255             
40256             ['xs','sm','md','lg'].map(function(size){
40257                 if (settings[size]) {
40258                     cfg.cls += ' col-' + size + '-' + settings[size];
40259                 }
40260             });
40261             
40262             this.store = new Roo.data.Store({
40263                 proxy : new Roo.data.MemoryProxy({}),
40264                 reader : new Roo.data.JsonReader({
40265                     fields : [
40266                         {
40267                             'name' : 'name',
40268                             'type' : 'string'
40269                         },
40270                         {
40271                             'name' : 'iso2',
40272                             'type' : 'string'
40273                         },
40274                         {
40275                             'name' : 'dialCode',
40276                             'type' : 'string'
40277                         },
40278                         {
40279                             'name' : 'priority',
40280                             'type' : 'string'
40281                         },
40282                         {
40283                             'name' : 'areaCodes',
40284                             'type' : 'string'
40285                         }
40286                     ]
40287                 })
40288             });
40289             
40290             if(!this.preferedCountries) {
40291                 this.preferedCountries = [
40292                     'hk',
40293                     'gb',
40294                     'us'
40295                 ];
40296             }
40297             
40298             var p = this.preferedCountries.reverse();
40299             
40300             if(p) {
40301                 for (var i = 0; i < p.length; i++) {
40302                     for (var j = 0; j < this.allCountries.length; j++) {
40303                         if(this.allCountries[j].iso2 == p[i]) {
40304                             var t = this.allCountries[j];
40305                             this.allCountries.splice(j,1);
40306                             this.allCountries.unshift(t);
40307                         }
40308                     } 
40309                 }
40310             }
40311             
40312             this.store.proxy.data = {
40313                 success: true,
40314                 data: this.allCountries
40315             };
40316             
40317             return cfg;
40318         },
40319         
40320         initEvents : function()
40321         {
40322             this.createList();
40323             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40324             
40325             this.indicator = this.indicatorEl();
40326             this.flag = this.flagEl();
40327             this.dialCodeHolder = this.dialCodeHolderEl();
40328             
40329             this.trigger = this.el.select('div.flag-box',true).first();
40330             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40331             
40332             var _this = this;
40333             
40334             (function(){
40335                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40336                 _this.list.setWidth(lw);
40337             }).defer(100);
40338             
40339             this.list.on('mouseover', this.onViewOver, this);
40340             this.list.on('mousemove', this.onViewMove, this);
40341             this.inputEl().on("keyup", this.onKeyUp, this);
40342             this.inputEl().on("keypress", this.onKeyPress, this);
40343             
40344             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40345
40346             this.view = new Roo.View(this.list, this.tpl, {
40347                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40348             });
40349             
40350             this.view.on('click', this.onViewClick, this);
40351             this.setValue(this.defaultDialCode);
40352         },
40353         
40354         onTriggerClick : function(e)
40355         {
40356             Roo.log('trigger click');
40357             if(this.disabled){
40358                 return;
40359             }
40360             
40361             if(this.isExpanded()){
40362                 this.collapse();
40363                 this.hasFocus = false;
40364             }else {
40365                 this.store.load({});
40366                 this.hasFocus = true;
40367                 this.expand();
40368             }
40369         },
40370         
40371         isExpanded : function()
40372         {
40373             return this.list.isVisible();
40374         },
40375         
40376         collapse : function()
40377         {
40378             if(!this.isExpanded()){
40379                 return;
40380             }
40381             this.list.hide();
40382             Roo.get(document).un('mousedown', this.collapseIf, this);
40383             Roo.get(document).un('mousewheel', this.collapseIf, this);
40384             this.fireEvent('collapse', this);
40385             this.validate();
40386         },
40387         
40388         expand : function()
40389         {
40390             Roo.log('expand');
40391
40392             if(this.isExpanded() || !this.hasFocus){
40393                 return;
40394             }
40395             
40396             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40397             this.list.setWidth(lw);
40398             
40399             this.list.show();
40400             this.restrictHeight();
40401             
40402             Roo.get(document).on('mousedown', this.collapseIf, this);
40403             Roo.get(document).on('mousewheel', this.collapseIf, this);
40404             
40405             this.fireEvent('expand', this);
40406         },
40407         
40408         restrictHeight : function()
40409         {
40410             this.list.alignTo(this.inputEl(), this.listAlign);
40411             this.list.alignTo(this.inputEl(), this.listAlign);
40412         },
40413         
40414         onViewOver : function(e, t)
40415         {
40416             if(this.inKeyMode){
40417                 return;
40418             }
40419             var item = this.view.findItemFromChild(t);
40420             
40421             if(item){
40422                 var index = this.view.indexOf(item);
40423                 this.select(index, false);
40424             }
40425         },
40426
40427         // private
40428         onViewClick : function(view, doFocus, el, e)
40429         {
40430             var index = this.view.getSelectedIndexes()[0];
40431             
40432             var r = this.store.getAt(index);
40433             
40434             if(r){
40435                 this.onSelect(r, index);
40436             }
40437             if(doFocus !== false && !this.blockFocus){
40438                 this.inputEl().focus();
40439             }
40440         },
40441         
40442         onViewMove : function(e, t)
40443         {
40444             this.inKeyMode = false;
40445         },
40446         
40447         select : function(index, scrollIntoView)
40448         {
40449             this.selectedIndex = index;
40450             this.view.select(index);
40451             if(scrollIntoView !== false){
40452                 var el = this.view.getNode(index);
40453                 if(el){
40454                     this.list.scrollChildIntoView(el, false);
40455                 }
40456             }
40457         },
40458         
40459         createList : function()
40460         {
40461             this.list = Roo.get(document.body).createChild({
40462                 tag: 'ul',
40463                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40464                 style: 'display:none'
40465             });
40466             
40467             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40468         },
40469         
40470         collapseIf : function(e)
40471         {
40472             var in_combo  = e.within(this.el);
40473             var in_list =  e.within(this.list);
40474             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40475             
40476             if (in_combo || in_list || is_list) {
40477                 return;
40478             }
40479             this.collapse();
40480         },
40481         
40482         onSelect : function(record, index)
40483         {
40484             if(this.fireEvent('beforeselect', this, record, index) !== false){
40485                 
40486                 this.setFlagClass(record.data.iso2);
40487                 this.setDialCode(record.data.dialCode);
40488                 this.hasFocus = false;
40489                 this.collapse();
40490                 this.fireEvent('select', this, record, index);
40491             }
40492         },
40493         
40494         flagEl : function()
40495         {
40496             var flag = this.el.select('div.flag',true).first();
40497             if(!flag){
40498                 return false;
40499             }
40500             return flag;
40501         },
40502         
40503         dialCodeHolderEl : function()
40504         {
40505             var d = this.el.select('input.dial-code-holder',true).first();
40506             if(!d){
40507                 return false;
40508             }
40509             return d;
40510         },
40511         
40512         setDialCode : function(v)
40513         {
40514             this.dialCodeHolder.dom.value = '+'+v;
40515         },
40516         
40517         setFlagClass : function(n)
40518         {
40519             this.flag.dom.className = 'flag '+n;
40520         },
40521         
40522         getValue : function()
40523         {
40524             var v = this.inputEl().getValue();
40525             if(this.dialCodeHolder) {
40526                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40527             }
40528             return v;
40529         },
40530         
40531         setValue : function(v)
40532         {
40533             var d = this.getDialCode(v);
40534             
40535             //invalid dial code
40536             if(v.length == 0 || !d || d.length == 0) {
40537                 if(this.rendered){
40538                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40539                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40540                 }
40541                 return;
40542             }
40543             
40544             //valid dial code
40545             this.setFlagClass(this.dialCodeMapping[d].iso2);
40546             this.setDialCode(d);
40547             this.inputEl().dom.value = v.replace('+'+d,'');
40548             this.hiddenEl().dom.value = this.getValue();
40549             
40550             this.validate();
40551         },
40552         
40553         getDialCode : function(v)
40554         {
40555             v = v ||  '';
40556             
40557             if (v.length == 0) {
40558                 return this.dialCodeHolder.dom.value;
40559             }
40560             
40561             var dialCode = "";
40562             if (v.charAt(0) != "+") {
40563                 return false;
40564             }
40565             var numericChars = "";
40566             for (var i = 1; i < v.length; i++) {
40567               var c = v.charAt(i);
40568               if (!isNaN(c)) {
40569                 numericChars += c;
40570                 if (this.dialCodeMapping[numericChars]) {
40571                   dialCode = v.substr(1, i);
40572                 }
40573                 if (numericChars.length == 4) {
40574                   break;
40575                 }
40576               }
40577             }
40578             return dialCode;
40579         },
40580         
40581         reset : function()
40582         {
40583             this.setValue(this.defaultDialCode);
40584             this.validate();
40585         },
40586         
40587         hiddenEl : function()
40588         {
40589             return this.el.select('input.hidden-tel-input',true).first();
40590         },
40591         
40592         // after setting val
40593         onKeyUp : function(e){
40594             this.setValue(this.getValue());
40595         },
40596         
40597         onKeyPress : function(e){
40598             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40599                 e.stopEvent();
40600             }
40601         }
40602         
40603 });
40604 /**
40605  * @class Roo.bootstrap.MoneyField
40606  * @extends Roo.bootstrap.ComboBox
40607  * Bootstrap MoneyField class
40608  * 
40609  * @constructor
40610  * Create a new MoneyField.
40611  * @param {Object} config Configuration options
40612  */
40613
40614 Roo.bootstrap.MoneyField = function(config) {
40615     
40616     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40617     
40618 };
40619
40620 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40621     
40622     /**
40623      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40624      */
40625     allowDecimals : true,
40626     /**
40627      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40628      */
40629     decimalSeparator : ".",
40630     /**
40631      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40632      */
40633     decimalPrecision : 0,
40634     /**
40635      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40636      */
40637     allowNegative : true,
40638     /**
40639      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40640      */
40641     allowZero: true,
40642     /**
40643      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40644      */
40645     minValue : Number.NEGATIVE_INFINITY,
40646     /**
40647      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40648      */
40649     maxValue : Number.MAX_VALUE,
40650     /**
40651      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40652      */
40653     minText : "The minimum value for this field is {0}",
40654     /**
40655      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40656      */
40657     maxText : "The maximum value for this field is {0}",
40658     /**
40659      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40660      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40661      */
40662     nanText : "{0} is not a valid number",
40663     /**
40664      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40665      */
40666     castInt : true,
40667     /**
40668      * @cfg {String} defaults currency of the MoneyField
40669      * value should be in lkey
40670      */
40671     defaultCurrency : false,
40672     /**
40673      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40674      */
40675     thousandsDelimiter : false,
40676     /**
40677      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40678      */
40679     max_length: false,
40680     
40681     inputlg : 9,
40682     inputmd : 9,
40683     inputsm : 9,
40684     inputxs : 6,
40685     
40686     store : false,
40687     
40688     getAutoCreate : function()
40689     {
40690         var align = this.labelAlign || this.parentLabelAlign();
40691         
40692         var id = Roo.id();
40693
40694         var cfg = {
40695             cls: 'form-group',
40696             cn: []
40697         };
40698
40699         var input =  {
40700             tag: 'input',
40701             id : id,
40702             cls : 'form-control roo-money-amount-input',
40703             autocomplete: 'new-password'
40704         };
40705         
40706         var hiddenInput = {
40707             tag: 'input',
40708             type: 'hidden',
40709             id: Roo.id(),
40710             cls: 'hidden-number-input'
40711         };
40712         
40713         if(this.max_length) {
40714             input.maxlength = this.max_length; 
40715         }
40716         
40717         if (this.name) {
40718             hiddenInput.name = this.name;
40719         }
40720
40721         if (this.disabled) {
40722             input.disabled = true;
40723         }
40724
40725         var clg = 12 - this.inputlg;
40726         var cmd = 12 - this.inputmd;
40727         var csm = 12 - this.inputsm;
40728         var cxs = 12 - this.inputxs;
40729         
40730         var container = {
40731             tag : 'div',
40732             cls : 'row roo-money-field',
40733             cn : [
40734                 {
40735                     tag : 'div',
40736                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40737                     cn : [
40738                         {
40739                             tag : 'div',
40740                             cls: 'roo-select2-container input-group',
40741                             cn: [
40742                                 {
40743                                     tag : 'input',
40744                                     cls : 'form-control roo-money-currency-input',
40745                                     autocomplete: 'new-password',
40746                                     readOnly : 1,
40747                                     name : this.currencyName
40748                                 },
40749                                 {
40750                                     tag :'span',
40751                                     cls : 'input-group-addon',
40752                                     cn : [
40753                                         {
40754                                             tag: 'span',
40755                                             cls: 'caret'
40756                                         }
40757                                     ]
40758                                 }
40759                             ]
40760                         }
40761                     ]
40762                 },
40763                 {
40764                     tag : 'div',
40765                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40766                     cn : [
40767                         {
40768                             tag: 'div',
40769                             cls: this.hasFeedback ? 'has-feedback' : '',
40770                             cn: [
40771                                 input
40772                             ]
40773                         }
40774                     ]
40775                 }
40776             ]
40777             
40778         };
40779         
40780         if (this.fieldLabel.length) {
40781             var indicator = {
40782                 tag: 'i',
40783                 tooltip: 'This field is required'
40784             };
40785
40786             var label = {
40787                 tag: 'label',
40788                 'for':  id,
40789                 cls: 'control-label',
40790                 cn: []
40791             };
40792
40793             var label_text = {
40794                 tag: 'span',
40795                 html: this.fieldLabel
40796             };
40797
40798             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40799             label.cn = [
40800                 indicator,
40801                 label_text
40802             ];
40803
40804             if(this.indicatorpos == 'right') {
40805                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40806                 label.cn = [
40807                     label_text,
40808                     indicator
40809                 ];
40810             }
40811
40812             if(align == 'left') {
40813                 container = {
40814                     tag: 'div',
40815                     cn: [
40816                         container
40817                     ]
40818                 };
40819
40820                 if(this.labelWidth > 12){
40821                     label.style = "width: " + this.labelWidth + 'px';
40822                 }
40823                 if(this.labelWidth < 13 && this.labelmd == 0){
40824                     this.labelmd = this.labelWidth;
40825                 }
40826                 if(this.labellg > 0){
40827                     label.cls += ' col-lg-' + this.labellg;
40828                     input.cls += ' col-lg-' + (12 - this.labellg);
40829                 }
40830                 if(this.labelmd > 0){
40831                     label.cls += ' col-md-' + this.labelmd;
40832                     container.cls += ' col-md-' + (12 - this.labelmd);
40833                 }
40834                 if(this.labelsm > 0){
40835                     label.cls += ' col-sm-' + this.labelsm;
40836                     container.cls += ' col-sm-' + (12 - this.labelsm);
40837                 }
40838                 if(this.labelxs > 0){
40839                     label.cls += ' col-xs-' + this.labelxs;
40840                     container.cls += ' col-xs-' + (12 - this.labelxs);
40841                 }
40842             }
40843         }
40844
40845         cfg.cn = [
40846             label,
40847             container,
40848             hiddenInput
40849         ];
40850         
40851         var settings = this;
40852
40853         ['xs','sm','md','lg'].map(function(size){
40854             if (settings[size]) {
40855                 cfg.cls += ' col-' + size + '-' + settings[size];
40856             }
40857         });
40858         
40859         return cfg;
40860     },
40861     
40862     initEvents : function()
40863     {
40864         this.indicator = this.indicatorEl();
40865         
40866         this.initCurrencyEvent();
40867         
40868         this.initNumberEvent();
40869     },
40870     
40871     initCurrencyEvent : function()
40872     {
40873         if (!this.store) {
40874             throw "can not find store for combo";
40875         }
40876         
40877         this.store = Roo.factory(this.store, Roo.data);
40878         this.store.parent = this;
40879         
40880         this.createList();
40881         
40882         this.triggerEl = this.el.select('.input-group-addon', true).first();
40883         
40884         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40885         
40886         var _this = this;
40887         
40888         (function(){
40889             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40890             _this.list.setWidth(lw);
40891         }).defer(100);
40892         
40893         this.list.on('mouseover', this.onViewOver, this);
40894         this.list.on('mousemove', this.onViewMove, this);
40895         this.list.on('scroll', this.onViewScroll, this);
40896         
40897         if(!this.tpl){
40898             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40899         }
40900         
40901         this.view = new Roo.View(this.list, this.tpl, {
40902             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40903         });
40904         
40905         this.view.on('click', this.onViewClick, this);
40906         
40907         this.store.on('beforeload', this.onBeforeLoad, this);
40908         this.store.on('load', this.onLoad, this);
40909         this.store.on('loadexception', this.onLoadException, this);
40910         
40911         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40912             "up" : function(e){
40913                 this.inKeyMode = true;
40914                 this.selectPrev();
40915             },
40916
40917             "down" : function(e){
40918                 if(!this.isExpanded()){
40919                     this.onTriggerClick();
40920                 }else{
40921                     this.inKeyMode = true;
40922                     this.selectNext();
40923                 }
40924             },
40925
40926             "enter" : function(e){
40927                 this.collapse();
40928                 
40929                 if(this.fireEvent("specialkey", this, e)){
40930                     this.onViewClick(false);
40931                 }
40932                 
40933                 return true;
40934             },
40935
40936             "esc" : function(e){
40937                 this.collapse();
40938             },
40939
40940             "tab" : function(e){
40941                 this.collapse();
40942                 
40943                 if(this.fireEvent("specialkey", this, e)){
40944                     this.onViewClick(false);
40945                 }
40946                 
40947                 return true;
40948             },
40949
40950             scope : this,
40951
40952             doRelay : function(foo, bar, hname){
40953                 if(hname == 'down' || this.scope.isExpanded()){
40954                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40955                 }
40956                 return true;
40957             },
40958
40959             forceKeyDown: true
40960         });
40961         
40962         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40963         
40964     },
40965     
40966     initNumberEvent : function(e)
40967     {
40968         this.inputEl().on("keydown" , this.fireKey,  this);
40969         this.inputEl().on("focus", this.onFocus,  this);
40970         this.inputEl().on("blur", this.onBlur,  this);
40971         
40972         this.inputEl().relayEvent('keyup', this);
40973         
40974         if(this.indicator){
40975             this.indicator.addClass('invisible');
40976         }
40977  
40978         this.originalValue = this.getValue();
40979         
40980         if(this.validationEvent == 'keyup'){
40981             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40982             this.inputEl().on('keyup', this.filterValidation, this);
40983         }
40984         else if(this.validationEvent !== false){
40985             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40986         }
40987         
40988         if(this.selectOnFocus){
40989             this.on("focus", this.preFocus, this);
40990             
40991         }
40992         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40993             this.inputEl().on("keypress", this.filterKeys, this);
40994         } else {
40995             this.inputEl().relayEvent('keypress', this);
40996         }
40997         
40998         var allowed = "0123456789";
40999         
41000         if(this.allowDecimals){
41001             allowed += this.decimalSeparator;
41002         }
41003         
41004         if(this.allowNegative){
41005             allowed += "-";
41006         }
41007         
41008         if(this.thousandsDelimiter) {
41009             allowed += ",";
41010         }
41011         
41012         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41013         
41014         var keyPress = function(e){
41015             
41016             var k = e.getKey();
41017             
41018             var c = e.getCharCode();
41019             
41020             if(
41021                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41022                     allowed.indexOf(String.fromCharCode(c)) === -1
41023             ){
41024                 e.stopEvent();
41025                 return;
41026             }
41027             
41028             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41029                 return;
41030             }
41031             
41032             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41033                 e.stopEvent();
41034             }
41035         };
41036         
41037         this.inputEl().on("keypress", keyPress, this);
41038         
41039     },
41040     
41041     onTriggerClick : function(e)
41042     {   
41043         if(this.disabled){
41044             return;
41045         }
41046         
41047         this.page = 0;
41048         this.loadNext = false;
41049         
41050         if(this.isExpanded()){
41051             this.collapse();
41052             return;
41053         }
41054         
41055         this.hasFocus = true;
41056         
41057         if(this.triggerAction == 'all') {
41058             this.doQuery(this.allQuery, true);
41059             return;
41060         }
41061         
41062         this.doQuery(this.getRawValue());
41063     },
41064     
41065     getCurrency : function()
41066     {   
41067         var v = this.currencyEl().getValue();
41068         
41069         return v;
41070     },
41071     
41072     restrictHeight : function()
41073     {
41074         this.list.alignTo(this.currencyEl(), this.listAlign);
41075         this.list.alignTo(this.currencyEl(), this.listAlign);
41076     },
41077     
41078     onViewClick : function(view, doFocus, el, e)
41079     {
41080         var index = this.view.getSelectedIndexes()[0];
41081         
41082         var r = this.store.getAt(index);
41083         
41084         if(r){
41085             this.onSelect(r, index);
41086         }
41087     },
41088     
41089     onSelect : function(record, index){
41090         
41091         if(this.fireEvent('beforeselect', this, record, index) !== false){
41092         
41093             this.setFromCurrencyData(index > -1 ? record.data : false);
41094             
41095             this.collapse();
41096             
41097             this.fireEvent('select', this, record, index);
41098         }
41099     },
41100     
41101     setFromCurrencyData : function(o)
41102     {
41103         var currency = '';
41104         
41105         this.lastCurrency = o;
41106         
41107         if (this.currencyField) {
41108             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41109         } else {
41110             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41111         }
41112         
41113         this.lastSelectionText = currency;
41114         
41115         //setting default currency
41116         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41117             this.setCurrency(this.defaultCurrency);
41118             return;
41119         }
41120         
41121         this.setCurrency(currency);
41122     },
41123     
41124     setFromData : function(o)
41125     {
41126         var c = {};
41127         
41128         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41129         
41130         this.setFromCurrencyData(c);
41131         
41132         var value = '';
41133         
41134         if (this.name) {
41135             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41136         } else {
41137             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41138         }
41139         
41140         this.setValue(value);
41141         
41142     },
41143     
41144     setCurrency : function(v)
41145     {   
41146         this.currencyValue = v;
41147         
41148         if(this.rendered){
41149             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41150             this.validate();
41151         }
41152     },
41153     
41154     setValue : function(v)
41155     {
41156         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41157         
41158         this.value = v;
41159         
41160         if(this.rendered){
41161             
41162             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41163             
41164             this.inputEl().dom.value = (v == '') ? '' :
41165                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41166             
41167             if(!this.allowZero && v === '0') {
41168                 this.hiddenEl().dom.value = '';
41169                 this.inputEl().dom.value = '';
41170             }
41171             
41172             this.validate();
41173         }
41174     },
41175     
41176     getRawValue : function()
41177     {
41178         var v = this.inputEl().getValue();
41179         
41180         return v;
41181     },
41182     
41183     getValue : function()
41184     {
41185         return this.fixPrecision(this.parseValue(this.getRawValue()));
41186     },
41187     
41188     parseValue : function(value)
41189     {
41190         if(this.thousandsDelimiter) {
41191             value += "";
41192             r = new RegExp(",", "g");
41193             value = value.replace(r, "");
41194         }
41195         
41196         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41197         return isNaN(value) ? '' : value;
41198         
41199     },
41200     
41201     fixPrecision : function(value)
41202     {
41203         if(this.thousandsDelimiter) {
41204             value += "";
41205             r = new RegExp(",", "g");
41206             value = value.replace(r, "");
41207         }
41208         
41209         var nan = isNaN(value);
41210         
41211         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41212             return nan ? '' : value;
41213         }
41214         return parseFloat(value).toFixed(this.decimalPrecision);
41215     },
41216     
41217     decimalPrecisionFcn : function(v)
41218     {
41219         return Math.floor(v);
41220     },
41221     
41222     validateValue : function(value)
41223     {
41224         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41225             return false;
41226         }
41227         
41228         var num = this.parseValue(value);
41229         
41230         if(isNaN(num)){
41231             this.markInvalid(String.format(this.nanText, value));
41232             return false;
41233         }
41234         
41235         if(num < this.minValue){
41236             this.markInvalid(String.format(this.minText, this.minValue));
41237             return false;
41238         }
41239         
41240         if(num > this.maxValue){
41241             this.markInvalid(String.format(this.maxText, this.maxValue));
41242             return false;
41243         }
41244         
41245         return true;
41246     },
41247     
41248     validate : function()
41249     {
41250         if(this.disabled || this.allowBlank){
41251             this.markValid();
41252             return true;
41253         }
41254         
41255         var currency = this.getCurrency();
41256         
41257         if(this.validateValue(this.getRawValue()) && currency.length){
41258             this.markValid();
41259             return true;
41260         }
41261         
41262         this.markInvalid();
41263         return false;
41264     },
41265     
41266     getName: function()
41267     {
41268         return this.name;
41269     },
41270     
41271     beforeBlur : function()
41272     {
41273         if(!this.castInt){
41274             return;
41275         }
41276         
41277         var v = this.parseValue(this.getRawValue());
41278         
41279         if(v || v == 0){
41280             this.setValue(v);
41281         }
41282     },
41283     
41284     onBlur : function()
41285     {
41286         this.beforeBlur();
41287         
41288         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41289             //this.el.removeClass(this.focusClass);
41290         }
41291         
41292         this.hasFocus = false;
41293         
41294         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41295             this.validate();
41296         }
41297         
41298         var v = this.getValue();
41299         
41300         if(String(v) !== String(this.startValue)){
41301             this.fireEvent('change', this, v, this.startValue);
41302         }
41303         
41304         this.fireEvent("blur", this);
41305     },
41306     
41307     inputEl : function()
41308     {
41309         return this.el.select('.roo-money-amount-input', true).first();
41310     },
41311     
41312     currencyEl : function()
41313     {
41314         return this.el.select('.roo-money-currency-input', true).first();
41315     },
41316     
41317     hiddenEl : function()
41318     {
41319         return this.el.select('input.hidden-number-input',true).first();
41320     }
41321     
41322 });