Roo/bootstrap/MessageBox.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             this.updateProgress(0);
3609             activeTextEl.dom.value = opt.value || "";
3610             if(opt.prompt){
3611                 dlg.setDefaultButton(activeTextEl);
3612             }else{
3613                 var bs = opt.buttons;
3614                 var db = null;
3615                 if(bs && bs.ok){
3616                     db = buttons["ok"];
3617                 }else if(bs && bs.yes){
3618                     db = buttons["yes"];
3619                 }
3620                 dlg.setDefaultButton(db);
3621             }
3622             bwidth = updateButtons(opt.buttons);
3623             this.updateText(opt.msg);
3624             if(opt.cls){
3625                 d.el.addClass(opt.cls);
3626             }
3627             d.proxyDrag = opt.proxyDrag === true;
3628             d.modal = opt.modal !== false;
3629             d.mask = opt.modal !== false ? mask : false;
3630             if(!d.isVisible()){
3631                 // force it to the end of the z-index stack so it gets a cursor in FF
3632                 document.body.appendChild(dlg.el.dom);
3633                 d.animateTarget = null;
3634                 d.show(options.animEl);
3635             }
3636             return this;
3637         },
3638
3639         /**
3640          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3641          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3642          * and closing the message box when the process is complete.
3643          * @param {String} title The title bar text
3644          * @param {String} msg The message box body text
3645          * @return {Roo.MessageBox} This message box
3646          */
3647         progress : function(title, msg){
3648             this.show({
3649                 title : title,
3650                 msg : msg,
3651                 buttons: false,
3652                 progress:true,
3653                 closable:false,
3654                 minWidth: this.minProgressWidth,
3655                 modal : true
3656             });
3657             return this;
3658         },
3659
3660         /**
3661          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3662          * If a callback function is passed it will be called after the user clicks the button, and the
3663          * id of the button that was clicked will be passed as the only parameter to the callback
3664          * (could also be the top-right close button).
3665          * @param {String} title The title bar text
3666          * @param {String} msg The message box body text
3667          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3668          * @param {Object} scope (optional) The scope of the callback function
3669          * @return {Roo.MessageBox} This message box
3670          */
3671         alert : function(title, msg, fn, scope)
3672         {
3673             this.show({
3674                 title : title,
3675                 msg : msg,
3676                 buttons: this.OK,
3677                 fn: fn,
3678                 closable : false,
3679                 scope : scope,
3680                 modal : true
3681             });
3682             return this;
3683         },
3684
3685         /**
3686          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3687          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3688          * You are responsible for closing the message box when the process is complete.
3689          * @param {String} msg The message box body text
3690          * @param {String} title (optional) The title bar text
3691          * @return {Roo.MessageBox} This message box
3692          */
3693         wait : function(msg, title){
3694             this.show({
3695                 title : title,
3696                 msg : msg,
3697                 buttons: false,
3698                 closable:false,
3699                 progress:true,
3700                 modal:true,
3701                 width:300,
3702                 wait:true
3703             });
3704             waitTimer = Roo.TaskMgr.start({
3705                 run: function(i){
3706                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3707                 },
3708                 interval: 1000
3709             });
3710             return this;
3711         },
3712
3713         /**
3714          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3715          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3716          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3717          * @param {String} title The title bar text
3718          * @param {String} msg The message box body text
3719          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3720          * @param {Object} scope (optional) The scope of the callback function
3721          * @return {Roo.MessageBox} This message box
3722          */
3723         confirm : function(title, msg, fn, scope){
3724             this.show({
3725                 title : title,
3726                 msg : msg,
3727                 buttons: this.YESNO,
3728                 fn: fn,
3729                 scope : scope,
3730                 modal : true
3731             });
3732             return this;
3733         },
3734
3735         /**
3736          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3737          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3738          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3739          * (could also be the top-right close button) and the text that was entered will be passed as the two
3740          * parameters to the callback.
3741          * @param {String} title The title bar text
3742          * @param {String} msg The message box body text
3743          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3744          * @param {Object} scope (optional) The scope of the callback function
3745          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3746          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3747          * @return {Roo.MessageBox} This message box
3748          */
3749         prompt : function(title, msg, fn, scope, multiline){
3750             this.show({
3751                 title : title,
3752                 msg : msg,
3753                 buttons: this.OKCANCEL,
3754                 fn: fn,
3755                 minWidth:250,
3756                 scope : scope,
3757                 prompt:true,
3758                 multiline: multiline,
3759                 modal : true
3760             });
3761             return this;
3762         },
3763
3764         /**
3765          * Button config that displays a single OK button
3766          * @type Object
3767          */
3768         OK : {ok:true},
3769         /**
3770          * Button config that displays Yes and No buttons
3771          * @type Object
3772          */
3773         YESNO : {yes:true, no:true},
3774         /**
3775          * Button config that displays OK and Cancel buttons
3776          * @type Object
3777          */
3778         OKCANCEL : {ok:true, cancel:true},
3779         /**
3780          * Button config that displays Yes, No and Cancel buttons
3781          * @type Object
3782          */
3783         YESNOCANCEL : {yes:true, no:true, cancel:true},
3784
3785         /**
3786          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3787          * @type Number
3788          */
3789         defaultTextHeight : 75,
3790         /**
3791          * The maximum width in pixels of the message box (defaults to 600)
3792          * @type Number
3793          */
3794         maxWidth : 600,
3795         /**
3796          * The minimum width in pixels of the message box (defaults to 100)
3797          * @type Number
3798          */
3799         minWidth : 100,
3800         /**
3801          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3802          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3803          * @type Number
3804          */
3805         minProgressWidth : 250,
3806         /**
3807          * An object containing the default button text strings that can be overriden for localized language support.
3808          * Supported properties are: ok, cancel, yes and no.
3809          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3810          * @type Object
3811          */
3812         buttonText : {
3813             ok : "OK",
3814             cancel : "Cancel",
3815             yes : "Yes",
3816             no : "No"
3817         }
3818     };
3819 }();
3820
3821 /**
3822  * Shorthand for {@link Roo.MessageBox}
3823  */
3824 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3825 Roo.Msg = Roo.Msg || Roo.MessageBox;
3826 /*
3827  * - LGPL
3828  *
3829  * navbar
3830  * 
3831  */
3832
3833 /**
3834  * @class Roo.bootstrap.Navbar
3835  * @extends Roo.bootstrap.Component
3836  * Bootstrap Navbar class
3837
3838  * @constructor
3839  * Create a new Navbar
3840  * @param {Object} config The config object
3841  */
3842
3843
3844 Roo.bootstrap.Navbar = function(config){
3845     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3846     this.addEvents({
3847         // raw events
3848         /**
3849          * @event beforetoggle
3850          * Fire before toggle the menu
3851          * @param {Roo.EventObject} e
3852          */
3853         "beforetoggle" : true
3854     });
3855 };
3856
3857 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3858     
3859     
3860    
3861     // private
3862     navItems : false,
3863     loadMask : false,
3864     
3865     
3866     getAutoCreate : function(){
3867         
3868         
3869         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3870         
3871     },
3872     
3873     initEvents :function ()
3874     {
3875         //Roo.log(this.el.select('.navbar-toggle',true));
3876         this.el.select('.navbar-toggle',true).on('click', function() {
3877             if(this.fireEvent('beforetoggle', this) !== false){
3878                 var ce = this.el.select('.navbar-collapse',true).first();
3879                 ce.toggleClass('in'); // old...
3880                 if (ce.hasClass('collapse')) {
3881                     // show it...
3882                     ce.removeClass('collapse');
3883                     ce.addClass('show');
3884                     var h = ce.getHeight();
3885                     Roo.log(h);
3886                     ce.removeClass('show');
3887                     // at this point we should be able to see it..
3888                     ce.addClass('collapsing');
3889                     
3890                     ce.setHeight(0); // resize it ...
3891                     ce.on('transitionend', function() {
3892                         Roo.log('done transition');
3893                         ce.removeClass('collapsing');
3894                         ce.addClass('show');
3895                         ce.removeClass('collapse');
3896
3897                         ce.dom.style.height = '';
3898                     }, this, { single: true} );
3899                     ce.setHeight(h);
3900                     
3901                 } else {
3902                     ce.setHeight(ce.getHeight());
3903                     ce.removeClass('show');
3904                     ce.addClass('collapsing');
3905                     
3906                     ce.on('transitionend', function() {
3907                         ce.dom.style.height = '';
3908                         ce.removeClass('collapsing');
3909                         ce.addClass('collapse');
3910                     }, this, { single: true} );
3911                     ce.setHeight(0);
3912                 }
3913             }
3914             
3915         }, this);
3916         
3917         var mark = {
3918             tag: "div",
3919             cls:"x-dlg-mask"
3920         };
3921         
3922         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3923         
3924         var size = this.el.getSize();
3925         this.maskEl.setSize(size.width, size.height);
3926         this.maskEl.enableDisplayMode("block");
3927         this.maskEl.hide();
3928         
3929         if(this.loadMask){
3930             this.maskEl.show();
3931         }
3932     },
3933     
3934     
3935     getChildContainer : function()
3936     {
3937         if (this.el.select('.collapse').getCount()) {
3938             return this.el.select('.collapse',true).first();
3939         }
3940         
3941         return this.el;
3942     },
3943     
3944     mask : function()
3945     {
3946         this.maskEl.show();
3947     },
3948     
3949     unmask : function()
3950     {
3951         this.maskEl.hide();
3952     } 
3953     
3954     
3955     
3956     
3957 });
3958
3959
3960
3961  
3962
3963  /*
3964  * - LGPL
3965  *
3966  * navbar
3967  * 
3968  */
3969
3970 /**
3971  * @class Roo.bootstrap.NavSimplebar
3972  * @extends Roo.bootstrap.Navbar
3973  * Bootstrap Sidebar class
3974  *
3975  * @cfg {Boolean} inverse is inverted color
3976  * 
3977  * @cfg {String} type (nav | pills | tabs)
3978  * @cfg {Boolean} arrangement stacked | justified
3979  * @cfg {String} align (left | right) alignment
3980  * 
3981  * @cfg {Boolean} main (true|false) main nav bar? default false
3982  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3983  * 
3984  * @cfg {String} tag (header|footer|nav|div) default is nav 
3985
3986  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3987  * 
3988  * 
3989  * @constructor
3990  * Create a new Sidebar
3991  * @param {Object} config The config object
3992  */
3993
3994
3995 Roo.bootstrap.NavSimplebar = function(config){
3996     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3997 };
3998
3999 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4000     
4001     inverse: false,
4002     
4003     type: false,
4004     arrangement: '',
4005     align : false,
4006     
4007     weight : 'light',
4008     
4009     main : false,
4010     
4011     
4012     tag : false,
4013     
4014     
4015     getAutoCreate : function(){
4016         
4017         
4018         var cfg = {
4019             tag : this.tag || 'div',
4020             cls : 'navbar navbar-expand-lg'
4021         };
4022         if (['light','white'].indexOf(this.weight) > -1) {
4023             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4024         }
4025         cfg.cls += ' bg-' + this.weight;
4026         
4027         if (this.inverse) {
4028             cfg.cls += ' navbar-inverse';
4029             
4030         }
4031         
4032         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4033         
4034         if (Roo.bootstrap.version == 4) {
4035             return cfg;
4036         }
4037         
4038         cfg.cn = [
4039             {
4040                 cls: 'nav',
4041                 tag : 'ul'
4042             }
4043         ];
4044         
4045          
4046         this.type = this.type || 'nav';
4047         if (['tabs','pills'].indexOf(this.type)!==-1) {
4048             cfg.cn[0].cls += ' nav-' + this.type
4049         
4050         
4051         } else {
4052             if (this.type!=='nav') {
4053                 Roo.log('nav type must be nav/tabs/pills')
4054             }
4055             cfg.cn[0].cls += ' navbar-nav'
4056         }
4057         
4058         
4059         
4060         
4061         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4062             cfg.cn[0].cls += ' nav-' + this.arrangement;
4063         }
4064         
4065         
4066         if (this.align === 'right') {
4067             cfg.cn[0].cls += ' navbar-right';
4068         }
4069         
4070         
4071         
4072         
4073         return cfg;
4074     
4075         
4076     }
4077     
4078     
4079     
4080 });
4081
4082
4083
4084  
4085
4086  
4087        /*
4088  * - LGPL
4089  *
4090  * navbar
4091  * navbar-fixed-top
4092  * navbar-expand-md  fixed-top 
4093  */
4094
4095 /**
4096  * @class Roo.bootstrap.NavHeaderbar
4097  * @extends Roo.bootstrap.NavSimplebar
4098  * Bootstrap Sidebar class
4099  *
4100  * @cfg {String} brand what is brand
4101  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4102  * @cfg {String} brand_href href of the brand
4103  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4104  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4105  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4106  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4107  * 
4108  * @constructor
4109  * Create a new Sidebar
4110  * @param {Object} config The config object
4111  */
4112
4113
4114 Roo.bootstrap.NavHeaderbar = function(config){
4115     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4116       
4117 };
4118
4119 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4120     
4121     position: '',
4122     brand: '',
4123     brand_href: false,
4124     srButton : true,
4125     autohide : false,
4126     desktopCenter : false,
4127    
4128     
4129     getAutoCreate : function(){
4130         
4131         var   cfg = {
4132             tag: this.nav || 'nav',
4133             cls: 'navbar navbar-expand-md',
4134             role: 'navigation',
4135             cn: []
4136         };
4137         
4138         var cn = cfg.cn;
4139         if (this.desktopCenter) {
4140             cn.push({cls : 'container', cn : []});
4141             cn = cn[0].cn;
4142         }
4143         
4144         if(this.srButton){
4145             var btn = {
4146                 tag: 'button',
4147                 type: 'button',
4148                 cls: 'navbar-toggle navbar-toggler',
4149                 'data-toggle': 'collapse',
4150                 cn: [
4151                     {
4152                         tag: 'span',
4153                         cls: 'sr-only',
4154                         html: 'Toggle navigation'
4155                     },
4156                     {
4157                         tag: 'span',
4158                         cls: 'icon-bar navbar-toggler-icon'
4159                     },
4160                     {
4161                         tag: 'span',
4162                         cls: 'icon-bar'
4163                     },
4164                     {
4165                         tag: 'span',
4166                         cls: 'icon-bar'
4167                     }
4168                 ]
4169             };
4170             
4171             cn.push( Roo.bootstrap.version == 4 ? btn : {
4172                 tag: 'div',
4173                 cls: 'navbar-header',
4174                 cn: [
4175                     btn
4176                 ]
4177             });
4178         }
4179         
4180         cn.push({
4181             tag: 'div',
4182             cls: 'collapse navbar-collapse',
4183             cn : []
4184         });
4185         
4186         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4187         
4188         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4189             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4190             
4191             // tag can override this..
4192             
4193             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4194         }
4195         
4196         if (this.brand !== '') {
4197             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4198             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4199                 tag: 'a',
4200                 href: this.brand_href ? this.brand_href : '#',
4201                 cls: 'navbar-brand',
4202                 cn: [
4203                 this.brand
4204                 ]
4205             });
4206         }
4207         
4208         if(this.main){
4209             cfg.cls += ' main-nav';
4210         }
4211         
4212         
4213         return cfg;
4214
4215         
4216     },
4217     getHeaderChildContainer : function()
4218     {
4219         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4220             return this.el.select('.navbar-header',true).first();
4221         }
4222         
4223         return this.getChildContainer();
4224     },
4225     
4226     
4227     initEvents : function()
4228     {
4229         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4230         
4231         if (this.autohide) {
4232             
4233             var prevScroll = 0;
4234             var ft = this.el;
4235             
4236             Roo.get(document).on('scroll',function(e) {
4237                 var ns = Roo.get(document).getScroll().top;
4238                 var os = prevScroll;
4239                 prevScroll = ns;
4240                 
4241                 if(ns > os){
4242                     ft.removeClass('slideDown');
4243                     ft.addClass('slideUp');
4244                     return;
4245                 }
4246                 ft.removeClass('slideUp');
4247                 ft.addClass('slideDown');
4248                  
4249               
4250           },this);
4251         }
4252     }    
4253     
4254 });
4255
4256
4257
4258  
4259
4260  /*
4261  * - LGPL
4262  *
4263  * navbar
4264  * 
4265  */
4266
4267 /**
4268  * @class Roo.bootstrap.NavSidebar
4269  * @extends Roo.bootstrap.Navbar
4270  * Bootstrap Sidebar class
4271  * 
4272  * @constructor
4273  * Create a new Sidebar
4274  * @param {Object} config The config object
4275  */
4276
4277
4278 Roo.bootstrap.NavSidebar = function(config){
4279     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4280 };
4281
4282 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4283     
4284     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4285     
4286     getAutoCreate : function(){
4287         
4288         
4289         return  {
4290             tag: 'div',
4291             cls: 'sidebar sidebar-nav'
4292         };
4293     
4294         
4295     }
4296     
4297     
4298     
4299 });
4300
4301
4302
4303  
4304
4305  /*
4306  * - LGPL
4307  *
4308  * nav group
4309  * 
4310  */
4311
4312 /**
4313  * @class Roo.bootstrap.NavGroup
4314  * @extends Roo.bootstrap.Component
4315  * Bootstrap NavGroup class
4316  * @cfg {String} align (left|right)
4317  * @cfg {Boolean} inverse
4318  * @cfg {String} type (nav|pills|tab) default nav
4319  * @cfg {String} navId - reference Id for navbar.
4320
4321  * 
4322  * @constructor
4323  * Create a new nav group
4324  * @param {Object} config The config object
4325  */
4326
4327 Roo.bootstrap.NavGroup = function(config){
4328     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4329     this.navItems = [];
4330    
4331     Roo.bootstrap.NavGroup.register(this);
4332      this.addEvents({
4333         /**
4334              * @event changed
4335              * Fires when the active item changes
4336              * @param {Roo.bootstrap.NavGroup} this
4337              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4338              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4339          */
4340         'changed': true
4341      });
4342     
4343 };
4344
4345 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4346     
4347     align: '',
4348     inverse: false,
4349     form: false,
4350     type: 'nav',
4351     navId : '',
4352     // private
4353     
4354     navItems : false, 
4355     
4356     getAutoCreate : function()
4357     {
4358         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4359         
4360         cfg = {
4361             tag : 'ul',
4362             cls: 'nav' 
4363         };
4364         if (Roo.bootstrap.version == 4) {
4365             if (this.type == 'pills') {
4366                 cfg.cls = ' nav-pills';
4367             }
4368         } else {
4369             if (['tabs','pills'].indexOf(this.type)!==-1) {
4370                 cfg.cls += ' nav-' + this.type
4371             } else {
4372                 if (this.type !== 'nav') {
4373                     Roo.log('nav type must be nav/tabs/pills')
4374                 }
4375                 cfg.cls += ' navbar-nav'
4376             }
4377         }
4378         
4379         if (this.parent() && this.parent().sidebar) {
4380             cfg = {
4381                 tag: 'ul',
4382                 cls: 'dashboard-menu sidebar-menu'
4383             };
4384             
4385             return cfg;
4386         }
4387         
4388         if (this.form === true) {
4389             cfg = {
4390                 tag: 'form',
4391                 cls: 'navbar-form form-inline'
4392             };
4393             
4394             if (this.align === 'right') {
4395                 cfg.cls += ' navbar-right ml-md-auto';
4396             } else {
4397                 cfg.cls += ' navbar-left';
4398             }
4399         }
4400         
4401         if (this.align === 'right') {
4402             cfg.cls += ' navbar-right ml-md-auto';
4403         } else {
4404             cfg.cls += ' mr-auto';
4405         }
4406         
4407         if (this.inverse) {
4408             cfg.cls += ' navbar-inverse';
4409             
4410         }
4411         
4412         
4413         return cfg;
4414     },
4415     /**
4416     * sets the active Navigation item
4417     * @param {Roo.bootstrap.NavItem} the new current navitem
4418     */
4419     setActiveItem : function(item)
4420     {
4421         var prev = false;
4422         Roo.each(this.navItems, function(v){
4423             if (v == item) {
4424                 return ;
4425             }
4426             if (v.isActive()) {
4427                 v.setActive(false, true);
4428                 prev = v;
4429                 
4430             }
4431             
4432         });
4433
4434         item.setActive(true, true);
4435         this.fireEvent('changed', this, item, prev);
4436         
4437         
4438     },
4439     /**
4440     * gets the active Navigation item
4441     * @return {Roo.bootstrap.NavItem} the current navitem
4442     */
4443     getActive : function()
4444     {
4445         
4446         var prev = false;
4447         Roo.each(this.navItems, function(v){
4448             
4449             if (v.isActive()) {
4450                 prev = v;
4451                 
4452             }
4453             
4454         });
4455         return prev;
4456     },
4457     
4458     indexOfNav : function()
4459     {
4460         
4461         var prev = false;
4462         Roo.each(this.navItems, function(v,i){
4463             
4464             if (v.isActive()) {
4465                 prev = i;
4466                 
4467             }
4468             
4469         });
4470         return prev;
4471     },
4472     /**
4473     * adds a Navigation item
4474     * @param {Roo.bootstrap.NavItem} the navitem to add
4475     */
4476     addItem : function(cfg)
4477     {
4478         if (this.form && Roo.bootstrap.version == 4) {
4479             cfg.tag = 'div';
4480         }
4481         var cn = new Roo.bootstrap.NavItem(cfg);
4482         this.register(cn);
4483         cn.parentId = this.id;
4484         cn.onRender(this.el, null);
4485         return cn;
4486     },
4487     /**
4488     * register a Navigation item
4489     * @param {Roo.bootstrap.NavItem} the navitem to add
4490     */
4491     register : function(item)
4492     {
4493         this.navItems.push( item);
4494         item.navId = this.navId;
4495     
4496     },
4497     
4498     /**
4499     * clear all the Navigation item
4500     */
4501    
4502     clearAll : function()
4503     {
4504         this.navItems = [];
4505         this.el.dom.innerHTML = '';
4506     },
4507     
4508     getNavItem: function(tabId)
4509     {
4510         var ret = false;
4511         Roo.each(this.navItems, function(e) {
4512             if (e.tabId == tabId) {
4513                ret =  e;
4514                return false;
4515             }
4516             return true;
4517             
4518         });
4519         return ret;
4520     },
4521     
4522     setActiveNext : function()
4523     {
4524         var i = this.indexOfNav(this.getActive());
4525         if (i > this.navItems.length) {
4526             return;
4527         }
4528         this.setActiveItem(this.navItems[i+1]);
4529     },
4530     setActivePrev : function()
4531     {
4532         var i = this.indexOfNav(this.getActive());
4533         if (i  < 1) {
4534             return;
4535         }
4536         this.setActiveItem(this.navItems[i-1]);
4537     },
4538     clearWasActive : function(except) {
4539         Roo.each(this.navItems, function(e) {
4540             if (e.tabId != except.tabId && e.was_active) {
4541                e.was_active = false;
4542                return false;
4543             }
4544             return true;
4545             
4546         });
4547     },
4548     getWasActive : function ()
4549     {
4550         var r = false;
4551         Roo.each(this.navItems, function(e) {
4552             if (e.was_active) {
4553                r = e;
4554                return false;
4555             }
4556             return true;
4557             
4558         });
4559         return r;
4560     }
4561     
4562     
4563 });
4564
4565  
4566 Roo.apply(Roo.bootstrap.NavGroup, {
4567     
4568     groups: {},
4569      /**
4570     * register a Navigation Group
4571     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4572     */
4573     register : function(navgrp)
4574     {
4575         this.groups[navgrp.navId] = navgrp;
4576         
4577     },
4578     /**
4579     * fetch a Navigation Group based on the navigation ID
4580     * @param {string} the navgroup to add
4581     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4582     */
4583     get: function(navId) {
4584         if (typeof(this.groups[navId]) == 'undefined') {
4585             return false;
4586             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4587         }
4588         return this.groups[navId] ;
4589     }
4590     
4591     
4592     
4593 });
4594
4595  /*
4596  * - LGPL
4597  *
4598  * row
4599  * 
4600  */
4601
4602 /**
4603  * @class Roo.bootstrap.NavItem
4604  * @extends Roo.bootstrap.Component
4605  * Bootstrap Navbar.NavItem class
4606  * @cfg {String} href  link to
4607  * @cfg {String} html content of button
4608  * @cfg {String} badge text inside badge
4609  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4610  * @cfg {String} glyphicon DEPRICATED - use fa
4611  * @cfg {String} icon DEPRICATED - use fa
4612  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4613  * @cfg {Boolean} active Is item active
4614  * @cfg {Boolean} disabled Is item disabled
4615  
4616  * @cfg {Boolean} preventDefault (true | false) default false
4617  * @cfg {String} tabId the tab that this item activates.
4618  * @cfg {String} tagtype (a|span) render as a href or span?
4619  * @cfg {Boolean} animateRef (true|false) link to element default false  
4620   
4621  * @constructor
4622  * Create a new Navbar Item
4623  * @param {Object} config The config object
4624  */
4625 Roo.bootstrap.NavItem = function(config){
4626     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4627     this.addEvents({
4628         // raw events
4629         /**
4630          * @event click
4631          * The raw click event for the entire grid.
4632          * @param {Roo.EventObject} e
4633          */
4634         "click" : true,
4635          /**
4636             * @event changed
4637             * Fires when the active item active state changes
4638             * @param {Roo.bootstrap.NavItem} this
4639             * @param {boolean} state the new state
4640              
4641          */
4642         'changed': true,
4643         /**
4644             * @event scrollto
4645             * Fires when scroll to element
4646             * @param {Roo.bootstrap.NavItem} this
4647             * @param {Object} options
4648             * @param {Roo.EventObject} e
4649              
4650          */
4651         'scrollto': true
4652     });
4653    
4654 };
4655
4656 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4657     
4658     href: false,
4659     html: '',
4660     badge: '',
4661     icon: false,
4662     fa : false,
4663     glyphicon: false,
4664     active: false,
4665     preventDefault : false,
4666     tabId : false,
4667     tagtype : 'a',
4668     tag: 'li',
4669     disabled : false,
4670     animateRef : false,
4671     was_active : false,
4672     
4673     getAutoCreate : function(){
4674          
4675         var cfg = {
4676             tag: this.tag,
4677             cls: 'nav-item'
4678             
4679         };
4680         
4681         if (this.active) {
4682             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4683         }
4684         if (this.disabled) {
4685             cfg.cls += ' disabled';
4686         }
4687         
4688         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4689             cfg.cn = [
4690                 {
4691                     tag: this.tagtype,
4692                     href : this.href || "#",
4693                     html: this.html || ''
4694                 }
4695             ];
4696             if (this.tagtype == 'a') {
4697                 cfg.cn[0].cls = 'nav-link';
4698             }
4699             if (this.icon) {
4700                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4701             }
4702             if (this.fa) {
4703                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4704             }
4705             if(this.glyphicon) {
4706                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4707             }
4708             
4709             if (this.menu) {
4710                 
4711                 cfg.cn[0].html += " <span class='caret'></span>";
4712              
4713             }
4714             
4715             if (this.badge !== '') {
4716                  
4717                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4718             }
4719         }
4720         
4721         
4722         
4723         return cfg;
4724     },
4725     onRender : function(ct, position)
4726     {
4727        // Roo.log("Call onRender: " + this.xtype);
4728         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4729             this.tag = 'div';
4730         }
4731         
4732         return Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4733     },
4734       
4735     
4736     initEvents: function() 
4737     {
4738         if (typeof (this.menu) != 'undefined') {
4739             this.menu.parentType = this.xtype;
4740             this.menu.triggerEl = this.el;
4741             this.menu = this.addxtype(Roo.apply({}, this.menu));
4742         }
4743         
4744         this.el.select('a',true).on('click', this.onClick, this);
4745         
4746         if(this.tagtype == 'span'){
4747             this.el.select('span',true).on('click', this.onClick, this);
4748         }
4749        
4750         // at this point parent should be available..
4751         this.parent().register(this);
4752     },
4753     
4754     onClick : function(e)
4755     {
4756         if (e.getTarget('.dropdown-menu-item')) {
4757             // did you click on a menu itemm.... - then don't trigger onclick..
4758             return;
4759         }
4760         
4761         if(
4762                 this.preventDefault || 
4763                 this.href == '#' 
4764         ){
4765             Roo.log("NavItem - prevent Default?");
4766             e.preventDefault();
4767         }
4768         
4769         if (this.disabled) {
4770             return;
4771         }
4772         
4773         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4774         if (tg && tg.transition) {
4775             Roo.log("waiting for the transitionend");
4776             return;
4777         }
4778         
4779         
4780         
4781         //Roo.log("fire event clicked");
4782         if(this.fireEvent('click', this, e) === false){
4783             return;
4784         };
4785         
4786         if(this.tagtype == 'span'){
4787             return;
4788         }
4789         
4790         //Roo.log(this.href);
4791         var ael = this.el.select('a',true).first();
4792         //Roo.log(ael);
4793         
4794         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4795             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4796             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4797                 return; // ignore... - it's a 'hash' to another page.
4798             }
4799             Roo.log("NavItem - prevent Default?");
4800             e.preventDefault();
4801             this.scrollToElement(e);
4802         }
4803         
4804         
4805         var p =  this.parent();
4806    
4807         if (['tabs','pills'].indexOf(p.type)!==-1) {
4808             if (typeof(p.setActiveItem) !== 'undefined') {
4809                 p.setActiveItem(this);
4810             }
4811         }
4812         
4813         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4814         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4815             // remove the collapsed menu expand...
4816             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4817         }
4818     },
4819     
4820     isActive: function () {
4821         return this.active
4822     },
4823     setActive : function(state, fire, is_was_active)
4824     {
4825         if (this.active && !state && this.navId) {
4826             this.was_active = true;
4827             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4828             if (nv) {
4829                 nv.clearWasActive(this);
4830             }
4831             
4832         }
4833         this.active = state;
4834         
4835         if (!state ) {
4836             this.el.removeClass('active');
4837         } else if (!this.el.hasClass('active')) {
4838             this.el.addClass('active');
4839         }
4840         if (fire) {
4841             this.fireEvent('changed', this, state);
4842         }
4843         
4844         // show a panel if it's registered and related..
4845         
4846         if (!this.navId || !this.tabId || !state || is_was_active) {
4847             return;
4848         }
4849         
4850         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4851         if (!tg) {
4852             return;
4853         }
4854         var pan = tg.getPanelByName(this.tabId);
4855         if (!pan) {
4856             return;
4857         }
4858         // if we can not flip to new panel - go back to old nav highlight..
4859         if (false == tg.showPanel(pan)) {
4860             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4861             if (nv) {
4862                 var onav = nv.getWasActive();
4863                 if (onav) {
4864                     onav.setActive(true, false, true);
4865                 }
4866             }
4867             
4868         }
4869         
4870         
4871         
4872     },
4873      // this should not be here...
4874     setDisabled : function(state)
4875     {
4876         this.disabled = state;
4877         if (!state ) {
4878             this.el.removeClass('disabled');
4879         } else if (!this.el.hasClass('disabled')) {
4880             this.el.addClass('disabled');
4881         }
4882         
4883     },
4884     
4885     /**
4886      * Fetch the element to display the tooltip on.
4887      * @return {Roo.Element} defaults to this.el
4888      */
4889     tooltipEl : function()
4890     {
4891         return this.el.select('' + this.tagtype + '', true).first();
4892     },
4893     
4894     scrollToElement : function(e)
4895     {
4896         var c = document.body;
4897         
4898         /*
4899          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4900          */
4901         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4902             c = document.documentElement;
4903         }
4904         
4905         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4906         
4907         if(!target){
4908             return;
4909         }
4910
4911         var o = target.calcOffsetsTo(c);
4912         
4913         var options = {
4914             target : target,
4915             value : o[1]
4916         };
4917         
4918         this.fireEvent('scrollto', this, options, e);
4919         
4920         Roo.get(c).scrollTo('top', options.value, true);
4921         
4922         return;
4923     }
4924 });
4925  
4926
4927  /*
4928  * - LGPL
4929  *
4930  * sidebar item
4931  *
4932  *  li
4933  *    <span> icon </span>
4934  *    <span> text </span>
4935  *    <span>badge </span>
4936  */
4937
4938 /**
4939  * @class Roo.bootstrap.NavSidebarItem
4940  * @extends Roo.bootstrap.NavItem
4941  * Bootstrap Navbar.NavSidebarItem class
4942  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4943  * {Boolean} open is the menu open
4944  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4945  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4946  * {String} buttonSize (sm|md|lg)the extra classes for the button
4947  * {Boolean} showArrow show arrow next to the text (default true)
4948  * @constructor
4949  * Create a new Navbar Button
4950  * @param {Object} config The config object
4951  */
4952 Roo.bootstrap.NavSidebarItem = function(config){
4953     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4954     this.addEvents({
4955         // raw events
4956         /**
4957          * @event click
4958          * The raw click event for the entire grid.
4959          * @param {Roo.EventObject} e
4960          */
4961         "click" : true,
4962          /**
4963             * @event changed
4964             * Fires when the active item active state changes
4965             * @param {Roo.bootstrap.NavSidebarItem} this
4966             * @param {boolean} state the new state
4967              
4968          */
4969         'changed': true
4970     });
4971    
4972 };
4973
4974 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4975     
4976     badgeWeight : 'default',
4977     
4978     open: false,
4979     
4980     buttonView : false,
4981     
4982     buttonWeight : 'default',
4983     
4984     buttonSize : 'md',
4985     
4986     showArrow : true,
4987     
4988     getAutoCreate : function(){
4989         
4990         
4991         var a = {
4992                 tag: 'a',
4993                 href : this.href || '#',
4994                 cls: '',
4995                 html : '',
4996                 cn : []
4997         };
4998         
4999         if(this.buttonView){
5000             a = {
5001                 tag: 'button',
5002                 href : this.href || '#',
5003                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5004                 html : this.html,
5005                 cn : []
5006             };
5007         }
5008         
5009         var cfg = {
5010             tag: 'li',
5011             cls: '',
5012             cn: [ a ]
5013         };
5014         
5015         if (this.active) {
5016             cfg.cls += ' active';
5017         }
5018         
5019         if (this.disabled) {
5020             cfg.cls += ' disabled';
5021         }
5022         if (this.open) {
5023             cfg.cls += ' open x-open';
5024         }
5025         // left icon..
5026         if (this.glyphicon || this.icon) {
5027             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5028             a.cn.push({ tag : 'i', cls : c }) ;
5029         }
5030         
5031         if(!this.buttonView){
5032             var span = {
5033                 tag: 'span',
5034                 html : this.html || ''
5035             };
5036
5037             a.cn.push(span);
5038             
5039         }
5040         
5041         if (this.badge !== '') {
5042             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5043         }
5044         
5045         if (this.menu) {
5046             
5047             if(this.showArrow){
5048                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5049             }
5050             
5051             a.cls += ' dropdown-toggle treeview' ;
5052         }
5053         
5054         return cfg;
5055     },
5056     
5057     initEvents : function()
5058     { 
5059         if (typeof (this.menu) != 'undefined') {
5060             this.menu.parentType = this.xtype;
5061             this.menu.triggerEl = this.el;
5062             this.menu = this.addxtype(Roo.apply({}, this.menu));
5063         }
5064         
5065         this.el.on('click', this.onClick, this);
5066         
5067         if(this.badge !== ''){
5068             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5069         }
5070         
5071     },
5072     
5073     onClick : function(e)
5074     {
5075         if(this.disabled){
5076             e.preventDefault();
5077             return;
5078         }
5079         
5080         if(this.preventDefault){
5081             e.preventDefault();
5082         }
5083         
5084         this.fireEvent('click', this);
5085     },
5086     
5087     disable : function()
5088     {
5089         this.setDisabled(true);
5090     },
5091     
5092     enable : function()
5093     {
5094         this.setDisabled(false);
5095     },
5096     
5097     setDisabled : function(state)
5098     {
5099         if(this.disabled == state){
5100             return;
5101         }
5102         
5103         this.disabled = state;
5104         
5105         if (state) {
5106             this.el.addClass('disabled');
5107             return;
5108         }
5109         
5110         this.el.removeClass('disabled');
5111         
5112         return;
5113     },
5114     
5115     setActive : function(state)
5116     {
5117         if(this.active == state){
5118             return;
5119         }
5120         
5121         this.active = state;
5122         
5123         if (state) {
5124             this.el.addClass('active');
5125             return;
5126         }
5127         
5128         this.el.removeClass('active');
5129         
5130         return;
5131     },
5132     
5133     isActive: function () 
5134     {
5135         return this.active;
5136     },
5137     
5138     setBadge : function(str)
5139     {
5140         if(!this.badgeEl){
5141             return;
5142         }
5143         
5144         this.badgeEl.dom.innerHTML = str;
5145     }
5146     
5147    
5148      
5149  
5150 });
5151  
5152
5153  /*
5154  * - LGPL
5155  *
5156  * row
5157  * 
5158  */
5159
5160 /**
5161  * @class Roo.bootstrap.Row
5162  * @extends Roo.bootstrap.Component
5163  * Bootstrap Row class (contains columns...)
5164  * 
5165  * @constructor
5166  * Create a new Row
5167  * @param {Object} config The config object
5168  */
5169
5170 Roo.bootstrap.Row = function(config){
5171     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5172 };
5173
5174 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5175     
5176     getAutoCreate : function(){
5177        return {
5178             cls: 'row clearfix'
5179        };
5180     }
5181     
5182     
5183 });
5184
5185  
5186
5187  /*
5188  * - LGPL
5189  *
5190  * element
5191  * 
5192  */
5193
5194 /**
5195  * @class Roo.bootstrap.Element
5196  * @extends Roo.bootstrap.Component
5197  * Bootstrap Element class
5198  * @cfg {String} html contents of the element
5199  * @cfg {String} tag tag of the element
5200  * @cfg {String} cls class of the element
5201  * @cfg {Boolean} preventDefault (true|false) default false
5202  * @cfg {Boolean} clickable (true|false) default false
5203  * 
5204  * @constructor
5205  * Create a new Element
5206  * @param {Object} config The config object
5207  */
5208
5209 Roo.bootstrap.Element = function(config){
5210     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5211     
5212     this.addEvents({
5213         // raw events
5214         /**
5215          * @event click
5216          * When a element is chick
5217          * @param {Roo.bootstrap.Element} this
5218          * @param {Roo.EventObject} e
5219          */
5220         "click" : true
5221     });
5222 };
5223
5224 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5225     
5226     tag: 'div',
5227     cls: '',
5228     html: '',
5229     preventDefault: false, 
5230     clickable: false,
5231     
5232     getAutoCreate : function(){
5233         
5234         var cfg = {
5235             tag: this.tag,
5236             // cls: this.cls, double assign in parent class Component.js :: onRender
5237             html: this.html
5238         };
5239         
5240         return cfg;
5241     },
5242     
5243     initEvents: function() 
5244     {
5245         Roo.bootstrap.Element.superclass.initEvents.call(this);
5246         
5247         if(this.clickable){
5248             this.el.on('click', this.onClick, this);
5249         }
5250         
5251     },
5252     
5253     onClick : function(e)
5254     {
5255         if(this.preventDefault){
5256             e.preventDefault();
5257         }
5258         
5259         this.fireEvent('click', this, e);
5260     },
5261     
5262     getValue : function()
5263     {
5264         return this.el.dom.innerHTML;
5265     },
5266     
5267     setValue : function(value)
5268     {
5269         this.el.dom.innerHTML = value;
5270     }
5271    
5272 });
5273
5274  
5275
5276  /*
5277  * - LGPL
5278  *
5279  * pagination
5280  * 
5281  */
5282
5283 /**
5284  * @class Roo.bootstrap.Pagination
5285  * @extends Roo.bootstrap.Component
5286  * Bootstrap Pagination class
5287  * @cfg {String} size xs | sm | md | lg
5288  * @cfg {Boolean} inverse false | true
5289  * 
5290  * @constructor
5291  * Create a new Pagination
5292  * @param {Object} config The config object
5293  */
5294
5295 Roo.bootstrap.Pagination = function(config){
5296     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5297 };
5298
5299 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5300     
5301     cls: false,
5302     size: false,
5303     inverse: false,
5304     
5305     getAutoCreate : function(){
5306         var cfg = {
5307             tag: 'ul',
5308                 cls: 'pagination'
5309         };
5310         if (this.inverse) {
5311             cfg.cls += ' inverse';
5312         }
5313         if (this.html) {
5314             cfg.html=this.html;
5315         }
5316         if (this.cls) {
5317             cfg.cls += " " + this.cls;
5318         }
5319         return cfg;
5320     }
5321    
5322 });
5323
5324  
5325
5326  /*
5327  * - LGPL
5328  *
5329  * Pagination item
5330  * 
5331  */
5332
5333
5334 /**
5335  * @class Roo.bootstrap.PaginationItem
5336  * @extends Roo.bootstrap.Component
5337  * Bootstrap PaginationItem class
5338  * @cfg {String} html text
5339  * @cfg {String} href the link
5340  * @cfg {Boolean} preventDefault (true | false) default true
5341  * @cfg {Boolean} active (true | false) default false
5342  * @cfg {Boolean} disabled default false
5343  * 
5344  * 
5345  * @constructor
5346  * Create a new PaginationItem
5347  * @param {Object} config The config object
5348  */
5349
5350
5351 Roo.bootstrap.PaginationItem = function(config){
5352     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5353     this.addEvents({
5354         // raw events
5355         /**
5356          * @event click
5357          * The raw click event for the entire grid.
5358          * @param {Roo.EventObject} e
5359          */
5360         "click" : true
5361     });
5362 };
5363
5364 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5365     
5366     href : false,
5367     html : false,
5368     preventDefault: true,
5369     active : false,
5370     cls : false,
5371     disabled: false,
5372     
5373     getAutoCreate : function(){
5374         var cfg= {
5375             tag: 'li',
5376             cn: [
5377                 {
5378                     tag : 'a',
5379                     href : this.href ? this.href : '#',
5380                     html : this.html ? this.html : ''
5381                 }
5382             ]
5383         };
5384         
5385         if(this.cls){
5386             cfg.cls = this.cls;
5387         }
5388         
5389         if(this.disabled){
5390             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5391         }
5392         
5393         if(this.active){
5394             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5395         }
5396         
5397         return cfg;
5398     },
5399     
5400     initEvents: function() {
5401         
5402         this.el.on('click', this.onClick, this);
5403         
5404     },
5405     onClick : function(e)
5406     {
5407         Roo.log('PaginationItem on click ');
5408         if(this.preventDefault){
5409             e.preventDefault();
5410         }
5411         
5412         if(this.disabled){
5413             return;
5414         }
5415         
5416         this.fireEvent('click', this, e);
5417     }
5418    
5419 });
5420
5421  
5422
5423  /*
5424  * - LGPL
5425  *
5426  * slider
5427  * 
5428  */
5429
5430
5431 /**
5432  * @class Roo.bootstrap.Slider
5433  * @extends Roo.bootstrap.Component
5434  * Bootstrap Slider class
5435  *    
5436  * @constructor
5437  * Create a new Slider
5438  * @param {Object} config The config object
5439  */
5440
5441 Roo.bootstrap.Slider = function(config){
5442     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5443 };
5444
5445 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5446     
5447     getAutoCreate : function(){
5448         
5449         var cfg = {
5450             tag: 'div',
5451             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5452             cn: [
5453                 {
5454                     tag: 'a',
5455                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5456                 }
5457             ]
5458         };
5459         
5460         return cfg;
5461     }
5462    
5463 });
5464
5465  /*
5466  * Based on:
5467  * Ext JS Library 1.1.1
5468  * Copyright(c) 2006-2007, Ext JS, LLC.
5469  *
5470  * Originally Released Under LGPL - original licence link has changed is not relivant.
5471  *
5472  * Fork - LGPL
5473  * <script type="text/javascript">
5474  */
5475  
5476
5477 /**
5478  * @class Roo.grid.ColumnModel
5479  * @extends Roo.util.Observable
5480  * This is the default implementation of a ColumnModel used by the Grid. It defines
5481  * the columns in the grid.
5482  * <br>Usage:<br>
5483  <pre><code>
5484  var colModel = new Roo.grid.ColumnModel([
5485         {header: "Ticker", width: 60, sortable: true, locked: true},
5486         {header: "Company Name", width: 150, sortable: true},
5487         {header: "Market Cap.", width: 100, sortable: true},
5488         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5489         {header: "Employees", width: 100, sortable: true, resizable: false}
5490  ]);
5491  </code></pre>
5492  * <p>
5493  
5494  * The config options listed for this class are options which may appear in each
5495  * individual column definition.
5496  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5497  * @constructor
5498  * @param {Object} config An Array of column config objects. See this class's
5499  * config objects for details.
5500 */
5501 Roo.grid.ColumnModel = function(config){
5502         /**
5503      * The config passed into the constructor
5504      */
5505     this.config = config;
5506     this.lookup = {};
5507
5508     // if no id, create one
5509     // if the column does not have a dataIndex mapping,
5510     // map it to the order it is in the config
5511     for(var i = 0, len = config.length; i < len; i++){
5512         var c = config[i];
5513         if(typeof c.dataIndex == "undefined"){
5514             c.dataIndex = i;
5515         }
5516         if(typeof c.renderer == "string"){
5517             c.renderer = Roo.util.Format[c.renderer];
5518         }
5519         if(typeof c.id == "undefined"){
5520             c.id = Roo.id();
5521         }
5522         if(c.editor && c.editor.xtype){
5523             c.editor  = Roo.factory(c.editor, Roo.grid);
5524         }
5525         if(c.editor && c.editor.isFormField){
5526             c.editor = new Roo.grid.GridEditor(c.editor);
5527         }
5528         this.lookup[c.id] = c;
5529     }
5530
5531     /**
5532      * The width of columns which have no width specified (defaults to 100)
5533      * @type Number
5534      */
5535     this.defaultWidth = 100;
5536
5537     /**
5538      * Default sortable of columns which have no sortable specified (defaults to false)
5539      * @type Boolean
5540      */
5541     this.defaultSortable = false;
5542
5543     this.addEvents({
5544         /**
5545              * @event widthchange
5546              * Fires when the width of a column changes.
5547              * @param {ColumnModel} this
5548              * @param {Number} columnIndex The column index
5549              * @param {Number} newWidth The new width
5550              */
5551             "widthchange": true,
5552         /**
5553              * @event headerchange
5554              * Fires when the text of a header changes.
5555              * @param {ColumnModel} this
5556              * @param {Number} columnIndex The column index
5557              * @param {Number} newText The new header text
5558              */
5559             "headerchange": true,
5560         /**
5561              * @event hiddenchange
5562              * Fires when a column is hidden or "unhidden".
5563              * @param {ColumnModel} this
5564              * @param {Number} columnIndex The column index
5565              * @param {Boolean} hidden true if hidden, false otherwise
5566              */
5567             "hiddenchange": true,
5568             /**
5569          * @event columnmoved
5570          * Fires when a column is moved.
5571          * @param {ColumnModel} this
5572          * @param {Number} oldIndex
5573          * @param {Number} newIndex
5574          */
5575         "columnmoved" : true,
5576         /**
5577          * @event columlockchange
5578          * Fires when a column's locked state is changed
5579          * @param {ColumnModel} this
5580          * @param {Number} colIndex
5581          * @param {Boolean} locked true if locked
5582          */
5583         "columnlockchange" : true
5584     });
5585     Roo.grid.ColumnModel.superclass.constructor.call(this);
5586 };
5587 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5588     /**
5589      * @cfg {String} header The header text to display in the Grid view.
5590      */
5591     /**
5592      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5593      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5594      * specified, the column's index is used as an index into the Record's data Array.
5595      */
5596     /**
5597      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5598      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5599      */
5600     /**
5601      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5602      * Defaults to the value of the {@link #defaultSortable} property.
5603      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5604      */
5605     /**
5606      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5607      */
5608     /**
5609      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5610      */
5611     /**
5612      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5613      */
5614     /**
5615      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5616      */
5617     /**
5618      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5619      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5620      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5621      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5622      */
5623        /**
5624      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5625      */
5626     /**
5627      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5628      */
5629     /**
5630      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5631      */
5632     /**
5633      * @cfg {String} cursor (Optional)
5634      */
5635     /**
5636      * @cfg {String} tooltip (Optional)
5637      */
5638     /**
5639      * @cfg {Number} xs (Optional)
5640      */
5641     /**
5642      * @cfg {Number} sm (Optional)
5643      */
5644     /**
5645      * @cfg {Number} md (Optional)
5646      */
5647     /**
5648      * @cfg {Number} lg (Optional)
5649      */
5650     /**
5651      * Returns the id of the column at the specified index.
5652      * @param {Number} index The column index
5653      * @return {String} the id
5654      */
5655     getColumnId : function(index){
5656         return this.config[index].id;
5657     },
5658
5659     /**
5660      * Returns the column for a specified id.
5661      * @param {String} id The column id
5662      * @return {Object} the column
5663      */
5664     getColumnById : function(id){
5665         return this.lookup[id];
5666     },
5667
5668     
5669     /**
5670      * Returns the column for a specified dataIndex.
5671      * @param {String} dataIndex The column dataIndex
5672      * @return {Object|Boolean} the column or false if not found
5673      */
5674     getColumnByDataIndex: function(dataIndex){
5675         var index = this.findColumnIndex(dataIndex);
5676         return index > -1 ? this.config[index] : false;
5677     },
5678     
5679     /**
5680      * Returns the index for a specified column id.
5681      * @param {String} id The column id
5682      * @return {Number} the index, or -1 if not found
5683      */
5684     getIndexById : function(id){
5685         for(var i = 0, len = this.config.length; i < len; i++){
5686             if(this.config[i].id == id){
5687                 return i;
5688             }
5689         }
5690         return -1;
5691     },
5692     
5693     /**
5694      * Returns the index for a specified column dataIndex.
5695      * @param {String} dataIndex The column dataIndex
5696      * @return {Number} the index, or -1 if not found
5697      */
5698     
5699     findColumnIndex : function(dataIndex){
5700         for(var i = 0, len = this.config.length; i < len; i++){
5701             if(this.config[i].dataIndex == dataIndex){
5702                 return i;
5703             }
5704         }
5705         return -1;
5706     },
5707     
5708     
5709     moveColumn : function(oldIndex, newIndex){
5710         var c = this.config[oldIndex];
5711         this.config.splice(oldIndex, 1);
5712         this.config.splice(newIndex, 0, c);
5713         this.dataMap = null;
5714         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5715     },
5716
5717     isLocked : function(colIndex){
5718         return this.config[colIndex].locked === true;
5719     },
5720
5721     setLocked : function(colIndex, value, suppressEvent){
5722         if(this.isLocked(colIndex) == value){
5723             return;
5724         }
5725         this.config[colIndex].locked = value;
5726         if(!suppressEvent){
5727             this.fireEvent("columnlockchange", this, colIndex, value);
5728         }
5729     },
5730
5731     getTotalLockedWidth : function(){
5732         var totalWidth = 0;
5733         for(var i = 0; i < this.config.length; i++){
5734             if(this.isLocked(i) && !this.isHidden(i)){
5735                 this.totalWidth += this.getColumnWidth(i);
5736             }
5737         }
5738         return totalWidth;
5739     },
5740
5741     getLockedCount : function(){
5742         for(var i = 0, len = this.config.length; i < len; i++){
5743             if(!this.isLocked(i)){
5744                 return i;
5745             }
5746         }
5747         
5748         return this.config.length;
5749     },
5750
5751     /**
5752      * Returns the number of columns.
5753      * @return {Number}
5754      */
5755     getColumnCount : function(visibleOnly){
5756         if(visibleOnly === true){
5757             var c = 0;
5758             for(var i = 0, len = this.config.length; i < len; i++){
5759                 if(!this.isHidden(i)){
5760                     c++;
5761                 }
5762             }
5763             return c;
5764         }
5765         return this.config.length;
5766     },
5767
5768     /**
5769      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5770      * @param {Function} fn
5771      * @param {Object} scope (optional)
5772      * @return {Array} result
5773      */
5774     getColumnsBy : function(fn, scope){
5775         var r = [];
5776         for(var i = 0, len = this.config.length; i < len; i++){
5777             var c = this.config[i];
5778             if(fn.call(scope||this, c, i) === true){
5779                 r[r.length] = c;
5780             }
5781         }
5782         return r;
5783     },
5784
5785     /**
5786      * Returns true if the specified column is sortable.
5787      * @param {Number} col The column index
5788      * @return {Boolean}
5789      */
5790     isSortable : function(col){
5791         if(typeof this.config[col].sortable == "undefined"){
5792             return this.defaultSortable;
5793         }
5794         return this.config[col].sortable;
5795     },
5796
5797     /**
5798      * Returns the rendering (formatting) function defined for the column.
5799      * @param {Number} col The column index.
5800      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5801      */
5802     getRenderer : function(col){
5803         if(!this.config[col].renderer){
5804             return Roo.grid.ColumnModel.defaultRenderer;
5805         }
5806         return this.config[col].renderer;
5807     },
5808
5809     /**
5810      * Sets the rendering (formatting) function for a column.
5811      * @param {Number} col The column index
5812      * @param {Function} fn The function to use to process the cell's raw data
5813      * to return HTML markup for the grid view. The render function is called with
5814      * the following parameters:<ul>
5815      * <li>Data value.</li>
5816      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5817      * <li>css A CSS style string to apply to the table cell.</li>
5818      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5819      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5820      * <li>Row index</li>
5821      * <li>Column index</li>
5822      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5823      */
5824     setRenderer : function(col, fn){
5825         this.config[col].renderer = fn;
5826     },
5827
5828     /**
5829      * Returns the width for the specified column.
5830      * @param {Number} col The column index
5831      * @return {Number}
5832      */
5833     getColumnWidth : function(col){
5834         return this.config[col].width * 1 || this.defaultWidth;
5835     },
5836
5837     /**
5838      * Sets the width for a column.
5839      * @param {Number} col The column index
5840      * @param {Number} width The new width
5841      */
5842     setColumnWidth : function(col, width, suppressEvent){
5843         this.config[col].width = width;
5844         this.totalWidth = null;
5845         if(!suppressEvent){
5846              this.fireEvent("widthchange", this, col, width);
5847         }
5848     },
5849
5850     /**
5851      * Returns the total width of all columns.
5852      * @param {Boolean} includeHidden True to include hidden column widths
5853      * @return {Number}
5854      */
5855     getTotalWidth : function(includeHidden){
5856         if(!this.totalWidth){
5857             this.totalWidth = 0;
5858             for(var i = 0, len = this.config.length; i < len; i++){
5859                 if(includeHidden || !this.isHidden(i)){
5860                     this.totalWidth += this.getColumnWidth(i);
5861                 }
5862             }
5863         }
5864         return this.totalWidth;
5865     },
5866
5867     /**
5868      * Returns the header for the specified column.
5869      * @param {Number} col The column index
5870      * @return {String}
5871      */
5872     getColumnHeader : function(col){
5873         return this.config[col].header;
5874     },
5875
5876     /**
5877      * Sets the header for a column.
5878      * @param {Number} col The column index
5879      * @param {String} header The new header
5880      */
5881     setColumnHeader : function(col, header){
5882         this.config[col].header = header;
5883         this.fireEvent("headerchange", this, col, header);
5884     },
5885
5886     /**
5887      * Returns the tooltip for the specified column.
5888      * @param {Number} col The column index
5889      * @return {String}
5890      */
5891     getColumnTooltip : function(col){
5892             return this.config[col].tooltip;
5893     },
5894     /**
5895      * Sets the tooltip for a column.
5896      * @param {Number} col The column index
5897      * @param {String} tooltip The new tooltip
5898      */
5899     setColumnTooltip : function(col, tooltip){
5900             this.config[col].tooltip = tooltip;
5901     },
5902
5903     /**
5904      * Returns the dataIndex for the specified column.
5905      * @param {Number} col The column index
5906      * @return {Number}
5907      */
5908     getDataIndex : function(col){
5909         return this.config[col].dataIndex;
5910     },
5911
5912     /**
5913      * Sets the dataIndex for a column.
5914      * @param {Number} col The column index
5915      * @param {Number} dataIndex The new dataIndex
5916      */
5917     setDataIndex : function(col, dataIndex){
5918         this.config[col].dataIndex = dataIndex;
5919     },
5920
5921     
5922     
5923     /**
5924      * Returns true if the cell is editable.
5925      * @param {Number} colIndex The column index
5926      * @param {Number} rowIndex The row index - this is nto actually used..?
5927      * @return {Boolean}
5928      */
5929     isCellEditable : function(colIndex, rowIndex){
5930         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5931     },
5932
5933     /**
5934      * Returns the editor defined for the cell/column.
5935      * return false or null to disable editing.
5936      * @param {Number} colIndex The column index
5937      * @param {Number} rowIndex The row index
5938      * @return {Object}
5939      */
5940     getCellEditor : function(colIndex, rowIndex){
5941         return this.config[colIndex].editor;
5942     },
5943
5944     /**
5945      * Sets if a column is editable.
5946      * @param {Number} col The column index
5947      * @param {Boolean} editable True if the column is editable
5948      */
5949     setEditable : function(col, editable){
5950         this.config[col].editable = editable;
5951     },
5952
5953
5954     /**
5955      * Returns true if the column is hidden.
5956      * @param {Number} colIndex The column index
5957      * @return {Boolean}
5958      */
5959     isHidden : function(colIndex){
5960         return this.config[colIndex].hidden;
5961     },
5962
5963
5964     /**
5965      * Returns true if the column width cannot be changed
5966      */
5967     isFixed : function(colIndex){
5968         return this.config[colIndex].fixed;
5969     },
5970
5971     /**
5972      * Returns true if the column can be resized
5973      * @return {Boolean}
5974      */
5975     isResizable : function(colIndex){
5976         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5977     },
5978     /**
5979      * Sets if a column is hidden.
5980      * @param {Number} colIndex The column index
5981      * @param {Boolean} hidden True if the column is hidden
5982      */
5983     setHidden : function(colIndex, hidden){
5984         this.config[colIndex].hidden = hidden;
5985         this.totalWidth = null;
5986         this.fireEvent("hiddenchange", this, colIndex, hidden);
5987     },
5988
5989     /**
5990      * Sets the editor for a column.
5991      * @param {Number} col The column index
5992      * @param {Object} editor The editor object
5993      */
5994     setEditor : function(col, editor){
5995         this.config[col].editor = editor;
5996     }
5997 });
5998
5999 Roo.grid.ColumnModel.defaultRenderer = function(value)
6000 {
6001     if(typeof value == "object") {
6002         return value;
6003     }
6004         if(typeof value == "string" && value.length < 1){
6005             return "&#160;";
6006         }
6007     
6008         return String.format("{0}", value);
6009 };
6010
6011 // Alias for backwards compatibility
6012 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6013 /*
6014  * Based on:
6015  * Ext JS Library 1.1.1
6016  * Copyright(c) 2006-2007, Ext JS, LLC.
6017  *
6018  * Originally Released Under LGPL - original licence link has changed is not relivant.
6019  *
6020  * Fork - LGPL
6021  * <script type="text/javascript">
6022  */
6023  
6024 /**
6025  * @class Roo.LoadMask
6026  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6027  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6028  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6029  * element's UpdateManager load indicator and will be destroyed after the initial load.
6030  * @constructor
6031  * Create a new LoadMask
6032  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6033  * @param {Object} config The config object
6034  */
6035 Roo.LoadMask = function(el, config){
6036     this.el = Roo.get(el);
6037     Roo.apply(this, config);
6038     if(this.store){
6039         this.store.on('beforeload', this.onBeforeLoad, this);
6040         this.store.on('load', this.onLoad, this);
6041         this.store.on('loadexception', this.onLoadException, this);
6042         this.removeMask = false;
6043     }else{
6044         var um = this.el.getUpdateManager();
6045         um.showLoadIndicator = false; // disable the default indicator
6046         um.on('beforeupdate', this.onBeforeLoad, this);
6047         um.on('update', this.onLoad, this);
6048         um.on('failure', this.onLoad, this);
6049         this.removeMask = true;
6050     }
6051 };
6052
6053 Roo.LoadMask.prototype = {
6054     /**
6055      * @cfg {Boolean} removeMask
6056      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6057      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6058      */
6059     /**
6060      * @cfg {String} msg
6061      * The text to display in a centered loading message box (defaults to 'Loading...')
6062      */
6063     msg : 'Loading...',
6064     /**
6065      * @cfg {String} msgCls
6066      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6067      */
6068     msgCls : 'x-mask-loading',
6069
6070     /**
6071      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6072      * @type Boolean
6073      */
6074     disabled: false,
6075
6076     /**
6077      * Disables the mask to prevent it from being displayed
6078      */
6079     disable : function(){
6080        this.disabled = true;
6081     },
6082
6083     /**
6084      * Enables the mask so that it can be displayed
6085      */
6086     enable : function(){
6087         this.disabled = false;
6088     },
6089     
6090     onLoadException : function()
6091     {
6092         Roo.log(arguments);
6093         
6094         if (typeof(arguments[3]) != 'undefined') {
6095             Roo.MessageBox.alert("Error loading",arguments[3]);
6096         } 
6097         /*
6098         try {
6099             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6100                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6101             }   
6102         } catch(e) {
6103             
6104         }
6105         */
6106     
6107         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6108     },
6109     // private
6110     onLoad : function()
6111     {
6112         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6113     },
6114
6115     // private
6116     onBeforeLoad : function(){
6117         if(!this.disabled){
6118             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6119         }
6120     },
6121
6122     // private
6123     destroy : function(){
6124         if(this.store){
6125             this.store.un('beforeload', this.onBeforeLoad, this);
6126             this.store.un('load', this.onLoad, this);
6127             this.store.un('loadexception', this.onLoadException, this);
6128         }else{
6129             var um = this.el.getUpdateManager();
6130             um.un('beforeupdate', this.onBeforeLoad, this);
6131             um.un('update', this.onLoad, this);
6132             um.un('failure', this.onLoad, this);
6133         }
6134     }
6135 };/*
6136  * - LGPL
6137  *
6138  * table
6139  * 
6140  */
6141
6142 /**
6143  * @class Roo.bootstrap.Table
6144  * @extends Roo.bootstrap.Component
6145  * Bootstrap Table class
6146  * @cfg {String} cls table class
6147  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6148  * @cfg {String} bgcolor Specifies the background color for a table
6149  * @cfg {Number} border Specifies whether the table cells should have borders or not
6150  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6151  * @cfg {Number} cellspacing Specifies the space between cells
6152  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6153  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6154  * @cfg {String} sortable Specifies that the table should be sortable
6155  * @cfg {String} summary Specifies a summary of the content of a table
6156  * @cfg {Number} width Specifies the width of a table
6157  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6158  * 
6159  * @cfg {boolean} striped Should the rows be alternative striped
6160  * @cfg {boolean} bordered Add borders to the table
6161  * @cfg {boolean} hover Add hover highlighting
6162  * @cfg {boolean} condensed Format condensed
6163  * @cfg {boolean} responsive Format condensed
6164  * @cfg {Boolean} loadMask (true|false) default false
6165  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6166  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6167  * @cfg {Boolean} rowSelection (true|false) default false
6168  * @cfg {Boolean} cellSelection (true|false) default false
6169  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6170  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6171  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6172  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6173  
6174  * 
6175  * @constructor
6176  * Create a new Table
6177  * @param {Object} config The config object
6178  */
6179
6180 Roo.bootstrap.Table = function(config){
6181     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6182     
6183   
6184     
6185     // BC...
6186     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6187     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6188     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6189     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6190     
6191     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6192     if (this.sm) {
6193         this.sm.grid = this;
6194         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6195         this.sm = this.selModel;
6196         this.sm.xmodule = this.xmodule || false;
6197     }
6198     
6199     if (this.cm && typeof(this.cm.config) == 'undefined') {
6200         this.colModel = new Roo.grid.ColumnModel(this.cm);
6201         this.cm = this.colModel;
6202         this.cm.xmodule = this.xmodule || false;
6203     }
6204     if (this.store) {
6205         this.store= Roo.factory(this.store, Roo.data);
6206         this.ds = this.store;
6207         this.ds.xmodule = this.xmodule || false;
6208          
6209     }
6210     if (this.footer && this.store) {
6211         this.footer.dataSource = this.ds;
6212         this.footer = Roo.factory(this.footer);
6213     }
6214     
6215     /** @private */
6216     this.addEvents({
6217         /**
6218          * @event cellclick
6219          * Fires when a cell is clicked
6220          * @param {Roo.bootstrap.Table} this
6221          * @param {Roo.Element} el
6222          * @param {Number} rowIndex
6223          * @param {Number} columnIndex
6224          * @param {Roo.EventObject} e
6225          */
6226         "cellclick" : true,
6227         /**
6228          * @event celldblclick
6229          * Fires when a cell is double clicked
6230          * @param {Roo.bootstrap.Table} this
6231          * @param {Roo.Element} el
6232          * @param {Number} rowIndex
6233          * @param {Number} columnIndex
6234          * @param {Roo.EventObject} e
6235          */
6236         "celldblclick" : true,
6237         /**
6238          * @event rowclick
6239          * Fires when a row is clicked
6240          * @param {Roo.bootstrap.Table} this
6241          * @param {Roo.Element} el
6242          * @param {Number} rowIndex
6243          * @param {Roo.EventObject} e
6244          */
6245         "rowclick" : true,
6246         /**
6247          * @event rowdblclick
6248          * Fires when a row is double clicked
6249          * @param {Roo.bootstrap.Table} this
6250          * @param {Roo.Element} el
6251          * @param {Number} rowIndex
6252          * @param {Roo.EventObject} e
6253          */
6254         "rowdblclick" : true,
6255         /**
6256          * @event mouseover
6257          * Fires when a mouseover occur
6258          * @param {Roo.bootstrap.Table} this
6259          * @param {Roo.Element} el
6260          * @param {Number} rowIndex
6261          * @param {Number} columnIndex
6262          * @param {Roo.EventObject} e
6263          */
6264         "mouseover" : true,
6265         /**
6266          * @event mouseout
6267          * Fires when a mouseout occur
6268          * @param {Roo.bootstrap.Table} this
6269          * @param {Roo.Element} el
6270          * @param {Number} rowIndex
6271          * @param {Number} columnIndex
6272          * @param {Roo.EventObject} e
6273          */
6274         "mouseout" : true,
6275         /**
6276          * @event rowclass
6277          * Fires when a row is rendered, so you can change add a style to it.
6278          * @param {Roo.bootstrap.Table} this
6279          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6280          */
6281         'rowclass' : true,
6282           /**
6283          * @event rowsrendered
6284          * Fires when all the  rows have been rendered
6285          * @param {Roo.bootstrap.Table} this
6286          */
6287         'rowsrendered' : true,
6288         /**
6289          * @event contextmenu
6290          * The raw contextmenu event for the entire grid.
6291          * @param {Roo.EventObject} e
6292          */
6293         "contextmenu" : true,
6294         /**
6295          * @event rowcontextmenu
6296          * Fires when a row is right clicked
6297          * @param {Roo.bootstrap.Table} this
6298          * @param {Number} rowIndex
6299          * @param {Roo.EventObject} e
6300          */
6301         "rowcontextmenu" : true,
6302         /**
6303          * @event cellcontextmenu
6304          * Fires when a cell is right clicked
6305          * @param {Roo.bootstrap.Table} this
6306          * @param {Number} rowIndex
6307          * @param {Number} cellIndex
6308          * @param {Roo.EventObject} e
6309          */
6310          "cellcontextmenu" : true,
6311          /**
6312          * @event headercontextmenu
6313          * Fires when a header is right clicked
6314          * @param {Roo.bootstrap.Table} this
6315          * @param {Number} columnIndex
6316          * @param {Roo.EventObject} e
6317          */
6318         "headercontextmenu" : true
6319     });
6320 };
6321
6322 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6323     
6324     cls: false,
6325     align: false,
6326     bgcolor: false,
6327     border: false,
6328     cellpadding: false,
6329     cellspacing: false,
6330     frame: false,
6331     rules: false,
6332     sortable: false,
6333     summary: false,
6334     width: false,
6335     striped : false,
6336     scrollBody : false,
6337     bordered: false,
6338     hover:  false,
6339     condensed : false,
6340     responsive : false,
6341     sm : false,
6342     cm : false,
6343     store : false,
6344     loadMask : false,
6345     footerShow : true,
6346     headerShow : true,
6347   
6348     rowSelection : false,
6349     cellSelection : false,
6350     layout : false,
6351     
6352     // Roo.Element - the tbody
6353     mainBody: false,
6354     // Roo.Element - thead element
6355     mainHead: false,
6356     
6357     container: false, // used by gridpanel...
6358     
6359     lazyLoad : false,
6360     
6361     CSS : Roo.util.CSS,
6362     
6363     auto_hide_footer : false,
6364     
6365     getAutoCreate : function()
6366     {
6367         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6368         
6369         cfg = {
6370             tag: 'table',
6371             cls : 'table',
6372             cn : []
6373         };
6374         if (this.scrollBody) {
6375             cfg.cls += ' table-body-fixed';
6376         }    
6377         if (this.striped) {
6378             cfg.cls += ' table-striped';
6379         }
6380         
6381         if (this.hover) {
6382             cfg.cls += ' table-hover';
6383         }
6384         if (this.bordered) {
6385             cfg.cls += ' table-bordered';
6386         }
6387         if (this.condensed) {
6388             cfg.cls += ' table-condensed';
6389         }
6390         if (this.responsive) {
6391             cfg.cls += ' table-responsive';
6392         }
6393         
6394         if (this.cls) {
6395             cfg.cls+=  ' ' +this.cls;
6396         }
6397         
6398         // this lot should be simplifed...
6399         var _t = this;
6400         var cp = [
6401             'align',
6402             'bgcolor',
6403             'border',
6404             'cellpadding',
6405             'cellspacing',
6406             'frame',
6407             'rules',
6408             'sortable',
6409             'summary',
6410             'width'
6411         ].forEach(function(k) {
6412             if (_t[k]) {
6413                 cfg[k] = _t[k];
6414             }
6415         });
6416         
6417         
6418         if (this.layout) {
6419             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6420         }
6421         
6422         if(this.store || this.cm){
6423             if(this.headerShow){
6424                 cfg.cn.push(this.renderHeader());
6425             }
6426             
6427             cfg.cn.push(this.renderBody());
6428             
6429             if(this.footerShow){
6430                 cfg.cn.push(this.renderFooter());
6431             }
6432             // where does this come from?
6433             //cfg.cls+=  ' TableGrid';
6434         }
6435         
6436         return { cn : [ cfg ] };
6437     },
6438     
6439     initEvents : function()
6440     {   
6441         if(!this.store || !this.cm){
6442             return;
6443         }
6444         if (this.selModel) {
6445             this.selModel.initEvents();
6446         }
6447         
6448         
6449         //Roo.log('initEvents with ds!!!!');
6450         
6451         this.mainBody = this.el.select('tbody', true).first();
6452         this.mainHead = this.el.select('thead', true).first();
6453         this.mainFoot = this.el.select('tfoot', true).first();
6454         
6455         
6456         
6457         var _this = this;
6458         
6459         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6460             e.on('click', _this.sort, _this);
6461         });
6462         
6463         this.mainBody.on("click", this.onClick, this);
6464         this.mainBody.on("dblclick", this.onDblClick, this);
6465         
6466         // why is this done????? = it breaks dialogs??
6467         //this.parent().el.setStyle('position', 'relative');
6468         
6469         
6470         if (this.footer) {
6471             this.footer.parentId = this.id;
6472             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6473             
6474             if(this.lazyLoad){
6475                 this.el.select('tfoot tr td').first().addClass('hide');
6476             }
6477         } 
6478         
6479         if(this.loadMask) {
6480             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6481         }
6482         
6483         this.store.on('load', this.onLoad, this);
6484         this.store.on('beforeload', this.onBeforeLoad, this);
6485         this.store.on('update', this.onUpdate, this);
6486         this.store.on('add', this.onAdd, this);
6487         this.store.on("clear", this.clear, this);
6488         
6489         this.el.on("contextmenu", this.onContextMenu, this);
6490         
6491         this.mainBody.on('scroll', this.onBodyScroll, this);
6492         
6493         this.cm.on("headerchange", this.onHeaderChange, this);
6494         
6495         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6496         
6497     },
6498     
6499     onContextMenu : function(e, t)
6500     {
6501         this.processEvent("contextmenu", e);
6502     },
6503     
6504     processEvent : function(name, e)
6505     {
6506         if (name != 'touchstart' ) {
6507             this.fireEvent(name, e);    
6508         }
6509         
6510         var t = e.getTarget();
6511         
6512         var cell = Roo.get(t);
6513         
6514         if(!cell){
6515             return;
6516         }
6517         
6518         if(cell.findParent('tfoot', false, true)){
6519             return;
6520         }
6521         
6522         if(cell.findParent('thead', false, true)){
6523             
6524             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6525                 cell = Roo.get(t).findParent('th', false, true);
6526                 if (!cell) {
6527                     Roo.log("failed to find th in thead?");
6528                     Roo.log(e.getTarget());
6529                     return;
6530                 }
6531             }
6532             
6533             var cellIndex = cell.dom.cellIndex;
6534             
6535             var ename = name == 'touchstart' ? 'click' : name;
6536             this.fireEvent("header" + ename, this, cellIndex, e);
6537             
6538             return;
6539         }
6540         
6541         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6542             cell = Roo.get(t).findParent('td', false, true);
6543             if (!cell) {
6544                 Roo.log("failed to find th in tbody?");
6545                 Roo.log(e.getTarget());
6546                 return;
6547             }
6548         }
6549         
6550         var row = cell.findParent('tr', false, true);
6551         var cellIndex = cell.dom.cellIndex;
6552         var rowIndex = row.dom.rowIndex - 1;
6553         
6554         if(row !== false){
6555             
6556             this.fireEvent("row" + name, this, rowIndex, e);
6557             
6558             if(cell !== false){
6559             
6560                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6561             }
6562         }
6563         
6564     },
6565     
6566     onMouseover : function(e, el)
6567     {
6568         var cell = Roo.get(el);
6569         
6570         if(!cell){
6571             return;
6572         }
6573         
6574         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6575             cell = cell.findParent('td', false, true);
6576         }
6577         
6578         var row = cell.findParent('tr', false, true);
6579         var cellIndex = cell.dom.cellIndex;
6580         var rowIndex = row.dom.rowIndex - 1; // start from 0
6581         
6582         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6583         
6584     },
6585     
6586     onMouseout : function(e, el)
6587     {
6588         var cell = Roo.get(el);
6589         
6590         if(!cell){
6591             return;
6592         }
6593         
6594         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6595             cell = cell.findParent('td', false, true);
6596         }
6597         
6598         var row = cell.findParent('tr', false, true);
6599         var cellIndex = cell.dom.cellIndex;
6600         var rowIndex = row.dom.rowIndex - 1; // start from 0
6601         
6602         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6603         
6604     },
6605     
6606     onClick : function(e, el)
6607     {
6608         var cell = Roo.get(el);
6609         
6610         if(!cell || (!this.cellSelection && !this.rowSelection)){
6611             return;
6612         }
6613         
6614         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6615             cell = cell.findParent('td', false, true);
6616         }
6617         
6618         if(!cell || typeof(cell) == 'undefined'){
6619             return;
6620         }
6621         
6622         var row = cell.findParent('tr', false, true);
6623         
6624         if(!row || typeof(row) == 'undefined'){
6625             return;
6626         }
6627         
6628         var cellIndex = cell.dom.cellIndex;
6629         var rowIndex = this.getRowIndex(row);
6630         
6631         // why??? - should these not be based on SelectionModel?
6632         if(this.cellSelection){
6633             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6634         }
6635         
6636         if(this.rowSelection){
6637             this.fireEvent('rowclick', this, row, rowIndex, e);
6638         }
6639         
6640         
6641     },
6642         
6643     onDblClick : function(e,el)
6644     {
6645         var cell = Roo.get(el);
6646         
6647         if(!cell || (!this.cellSelection && !this.rowSelection)){
6648             return;
6649         }
6650         
6651         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6652             cell = cell.findParent('td', false, true);
6653         }
6654         
6655         if(!cell || typeof(cell) == 'undefined'){
6656             return;
6657         }
6658         
6659         var row = cell.findParent('tr', false, true);
6660         
6661         if(!row || typeof(row) == 'undefined'){
6662             return;
6663         }
6664         
6665         var cellIndex = cell.dom.cellIndex;
6666         var rowIndex = this.getRowIndex(row);
6667         
6668         if(this.cellSelection){
6669             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6670         }
6671         
6672         if(this.rowSelection){
6673             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6674         }
6675     },
6676     
6677     sort : function(e,el)
6678     {
6679         var col = Roo.get(el);
6680         
6681         if(!col.hasClass('sortable')){
6682             return;
6683         }
6684         
6685         var sort = col.attr('sort');
6686         var dir = 'ASC';
6687         
6688         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6689             dir = 'DESC';
6690         }
6691         
6692         this.store.sortInfo = {field : sort, direction : dir};
6693         
6694         if (this.footer) {
6695             Roo.log("calling footer first");
6696             this.footer.onClick('first');
6697         } else {
6698         
6699             this.store.load({ params : { start : 0 } });
6700         }
6701     },
6702     
6703     renderHeader : function()
6704     {
6705         var header = {
6706             tag: 'thead',
6707             cn : []
6708         };
6709         
6710         var cm = this.cm;
6711         this.totalWidth = 0;
6712         
6713         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6714             
6715             var config = cm.config[i];
6716             
6717             var c = {
6718                 tag: 'th',
6719                 cls : 'x-hcol-' + i,
6720                 style : '',
6721                 html: cm.getColumnHeader(i)
6722             };
6723             
6724             var hh = '';
6725             
6726             if(typeof(config.sortable) != 'undefined' && config.sortable){
6727                 c.cls = 'sortable';
6728                 c.html = '<i class="glyphicon"></i>' + c.html;
6729             }
6730             
6731             if(typeof(config.lgHeader) != 'undefined'){
6732                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6733             }
6734             
6735             if(typeof(config.mdHeader) != 'undefined'){
6736                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6737             }
6738             
6739             if(typeof(config.smHeader) != 'undefined'){
6740                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6741             }
6742             
6743             if(typeof(config.xsHeader) != 'undefined'){
6744                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6745             }
6746             
6747             if(hh.length){
6748                 c.html = hh;
6749             }
6750             
6751             if(typeof(config.tooltip) != 'undefined'){
6752                 c.tooltip = config.tooltip;
6753             }
6754             
6755             if(typeof(config.colspan) != 'undefined'){
6756                 c.colspan = config.colspan;
6757             }
6758             
6759             if(typeof(config.hidden) != 'undefined' && config.hidden){
6760                 c.style += ' display:none;';
6761             }
6762             
6763             if(typeof(config.dataIndex) != 'undefined'){
6764                 c.sort = config.dataIndex;
6765             }
6766             
6767            
6768             
6769             if(typeof(config.align) != 'undefined' && config.align.length){
6770                 c.style += ' text-align:' + config.align + ';';
6771             }
6772             
6773             if(typeof(config.width) != 'undefined'){
6774                 c.style += ' width:' + config.width + 'px;';
6775                 this.totalWidth += config.width;
6776             } else {
6777                 this.totalWidth += 100; // assume minimum of 100 per column?
6778             }
6779             
6780             if(typeof(config.cls) != 'undefined'){
6781                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6782             }
6783             
6784             ['xs','sm','md','lg'].map(function(size){
6785                 
6786                 if(typeof(config[size]) == 'undefined'){
6787                     return;
6788                 }
6789                 
6790                 if (!config[size]) { // 0 = hidden
6791                     c.cls += ' hidden-' + size;
6792                     return;
6793                 }
6794                 
6795                 c.cls += ' col-' + size + '-' + config[size];
6796
6797             });
6798             
6799             header.cn.push(c)
6800         }
6801         
6802         return header;
6803     },
6804     
6805     renderBody : function()
6806     {
6807         var body = {
6808             tag: 'tbody',
6809             cn : [
6810                 {
6811                     tag: 'tr',
6812                     cn : [
6813                         {
6814                             tag : 'td',
6815                             colspan :  this.cm.getColumnCount()
6816                         }
6817                     ]
6818                 }
6819             ]
6820         };
6821         
6822         return body;
6823     },
6824     
6825     renderFooter : function()
6826     {
6827         var footer = {
6828             tag: 'tfoot',
6829             cn : [
6830                 {
6831                     tag: 'tr',
6832                     cn : [
6833                         {
6834                             tag : 'td',
6835                             colspan :  this.cm.getColumnCount()
6836                         }
6837                     ]
6838                 }
6839             ]
6840         };
6841         
6842         return footer;
6843     },
6844     
6845     
6846     
6847     onLoad : function()
6848     {
6849 //        Roo.log('ds onload');
6850         this.clear();
6851         
6852         var _this = this;
6853         var cm = this.cm;
6854         var ds = this.store;
6855         
6856         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6857             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6858             if (_this.store.sortInfo) {
6859                     
6860                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6861                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6862                 }
6863                 
6864                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6865                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6866                 }
6867             }
6868         });
6869         
6870         var tbody =  this.mainBody;
6871               
6872         if(ds.getCount() > 0){
6873             ds.data.each(function(d,rowIndex){
6874                 var row =  this.renderRow(cm, ds, rowIndex);
6875                 
6876                 tbody.createChild(row);
6877                 
6878                 var _this = this;
6879                 
6880                 if(row.cellObjects.length){
6881                     Roo.each(row.cellObjects, function(r){
6882                         _this.renderCellObject(r);
6883                     })
6884                 }
6885                 
6886             }, this);
6887         }
6888         
6889         var tfoot = this.el.select('tfoot', true).first();
6890         
6891         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6892             
6893             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6894             
6895             var total = this.ds.getTotalCount();
6896             
6897             if(this.footer.pageSize < total){
6898                 this.mainFoot.show();
6899             }
6900         }
6901         
6902         Roo.each(this.el.select('tbody td', true).elements, function(e){
6903             e.on('mouseover', _this.onMouseover, _this);
6904         });
6905         
6906         Roo.each(this.el.select('tbody td', true).elements, function(e){
6907             e.on('mouseout', _this.onMouseout, _this);
6908         });
6909         this.fireEvent('rowsrendered', this);
6910         
6911         this.autoSize();
6912     },
6913     
6914     
6915     onUpdate : function(ds,record)
6916     {
6917         this.refreshRow(record);
6918         this.autoSize();
6919     },
6920     
6921     onRemove : function(ds, record, index, isUpdate){
6922         if(isUpdate !== true){
6923             this.fireEvent("beforerowremoved", this, index, record);
6924         }
6925         var bt = this.mainBody.dom;
6926         
6927         var rows = this.el.select('tbody > tr', true).elements;
6928         
6929         if(typeof(rows[index]) != 'undefined'){
6930             bt.removeChild(rows[index].dom);
6931         }
6932         
6933 //        if(bt.rows[index]){
6934 //            bt.removeChild(bt.rows[index]);
6935 //        }
6936         
6937         if(isUpdate !== true){
6938             //this.stripeRows(index);
6939             //this.syncRowHeights(index, index);
6940             //this.layout();
6941             this.fireEvent("rowremoved", this, index, record);
6942         }
6943     },
6944     
6945     onAdd : function(ds, records, rowIndex)
6946     {
6947         //Roo.log('on Add called');
6948         // - note this does not handle multiple adding very well..
6949         var bt = this.mainBody.dom;
6950         for (var i =0 ; i < records.length;i++) {
6951             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6952             //Roo.log(records[i]);
6953             //Roo.log(this.store.getAt(rowIndex+i));
6954             this.insertRow(this.store, rowIndex + i, false);
6955             return;
6956         }
6957         
6958     },
6959     
6960     
6961     refreshRow : function(record){
6962         var ds = this.store, index;
6963         if(typeof record == 'number'){
6964             index = record;
6965             record = ds.getAt(index);
6966         }else{
6967             index = ds.indexOf(record);
6968         }
6969         this.insertRow(ds, index, true);
6970         this.autoSize();
6971         this.onRemove(ds, record, index+1, true);
6972         this.autoSize();
6973         //this.syncRowHeights(index, index);
6974         //this.layout();
6975         this.fireEvent("rowupdated", this, index, record);
6976     },
6977     
6978     insertRow : function(dm, rowIndex, isUpdate){
6979         
6980         if(!isUpdate){
6981             this.fireEvent("beforerowsinserted", this, rowIndex);
6982         }
6983             //var s = this.getScrollState();
6984         var row = this.renderRow(this.cm, this.store, rowIndex);
6985         // insert before rowIndex..
6986         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6987         
6988         var _this = this;
6989                 
6990         if(row.cellObjects.length){
6991             Roo.each(row.cellObjects, function(r){
6992                 _this.renderCellObject(r);
6993             })
6994         }
6995             
6996         if(!isUpdate){
6997             this.fireEvent("rowsinserted", this, rowIndex);
6998             //this.syncRowHeights(firstRow, lastRow);
6999             //this.stripeRows(firstRow);
7000             //this.layout();
7001         }
7002         
7003     },
7004     
7005     
7006     getRowDom : function(rowIndex)
7007     {
7008         var rows = this.el.select('tbody > tr', true).elements;
7009         
7010         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7011         
7012     },
7013     // returns the object tree for a tr..
7014   
7015     
7016     renderRow : function(cm, ds, rowIndex) 
7017     {
7018         var d = ds.getAt(rowIndex);
7019         
7020         var row = {
7021             tag : 'tr',
7022             cls : 'x-row-' + rowIndex,
7023             cn : []
7024         };
7025             
7026         var cellObjects = [];
7027         
7028         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7029             var config = cm.config[i];
7030             
7031             var renderer = cm.getRenderer(i);
7032             var value = '';
7033             var id = false;
7034             
7035             if(typeof(renderer) !== 'undefined'){
7036                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7037             }
7038             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7039             // and are rendered into the cells after the row is rendered - using the id for the element.
7040             
7041             if(typeof(value) === 'object'){
7042                 id = Roo.id();
7043                 cellObjects.push({
7044                     container : id,
7045                     cfg : value 
7046                 })
7047             }
7048             
7049             var rowcfg = {
7050                 record: d,
7051                 rowIndex : rowIndex,
7052                 colIndex : i,
7053                 rowClass : ''
7054             };
7055
7056             this.fireEvent('rowclass', this, rowcfg);
7057             
7058             var td = {
7059                 tag: 'td',
7060                 cls : rowcfg.rowClass + ' x-col-' + i,
7061                 style: '',
7062                 html: (typeof(value) === 'object') ? '' : value
7063             };
7064             
7065             if (id) {
7066                 td.id = id;
7067             }
7068             
7069             if(typeof(config.colspan) != 'undefined'){
7070                 td.colspan = config.colspan;
7071             }
7072             
7073             if(typeof(config.hidden) != 'undefined' && config.hidden){
7074                 td.style += ' display:none;';
7075             }
7076             
7077             if(typeof(config.align) != 'undefined' && config.align.length){
7078                 td.style += ' text-align:' + config.align + ';';
7079             }
7080             if(typeof(config.valign) != 'undefined' && config.valign.length){
7081                 td.style += ' vertical-align:' + config.valign + ';';
7082             }
7083             
7084             if(typeof(config.width) != 'undefined'){
7085                 td.style += ' width:' +  config.width + 'px;';
7086             }
7087             
7088             if(typeof(config.cursor) != 'undefined'){
7089                 td.style += ' cursor:' +  config.cursor + ';';
7090             }
7091             
7092             if(typeof(config.cls) != 'undefined'){
7093                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7094             }
7095             
7096             ['xs','sm','md','lg'].map(function(size){
7097                 
7098                 if(typeof(config[size]) == 'undefined'){
7099                     return;
7100                 }
7101                 
7102                 if (!config[size]) { // 0 = hidden
7103                     td.cls += ' hidden-' + size;
7104                     return;
7105                 }
7106                 
7107                 td.cls += ' col-' + size + '-' + config[size];
7108
7109             });
7110             
7111             row.cn.push(td);
7112            
7113         }
7114         
7115         row.cellObjects = cellObjects;
7116         
7117         return row;
7118           
7119     },
7120     
7121     
7122     
7123     onBeforeLoad : function()
7124     {
7125         
7126     },
7127      /**
7128      * Remove all rows
7129      */
7130     clear : function()
7131     {
7132         this.el.select('tbody', true).first().dom.innerHTML = '';
7133     },
7134     /**
7135      * Show or hide a row.
7136      * @param {Number} rowIndex to show or hide
7137      * @param {Boolean} state hide
7138      */
7139     setRowVisibility : function(rowIndex, state)
7140     {
7141         var bt = this.mainBody.dom;
7142         
7143         var rows = this.el.select('tbody > tr', true).elements;
7144         
7145         if(typeof(rows[rowIndex]) == 'undefined'){
7146             return;
7147         }
7148         rows[rowIndex].dom.style.display = state ? '' : 'none';
7149     },
7150     
7151     
7152     getSelectionModel : function(){
7153         if(!this.selModel){
7154             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7155         }
7156         return this.selModel;
7157     },
7158     /*
7159      * Render the Roo.bootstrap object from renderder
7160      */
7161     renderCellObject : function(r)
7162     {
7163         var _this = this;
7164         
7165         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7166         
7167         var t = r.cfg.render(r.container);
7168         
7169         if(r.cfg.cn){
7170             Roo.each(r.cfg.cn, function(c){
7171                 var child = {
7172                     container: t.getChildContainer(),
7173                     cfg: c
7174                 };
7175                 _this.renderCellObject(child);
7176             })
7177         }
7178     },
7179     
7180     getRowIndex : function(row)
7181     {
7182         var rowIndex = -1;
7183         
7184         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7185             if(el != row){
7186                 return;
7187             }
7188             
7189             rowIndex = index;
7190         });
7191         
7192         return rowIndex;
7193     },
7194      /**
7195      * Returns the grid's underlying element = used by panel.Grid
7196      * @return {Element} The element
7197      */
7198     getGridEl : function(){
7199         return this.el;
7200     },
7201      /**
7202      * Forces a resize - used by panel.Grid
7203      * @return {Element} The element
7204      */
7205     autoSize : function()
7206     {
7207         //var ctr = Roo.get(this.container.dom.parentElement);
7208         var ctr = Roo.get(this.el.dom);
7209         
7210         var thd = this.getGridEl().select('thead',true).first();
7211         var tbd = this.getGridEl().select('tbody', true).first();
7212         var tfd = this.getGridEl().select('tfoot', true).first();
7213         
7214         var cw = ctr.getWidth();
7215         
7216         if (tbd) {
7217             
7218             tbd.setSize(ctr.getWidth(),
7219                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7220             );
7221             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7222             cw -= barsize;
7223         }
7224         cw = Math.max(cw, this.totalWidth);
7225         this.getGridEl().select('tr',true).setWidth(cw);
7226         // resize 'expandable coloumn?
7227         
7228         return; // we doe not have a view in this design..
7229         
7230     },
7231     onBodyScroll: function()
7232     {
7233         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7234         if(this.mainHead){
7235             this.mainHead.setStyle({
7236                 'position' : 'relative',
7237                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7238             });
7239         }
7240         
7241         if(this.lazyLoad){
7242             
7243             var scrollHeight = this.mainBody.dom.scrollHeight;
7244             
7245             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7246             
7247             var height = this.mainBody.getHeight();
7248             
7249             if(scrollHeight - height == scrollTop) {
7250                 
7251                 var total = this.ds.getTotalCount();
7252                 
7253                 if(this.footer.cursor + this.footer.pageSize < total){
7254                     
7255                     this.footer.ds.load({
7256                         params : {
7257                             start : this.footer.cursor + this.footer.pageSize,
7258                             limit : this.footer.pageSize
7259                         },
7260                         add : true
7261                     });
7262                 }
7263             }
7264             
7265         }
7266     },
7267     
7268     onHeaderChange : function()
7269     {
7270         var header = this.renderHeader();
7271         var table = this.el.select('table', true).first();
7272         
7273         this.mainHead.remove();
7274         this.mainHead = table.createChild(header, this.mainBody, false);
7275     },
7276     
7277     onHiddenChange : function(colModel, colIndex, hidden)
7278     {
7279         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7280         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7281         
7282         this.CSS.updateRule(thSelector, "display", "");
7283         this.CSS.updateRule(tdSelector, "display", "");
7284         
7285         if(hidden){
7286             this.CSS.updateRule(thSelector, "display", "none");
7287             this.CSS.updateRule(tdSelector, "display", "none");
7288         }
7289         
7290         this.onHeaderChange();
7291         this.onLoad();
7292     },
7293     
7294     setColumnWidth: function(col_index, width)
7295     {
7296         // width = "md-2 xs-2..."
7297         if(!this.colModel.config[col_index]) {
7298             return;
7299         }
7300         
7301         var w = width.split(" ");
7302         
7303         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7304         
7305         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7306         
7307         
7308         for(var j = 0; j < w.length; j++) {
7309             
7310             if(!w[j]) {
7311                 continue;
7312             }
7313             
7314             var size_cls = w[j].split("-");
7315             
7316             if(!Number.isInteger(size_cls[1] * 1)) {
7317                 continue;
7318             }
7319             
7320             if(!this.colModel.config[col_index][size_cls[0]]) {
7321                 continue;
7322             }
7323             
7324             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7325                 continue;
7326             }
7327             
7328             h_row[0].classList.replace(
7329                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7330                 "col-"+size_cls[0]+"-"+size_cls[1]
7331             );
7332             
7333             for(var i = 0; i < rows.length; i++) {
7334                 
7335                 var size_cls = w[j].split("-");
7336                 
7337                 if(!Number.isInteger(size_cls[1] * 1)) {
7338                     continue;
7339                 }
7340                 
7341                 if(!this.colModel.config[col_index][size_cls[0]]) {
7342                     continue;
7343                 }
7344                 
7345                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7346                     continue;
7347                 }
7348                 
7349                 rows[i].classList.replace(
7350                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7351                     "col-"+size_cls[0]+"-"+size_cls[1]
7352                 );
7353             }
7354             
7355             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7356         }
7357     }
7358 });
7359
7360  
7361
7362  /*
7363  * - LGPL
7364  *
7365  * table cell
7366  * 
7367  */
7368
7369 /**
7370  * @class Roo.bootstrap.TableCell
7371  * @extends Roo.bootstrap.Component
7372  * Bootstrap TableCell class
7373  * @cfg {String} html cell contain text
7374  * @cfg {String} cls cell class
7375  * @cfg {String} tag cell tag (td|th) default td
7376  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7377  * @cfg {String} align Aligns the content in a cell
7378  * @cfg {String} axis Categorizes cells
7379  * @cfg {String} bgcolor Specifies the background color of a cell
7380  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7381  * @cfg {Number} colspan Specifies the number of columns a cell should span
7382  * @cfg {String} headers Specifies one or more header cells a cell is related to
7383  * @cfg {Number} height Sets the height of a cell
7384  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7385  * @cfg {Number} rowspan Sets the number of rows a cell should span
7386  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7387  * @cfg {String} valign Vertical aligns the content in a cell
7388  * @cfg {Number} width Specifies the width of a cell
7389  * 
7390  * @constructor
7391  * Create a new TableCell
7392  * @param {Object} config The config object
7393  */
7394
7395 Roo.bootstrap.TableCell = function(config){
7396     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7397 };
7398
7399 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7400     
7401     html: false,
7402     cls: false,
7403     tag: false,
7404     abbr: false,
7405     align: false,
7406     axis: false,
7407     bgcolor: false,
7408     charoff: false,
7409     colspan: false,
7410     headers: false,
7411     height: false,
7412     nowrap: false,
7413     rowspan: false,
7414     scope: false,
7415     valign: false,
7416     width: false,
7417     
7418     
7419     getAutoCreate : function(){
7420         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7421         
7422         cfg = {
7423             tag: 'td'
7424         };
7425         
7426         if(this.tag){
7427             cfg.tag = this.tag;
7428         }
7429         
7430         if (this.html) {
7431             cfg.html=this.html
7432         }
7433         if (this.cls) {
7434             cfg.cls=this.cls
7435         }
7436         if (this.abbr) {
7437             cfg.abbr=this.abbr
7438         }
7439         if (this.align) {
7440             cfg.align=this.align
7441         }
7442         if (this.axis) {
7443             cfg.axis=this.axis
7444         }
7445         if (this.bgcolor) {
7446             cfg.bgcolor=this.bgcolor
7447         }
7448         if (this.charoff) {
7449             cfg.charoff=this.charoff
7450         }
7451         if (this.colspan) {
7452             cfg.colspan=this.colspan
7453         }
7454         if (this.headers) {
7455             cfg.headers=this.headers
7456         }
7457         if (this.height) {
7458             cfg.height=this.height
7459         }
7460         if (this.nowrap) {
7461             cfg.nowrap=this.nowrap
7462         }
7463         if (this.rowspan) {
7464             cfg.rowspan=this.rowspan
7465         }
7466         if (this.scope) {
7467             cfg.scope=this.scope
7468         }
7469         if (this.valign) {
7470             cfg.valign=this.valign
7471         }
7472         if (this.width) {
7473             cfg.width=this.width
7474         }
7475         
7476         
7477         return cfg;
7478     }
7479    
7480 });
7481
7482  
7483
7484  /*
7485  * - LGPL
7486  *
7487  * table row
7488  * 
7489  */
7490
7491 /**
7492  * @class Roo.bootstrap.TableRow
7493  * @extends Roo.bootstrap.Component
7494  * Bootstrap TableRow class
7495  * @cfg {String} cls row class
7496  * @cfg {String} align Aligns the content in a table row
7497  * @cfg {String} bgcolor Specifies a background color for a table row
7498  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7499  * @cfg {String} valign Vertical aligns the content in a table row
7500  * 
7501  * @constructor
7502  * Create a new TableRow
7503  * @param {Object} config The config object
7504  */
7505
7506 Roo.bootstrap.TableRow = function(config){
7507     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7508 };
7509
7510 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7511     
7512     cls: false,
7513     align: false,
7514     bgcolor: false,
7515     charoff: false,
7516     valign: false,
7517     
7518     getAutoCreate : function(){
7519         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7520         
7521         cfg = {
7522             tag: 'tr'
7523         };
7524             
7525         if(this.cls){
7526             cfg.cls = this.cls;
7527         }
7528         if(this.align){
7529             cfg.align = this.align;
7530         }
7531         if(this.bgcolor){
7532             cfg.bgcolor = this.bgcolor;
7533         }
7534         if(this.charoff){
7535             cfg.charoff = this.charoff;
7536         }
7537         if(this.valign){
7538             cfg.valign = this.valign;
7539         }
7540         
7541         return cfg;
7542     }
7543    
7544 });
7545
7546  
7547
7548  /*
7549  * - LGPL
7550  *
7551  * table body
7552  * 
7553  */
7554
7555 /**
7556  * @class Roo.bootstrap.TableBody
7557  * @extends Roo.bootstrap.Component
7558  * Bootstrap TableBody class
7559  * @cfg {String} cls element class
7560  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7561  * @cfg {String} align Aligns the content inside the element
7562  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7563  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7564  * 
7565  * @constructor
7566  * Create a new TableBody
7567  * @param {Object} config The config object
7568  */
7569
7570 Roo.bootstrap.TableBody = function(config){
7571     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7572 };
7573
7574 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7575     
7576     cls: false,
7577     tag: false,
7578     align: false,
7579     charoff: false,
7580     valign: false,
7581     
7582     getAutoCreate : function(){
7583         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7584         
7585         cfg = {
7586             tag: 'tbody'
7587         };
7588             
7589         if (this.cls) {
7590             cfg.cls=this.cls
7591         }
7592         if(this.tag){
7593             cfg.tag = this.tag;
7594         }
7595         
7596         if(this.align){
7597             cfg.align = this.align;
7598         }
7599         if(this.charoff){
7600             cfg.charoff = this.charoff;
7601         }
7602         if(this.valign){
7603             cfg.valign = this.valign;
7604         }
7605         
7606         return cfg;
7607     }
7608     
7609     
7610 //    initEvents : function()
7611 //    {
7612 //        
7613 //        if(!this.store){
7614 //            return;
7615 //        }
7616 //        
7617 //        this.store = Roo.factory(this.store, Roo.data);
7618 //        this.store.on('load', this.onLoad, this);
7619 //        
7620 //        this.store.load();
7621 //        
7622 //    },
7623 //    
7624 //    onLoad: function () 
7625 //    {   
7626 //        this.fireEvent('load', this);
7627 //    }
7628 //    
7629 //   
7630 });
7631
7632  
7633
7634  /*
7635  * Based on:
7636  * Ext JS Library 1.1.1
7637  * Copyright(c) 2006-2007, Ext JS, LLC.
7638  *
7639  * Originally Released Under LGPL - original licence link has changed is not relivant.
7640  *
7641  * Fork - LGPL
7642  * <script type="text/javascript">
7643  */
7644
7645 // as we use this in bootstrap.
7646 Roo.namespace('Roo.form');
7647  /**
7648  * @class Roo.form.Action
7649  * Internal Class used to handle form actions
7650  * @constructor
7651  * @param {Roo.form.BasicForm} el The form element or its id
7652  * @param {Object} config Configuration options
7653  */
7654
7655  
7656  
7657 // define the action interface
7658 Roo.form.Action = function(form, options){
7659     this.form = form;
7660     this.options = options || {};
7661 };
7662 /**
7663  * Client Validation Failed
7664  * @const 
7665  */
7666 Roo.form.Action.CLIENT_INVALID = 'client';
7667 /**
7668  * Server Validation Failed
7669  * @const 
7670  */
7671 Roo.form.Action.SERVER_INVALID = 'server';
7672  /**
7673  * Connect to Server Failed
7674  * @const 
7675  */
7676 Roo.form.Action.CONNECT_FAILURE = 'connect';
7677 /**
7678  * Reading Data from Server Failed
7679  * @const 
7680  */
7681 Roo.form.Action.LOAD_FAILURE = 'load';
7682
7683 Roo.form.Action.prototype = {
7684     type : 'default',
7685     failureType : undefined,
7686     response : undefined,
7687     result : undefined,
7688
7689     // interface method
7690     run : function(options){
7691
7692     },
7693
7694     // interface method
7695     success : function(response){
7696
7697     },
7698
7699     // interface method
7700     handleResponse : function(response){
7701
7702     },
7703
7704     // default connection failure
7705     failure : function(response){
7706         
7707         this.response = response;
7708         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7709         this.form.afterAction(this, false);
7710     },
7711
7712     processResponse : function(response){
7713         this.response = response;
7714         if(!response.responseText){
7715             return true;
7716         }
7717         this.result = this.handleResponse(response);
7718         return this.result;
7719     },
7720
7721     // utility functions used internally
7722     getUrl : function(appendParams){
7723         var url = this.options.url || this.form.url || this.form.el.dom.action;
7724         if(appendParams){
7725             var p = this.getParams();
7726             if(p){
7727                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7728             }
7729         }
7730         return url;
7731     },
7732
7733     getMethod : function(){
7734         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7735     },
7736
7737     getParams : function(){
7738         var bp = this.form.baseParams;
7739         var p = this.options.params;
7740         if(p){
7741             if(typeof p == "object"){
7742                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7743             }else if(typeof p == 'string' && bp){
7744                 p += '&' + Roo.urlEncode(bp);
7745             }
7746         }else if(bp){
7747             p = Roo.urlEncode(bp);
7748         }
7749         return p;
7750     },
7751
7752     createCallback : function(){
7753         return {
7754             success: this.success,
7755             failure: this.failure,
7756             scope: this,
7757             timeout: (this.form.timeout*1000),
7758             upload: this.form.fileUpload ? this.success : undefined
7759         };
7760     }
7761 };
7762
7763 Roo.form.Action.Submit = function(form, options){
7764     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7765 };
7766
7767 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7768     type : 'submit',
7769
7770     haveProgress : false,
7771     uploadComplete : false,
7772     
7773     // uploadProgress indicator.
7774     uploadProgress : function()
7775     {
7776         if (!this.form.progressUrl) {
7777             return;
7778         }
7779         
7780         if (!this.haveProgress) {
7781             Roo.MessageBox.progress("Uploading", "Uploading");
7782         }
7783         if (this.uploadComplete) {
7784            Roo.MessageBox.hide();
7785            return;
7786         }
7787         
7788         this.haveProgress = true;
7789    
7790         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7791         
7792         var c = new Roo.data.Connection();
7793         c.request({
7794             url : this.form.progressUrl,
7795             params: {
7796                 id : uid
7797             },
7798             method: 'GET',
7799             success : function(req){
7800                //console.log(data);
7801                 var rdata = false;
7802                 var edata;
7803                 try  {
7804                    rdata = Roo.decode(req.responseText)
7805                 } catch (e) {
7806                     Roo.log("Invalid data from server..");
7807                     Roo.log(edata);
7808                     return;
7809                 }
7810                 if (!rdata || !rdata.success) {
7811                     Roo.log(rdata);
7812                     Roo.MessageBox.alert(Roo.encode(rdata));
7813                     return;
7814                 }
7815                 var data = rdata.data;
7816                 
7817                 if (this.uploadComplete) {
7818                    Roo.MessageBox.hide();
7819                    return;
7820                 }
7821                    
7822                 if (data){
7823                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7824                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7825                     );
7826                 }
7827                 this.uploadProgress.defer(2000,this);
7828             },
7829        
7830             failure: function(data) {
7831                 Roo.log('progress url failed ');
7832                 Roo.log(data);
7833             },
7834             scope : this
7835         });
7836            
7837     },
7838     
7839     
7840     run : function()
7841     {
7842         // run get Values on the form, so it syncs any secondary forms.
7843         this.form.getValues();
7844         
7845         var o = this.options;
7846         var method = this.getMethod();
7847         var isPost = method == 'POST';
7848         if(o.clientValidation === false || this.form.isValid()){
7849             
7850             if (this.form.progressUrl) {
7851                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7852                     (new Date() * 1) + '' + Math.random());
7853                     
7854             } 
7855             
7856             
7857             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7858                 form:this.form.el.dom,
7859                 url:this.getUrl(!isPost),
7860                 method: method,
7861                 params:isPost ? this.getParams() : null,
7862                 isUpload: this.form.fileUpload
7863             }));
7864             
7865             this.uploadProgress();
7866
7867         }else if (o.clientValidation !== false){ // client validation failed
7868             this.failureType = Roo.form.Action.CLIENT_INVALID;
7869             this.form.afterAction(this, false);
7870         }
7871     },
7872
7873     success : function(response)
7874     {
7875         this.uploadComplete= true;
7876         if (this.haveProgress) {
7877             Roo.MessageBox.hide();
7878         }
7879         
7880         
7881         var result = this.processResponse(response);
7882         if(result === true || result.success){
7883             this.form.afterAction(this, true);
7884             return;
7885         }
7886         if(result.errors){
7887             this.form.markInvalid(result.errors);
7888             this.failureType = Roo.form.Action.SERVER_INVALID;
7889         }
7890         this.form.afterAction(this, false);
7891     },
7892     failure : function(response)
7893     {
7894         this.uploadComplete= true;
7895         if (this.haveProgress) {
7896             Roo.MessageBox.hide();
7897         }
7898         
7899         this.response = response;
7900         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7901         this.form.afterAction(this, false);
7902     },
7903     
7904     handleResponse : function(response){
7905         if(this.form.errorReader){
7906             var rs = this.form.errorReader.read(response);
7907             var errors = [];
7908             if(rs.records){
7909                 for(var i = 0, len = rs.records.length; i < len; i++) {
7910                     var r = rs.records[i];
7911                     errors[i] = r.data;
7912                 }
7913             }
7914             if(errors.length < 1){
7915                 errors = null;
7916             }
7917             return {
7918                 success : rs.success,
7919                 errors : errors
7920             };
7921         }
7922         var ret = false;
7923         try {
7924             ret = Roo.decode(response.responseText);
7925         } catch (e) {
7926             ret = {
7927                 success: false,
7928                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7929                 errors : []
7930             };
7931         }
7932         return ret;
7933         
7934     }
7935 });
7936
7937
7938 Roo.form.Action.Load = function(form, options){
7939     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7940     this.reader = this.form.reader;
7941 };
7942
7943 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7944     type : 'load',
7945
7946     run : function(){
7947         
7948         Roo.Ajax.request(Roo.apply(
7949                 this.createCallback(), {
7950                     method:this.getMethod(),
7951                     url:this.getUrl(false),
7952                     params:this.getParams()
7953         }));
7954     },
7955
7956     success : function(response){
7957         
7958         var result = this.processResponse(response);
7959         if(result === true || !result.success || !result.data){
7960             this.failureType = Roo.form.Action.LOAD_FAILURE;
7961             this.form.afterAction(this, false);
7962             return;
7963         }
7964         this.form.clearInvalid();
7965         this.form.setValues(result.data);
7966         this.form.afterAction(this, true);
7967     },
7968
7969     handleResponse : function(response){
7970         if(this.form.reader){
7971             var rs = this.form.reader.read(response);
7972             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7973             return {
7974                 success : rs.success,
7975                 data : data
7976             };
7977         }
7978         return Roo.decode(response.responseText);
7979     }
7980 });
7981
7982 Roo.form.Action.ACTION_TYPES = {
7983     'load' : Roo.form.Action.Load,
7984     'submit' : Roo.form.Action.Submit
7985 };/*
7986  * - LGPL
7987  *
7988  * form
7989  *
7990  */
7991
7992 /**
7993  * @class Roo.bootstrap.Form
7994  * @extends Roo.bootstrap.Component
7995  * Bootstrap Form class
7996  * @cfg {String} method  GET | POST (default POST)
7997  * @cfg {String} labelAlign top | left (default top)
7998  * @cfg {String} align left  | right - for navbars
7999  * @cfg {Boolean} loadMask load mask when submit (default true)
8000
8001  *
8002  * @constructor
8003  * Create a new Form
8004  * @param {Object} config The config object
8005  */
8006
8007
8008 Roo.bootstrap.Form = function(config){
8009     
8010     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8011     
8012     Roo.bootstrap.Form.popover.apply();
8013     
8014     this.addEvents({
8015         /**
8016          * @event clientvalidation
8017          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8018          * @param {Form} this
8019          * @param {Boolean} valid true if the form has passed client-side validation
8020          */
8021         clientvalidation: true,
8022         /**
8023          * @event beforeaction
8024          * Fires before any action is performed. Return false to cancel the action.
8025          * @param {Form} this
8026          * @param {Action} action The action to be performed
8027          */
8028         beforeaction: true,
8029         /**
8030          * @event actionfailed
8031          * Fires when an action fails.
8032          * @param {Form} this
8033          * @param {Action} action The action that failed
8034          */
8035         actionfailed : true,
8036         /**
8037          * @event actioncomplete
8038          * Fires when an action is completed.
8039          * @param {Form} this
8040          * @param {Action} action The action that completed
8041          */
8042         actioncomplete : true
8043     });
8044 };
8045
8046 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8047
8048      /**
8049      * @cfg {String} method
8050      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8051      */
8052     method : 'POST',
8053     /**
8054      * @cfg {String} url
8055      * The URL to use for form actions if one isn't supplied in the action options.
8056      */
8057     /**
8058      * @cfg {Boolean} fileUpload
8059      * Set to true if this form is a file upload.
8060      */
8061
8062     /**
8063      * @cfg {Object} baseParams
8064      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8065      */
8066
8067     /**
8068      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8069      */
8070     timeout: 30,
8071     /**
8072      * @cfg {Sting} align (left|right) for navbar forms
8073      */
8074     align : 'left',
8075
8076     // private
8077     activeAction : null,
8078
8079     /**
8080      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8081      * element by passing it or its id or mask the form itself by passing in true.
8082      * @type Mixed
8083      */
8084     waitMsgTarget : false,
8085
8086     loadMask : true,
8087     
8088     /**
8089      * @cfg {Boolean} errorMask (true|false) default false
8090      */
8091     errorMask : false,
8092     
8093     /**
8094      * @cfg {Number} maskOffset Default 100
8095      */
8096     maskOffset : 100,
8097     
8098     /**
8099      * @cfg {Boolean} maskBody
8100      */
8101     maskBody : false,
8102
8103     getAutoCreate : function(){
8104
8105         var cfg = {
8106             tag: 'form',
8107             method : this.method || 'POST',
8108             id : this.id || Roo.id(),
8109             cls : ''
8110         };
8111         if (this.parent().xtype.match(/^Nav/)) {
8112             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8113
8114         }
8115
8116         if (this.labelAlign == 'left' ) {
8117             cfg.cls += ' form-horizontal';
8118         }
8119
8120
8121         return cfg;
8122     },
8123     initEvents : function()
8124     {
8125         this.el.on('submit', this.onSubmit, this);
8126         // this was added as random key presses on the form where triggering form submit.
8127         this.el.on('keypress', function(e) {
8128             if (e.getCharCode() != 13) {
8129                 return true;
8130             }
8131             // we might need to allow it for textareas.. and some other items.
8132             // check e.getTarget().
8133
8134             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8135                 return true;
8136             }
8137
8138             Roo.log("keypress blocked");
8139
8140             e.preventDefault();
8141             return false;
8142         });
8143         
8144     },
8145     // private
8146     onSubmit : function(e){
8147         e.stopEvent();
8148     },
8149
8150      /**
8151      * Returns true if client-side validation on the form is successful.
8152      * @return Boolean
8153      */
8154     isValid : function(){
8155         var items = this.getItems();
8156         var valid = true;
8157         var target = false;
8158         
8159         items.each(function(f){
8160             
8161             if(f.validate()){
8162                 return;
8163             }
8164             
8165             Roo.log('invalid field: ' + f.name);
8166             
8167             valid = false;
8168
8169             if(!target && f.el.isVisible(true)){
8170                 target = f;
8171             }
8172            
8173         });
8174         
8175         if(this.errorMask && !valid){
8176             Roo.bootstrap.Form.popover.mask(this, target);
8177         }
8178         
8179         return valid;
8180     },
8181     
8182     /**
8183      * Returns true if any fields in this form have changed since their original load.
8184      * @return Boolean
8185      */
8186     isDirty : function(){
8187         var dirty = false;
8188         var items = this.getItems();
8189         items.each(function(f){
8190            if(f.isDirty()){
8191                dirty = true;
8192                return false;
8193            }
8194            return true;
8195         });
8196         return dirty;
8197     },
8198      /**
8199      * Performs a predefined action (submit or load) or custom actions you define on this form.
8200      * @param {String} actionName The name of the action type
8201      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8202      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8203      * accept other config options):
8204      * <pre>
8205 Property          Type             Description
8206 ----------------  ---------------  ----------------------------------------------------------------------------------
8207 url               String           The url for the action (defaults to the form's url)
8208 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8209 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8210 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8211                                    validate the form on the client (defaults to false)
8212      * </pre>
8213      * @return {BasicForm} this
8214      */
8215     doAction : function(action, options){
8216         if(typeof action == 'string'){
8217             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8218         }
8219         if(this.fireEvent('beforeaction', this, action) !== false){
8220             this.beforeAction(action);
8221             action.run.defer(100, action);
8222         }
8223         return this;
8224     },
8225
8226     // private
8227     beforeAction : function(action){
8228         var o = action.options;
8229         
8230         if(this.loadMask){
8231             
8232             if(this.maskBody){
8233                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8234             } else {
8235                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8236             }
8237         }
8238         // not really supported yet.. ??
8239
8240         //if(this.waitMsgTarget === true){
8241         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8242         //}else if(this.waitMsgTarget){
8243         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8244         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8245         //}else {
8246         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8247        // }
8248
8249     },
8250
8251     // private
8252     afterAction : function(action, success){
8253         this.activeAction = null;
8254         var o = action.options;
8255
8256         if(this.loadMask){
8257             
8258             if(this.maskBody){
8259                 Roo.get(document.body).unmask();
8260             } else {
8261                 this.el.unmask();
8262             }
8263         }
8264         
8265         //if(this.waitMsgTarget === true){
8266 //            this.el.unmask();
8267         //}else if(this.waitMsgTarget){
8268         //    this.waitMsgTarget.unmask();
8269         //}else{
8270         //    Roo.MessageBox.updateProgress(1);
8271         //    Roo.MessageBox.hide();
8272        // }
8273         //
8274         if(success){
8275             if(o.reset){
8276                 this.reset();
8277             }
8278             Roo.callback(o.success, o.scope, [this, action]);
8279             this.fireEvent('actioncomplete', this, action);
8280
8281         }else{
8282
8283             // failure condition..
8284             // we have a scenario where updates need confirming.
8285             // eg. if a locking scenario exists..
8286             // we look for { errors : { needs_confirm : true }} in the response.
8287             if (
8288                 (typeof(action.result) != 'undefined')  &&
8289                 (typeof(action.result.errors) != 'undefined')  &&
8290                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8291            ){
8292                 var _t = this;
8293                 Roo.log("not supported yet");
8294                  /*
8295
8296                 Roo.MessageBox.confirm(
8297                     "Change requires confirmation",
8298                     action.result.errorMsg,
8299                     function(r) {
8300                         if (r != 'yes') {
8301                             return;
8302                         }
8303                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8304                     }
8305
8306                 );
8307                 */
8308
8309
8310                 return;
8311             }
8312
8313             Roo.callback(o.failure, o.scope, [this, action]);
8314             // show an error message if no failed handler is set..
8315             if (!this.hasListener('actionfailed')) {
8316                 Roo.log("need to add dialog support");
8317                 /*
8318                 Roo.MessageBox.alert("Error",
8319                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8320                         action.result.errorMsg :
8321                         "Saving Failed, please check your entries or try again"
8322                 );
8323                 */
8324             }
8325
8326             this.fireEvent('actionfailed', this, action);
8327         }
8328
8329     },
8330     /**
8331      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8332      * @param {String} id The value to search for
8333      * @return Field
8334      */
8335     findField : function(id){
8336         var items = this.getItems();
8337         var field = items.get(id);
8338         if(!field){
8339              items.each(function(f){
8340                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8341                     field = f;
8342                     return false;
8343                 }
8344                 return true;
8345             });
8346         }
8347         return field || null;
8348     },
8349      /**
8350      * Mark fields in this form invalid in bulk.
8351      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8352      * @return {BasicForm} this
8353      */
8354     markInvalid : function(errors){
8355         if(errors instanceof Array){
8356             for(var i = 0, len = errors.length; i < len; i++){
8357                 var fieldError = errors[i];
8358                 var f = this.findField(fieldError.id);
8359                 if(f){
8360                     f.markInvalid(fieldError.msg);
8361                 }
8362             }
8363         }else{
8364             var field, id;
8365             for(id in errors){
8366                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8367                     field.markInvalid(errors[id]);
8368                 }
8369             }
8370         }
8371         //Roo.each(this.childForms || [], function (f) {
8372         //    f.markInvalid(errors);
8373         //});
8374
8375         return this;
8376     },
8377
8378     /**
8379      * Set values for fields in this form in bulk.
8380      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8381      * @return {BasicForm} this
8382      */
8383     setValues : function(values){
8384         if(values instanceof Array){ // array of objects
8385             for(var i = 0, len = values.length; i < len; i++){
8386                 var v = values[i];
8387                 var f = this.findField(v.id);
8388                 if(f){
8389                     f.setValue(v.value);
8390                     if(this.trackResetOnLoad){
8391                         f.originalValue = f.getValue();
8392                     }
8393                 }
8394             }
8395         }else{ // object hash
8396             var field, id;
8397             for(id in values){
8398                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8399
8400                     if (field.setFromData &&
8401                         field.valueField &&
8402                         field.displayField &&
8403                         // combos' with local stores can
8404                         // be queried via setValue()
8405                         // to set their value..
8406                         (field.store && !field.store.isLocal)
8407                         ) {
8408                         // it's a combo
8409                         var sd = { };
8410                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8411                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8412                         field.setFromData(sd);
8413
8414                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8415                         
8416                         field.setFromData(values);
8417                         
8418                     } else {
8419                         field.setValue(values[id]);
8420                     }
8421
8422
8423                     if(this.trackResetOnLoad){
8424                         field.originalValue = field.getValue();
8425                     }
8426                 }
8427             }
8428         }
8429
8430         //Roo.each(this.childForms || [], function (f) {
8431         //    f.setValues(values);
8432         //});
8433
8434         return this;
8435     },
8436
8437     /**
8438      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8439      * they are returned as an array.
8440      * @param {Boolean} asString
8441      * @return {Object}
8442      */
8443     getValues : function(asString){
8444         //if (this.childForms) {
8445             // copy values from the child forms
8446         //    Roo.each(this.childForms, function (f) {
8447         //        this.setValues(f.getValues());
8448         //    }, this);
8449         //}
8450
8451
8452
8453         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8454         if(asString === true){
8455             return fs;
8456         }
8457         return Roo.urlDecode(fs);
8458     },
8459
8460     /**
8461      * Returns the fields in this form as an object with key/value pairs.
8462      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8463      * @return {Object}
8464      */
8465     getFieldValues : function(with_hidden)
8466     {
8467         var items = this.getItems();
8468         var ret = {};
8469         items.each(function(f){
8470             
8471             if (!f.getName()) {
8472                 return;
8473             }
8474             
8475             var v = f.getValue();
8476             
8477             if (f.inputType =='radio') {
8478                 if (typeof(ret[f.getName()]) == 'undefined') {
8479                     ret[f.getName()] = ''; // empty..
8480                 }
8481
8482                 if (!f.el.dom.checked) {
8483                     return;
8484
8485                 }
8486                 v = f.el.dom.value;
8487
8488             }
8489             
8490             if(f.xtype == 'MoneyField'){
8491                 ret[f.currencyName] = f.getCurrency();
8492             }
8493
8494             // not sure if this supported any more..
8495             if ((typeof(v) == 'object') && f.getRawValue) {
8496                 v = f.getRawValue() ; // dates..
8497             }
8498             // combo boxes where name != hiddenName...
8499             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8500                 ret[f.name] = f.getRawValue();
8501             }
8502             ret[f.getName()] = v;
8503         });
8504
8505         return ret;
8506     },
8507
8508     /**
8509      * Clears all invalid messages in this form.
8510      * @return {BasicForm} this
8511      */
8512     clearInvalid : function(){
8513         var items = this.getItems();
8514
8515         items.each(function(f){
8516            f.clearInvalid();
8517         });
8518
8519         return this;
8520     },
8521
8522     /**
8523      * Resets this form.
8524      * @return {BasicForm} this
8525      */
8526     reset : function(){
8527         var items = this.getItems();
8528         items.each(function(f){
8529             f.reset();
8530         });
8531
8532         Roo.each(this.childForms || [], function (f) {
8533             f.reset();
8534         });
8535
8536
8537         return this;
8538     },
8539     
8540     getItems : function()
8541     {
8542         var r=new Roo.util.MixedCollection(false, function(o){
8543             return o.id || (o.id = Roo.id());
8544         });
8545         var iter = function(el) {
8546             if (el.inputEl) {
8547                 r.add(el);
8548             }
8549             if (!el.items) {
8550                 return;
8551             }
8552             Roo.each(el.items,function(e) {
8553                 iter(e);
8554             });
8555         };
8556
8557         iter(this);
8558         return r;
8559     },
8560     
8561     hideFields : function(items)
8562     {
8563         Roo.each(items, function(i){
8564             
8565             var f = this.findField(i);
8566             
8567             if(!f){
8568                 return;
8569             }
8570             
8571             f.hide();
8572             
8573         }, this);
8574     },
8575     
8576     showFields : function(items)
8577     {
8578         Roo.each(items, function(i){
8579             
8580             var f = this.findField(i);
8581             
8582             if(!f){
8583                 return;
8584             }
8585             
8586             f.show();
8587             
8588         }, this);
8589     }
8590
8591 });
8592
8593 Roo.apply(Roo.bootstrap.Form, {
8594     
8595     popover : {
8596         
8597         padding : 5,
8598         
8599         isApplied : false,
8600         
8601         isMasked : false,
8602         
8603         form : false,
8604         
8605         target : false,
8606         
8607         toolTip : false,
8608         
8609         intervalID : false,
8610         
8611         maskEl : false,
8612         
8613         apply : function()
8614         {
8615             if(this.isApplied){
8616                 return;
8617             }
8618             
8619             this.maskEl = {
8620                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8621                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8622                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8623                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8624             };
8625             
8626             this.maskEl.top.enableDisplayMode("block");
8627             this.maskEl.left.enableDisplayMode("block");
8628             this.maskEl.bottom.enableDisplayMode("block");
8629             this.maskEl.right.enableDisplayMode("block");
8630             
8631             this.toolTip = new Roo.bootstrap.Tooltip({
8632                 cls : 'roo-form-error-popover',
8633                 alignment : {
8634                     'left' : ['r-l', [-2,0], 'right'],
8635                     'right' : ['l-r', [2,0], 'left'],
8636                     'bottom' : ['tl-bl', [0,2], 'top'],
8637                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8638                 }
8639             });
8640             
8641             this.toolTip.render(Roo.get(document.body));
8642
8643             this.toolTip.el.enableDisplayMode("block");
8644             
8645             Roo.get(document.body).on('click', function(){
8646                 this.unmask();
8647             }, this);
8648             
8649             Roo.get(document.body).on('touchstart', function(){
8650                 this.unmask();
8651             }, this);
8652             
8653             this.isApplied = true
8654         },
8655         
8656         mask : function(form, target)
8657         {
8658             this.form = form;
8659             
8660             this.target = target;
8661             
8662             if(!this.form.errorMask || !target.el){
8663                 return;
8664             }
8665             
8666             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8667             
8668             Roo.log(scrollable);
8669             
8670             var ot = this.target.el.calcOffsetsTo(scrollable);
8671             
8672             var scrollTo = ot[1] - this.form.maskOffset;
8673             
8674             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8675             
8676             scrollable.scrollTo('top', scrollTo);
8677             
8678             var box = this.target.el.getBox();
8679             Roo.log(box);
8680             var zIndex = Roo.bootstrap.Modal.zIndex++;
8681
8682             
8683             this.maskEl.top.setStyle('position', 'absolute');
8684             this.maskEl.top.setStyle('z-index', zIndex);
8685             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8686             this.maskEl.top.setLeft(0);
8687             this.maskEl.top.setTop(0);
8688             this.maskEl.top.show();
8689             
8690             this.maskEl.left.setStyle('position', 'absolute');
8691             this.maskEl.left.setStyle('z-index', zIndex);
8692             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8693             this.maskEl.left.setLeft(0);
8694             this.maskEl.left.setTop(box.y - this.padding);
8695             this.maskEl.left.show();
8696
8697             this.maskEl.bottom.setStyle('position', 'absolute');
8698             this.maskEl.bottom.setStyle('z-index', zIndex);
8699             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8700             this.maskEl.bottom.setLeft(0);
8701             this.maskEl.bottom.setTop(box.bottom + this.padding);
8702             this.maskEl.bottom.show();
8703
8704             this.maskEl.right.setStyle('position', 'absolute');
8705             this.maskEl.right.setStyle('z-index', zIndex);
8706             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8707             this.maskEl.right.setLeft(box.right + this.padding);
8708             this.maskEl.right.setTop(box.y - this.padding);
8709             this.maskEl.right.show();
8710
8711             this.toolTip.bindEl = this.target.el;
8712
8713             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8714
8715             var tip = this.target.blankText;
8716
8717             if(this.target.getValue() !== '' ) {
8718                 
8719                 if (this.target.invalidText.length) {
8720                     tip = this.target.invalidText;
8721                 } else if (this.target.regexText.length){
8722                     tip = this.target.regexText;
8723                 }
8724             }
8725
8726             this.toolTip.show(tip);
8727
8728             this.intervalID = window.setInterval(function() {
8729                 Roo.bootstrap.Form.popover.unmask();
8730             }, 10000);
8731
8732             window.onwheel = function(){ return false;};
8733             
8734             (function(){ this.isMasked = true; }).defer(500, this);
8735             
8736         },
8737         
8738         unmask : function()
8739         {
8740             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8741                 return;
8742             }
8743             
8744             this.maskEl.top.setStyle('position', 'absolute');
8745             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8746             this.maskEl.top.hide();
8747
8748             this.maskEl.left.setStyle('position', 'absolute');
8749             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8750             this.maskEl.left.hide();
8751
8752             this.maskEl.bottom.setStyle('position', 'absolute');
8753             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8754             this.maskEl.bottom.hide();
8755
8756             this.maskEl.right.setStyle('position', 'absolute');
8757             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8758             this.maskEl.right.hide();
8759             
8760             this.toolTip.hide();
8761             
8762             this.toolTip.el.hide();
8763             
8764             window.onwheel = function(){ return true;};
8765             
8766             if(this.intervalID){
8767                 window.clearInterval(this.intervalID);
8768                 this.intervalID = false;
8769             }
8770             
8771             this.isMasked = false;
8772             
8773         }
8774         
8775     }
8776     
8777 });
8778
8779 /*
8780  * Based on:
8781  * Ext JS Library 1.1.1
8782  * Copyright(c) 2006-2007, Ext JS, LLC.
8783  *
8784  * Originally Released Under LGPL - original licence link has changed is not relivant.
8785  *
8786  * Fork - LGPL
8787  * <script type="text/javascript">
8788  */
8789 /**
8790  * @class Roo.form.VTypes
8791  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8792  * @singleton
8793  */
8794 Roo.form.VTypes = function(){
8795     // closure these in so they are only created once.
8796     var alpha = /^[a-zA-Z_]+$/;
8797     var alphanum = /^[a-zA-Z0-9_]+$/;
8798     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8799     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8800
8801     // All these messages and functions are configurable
8802     return {
8803         /**
8804          * The function used to validate email addresses
8805          * @param {String} value The email address
8806          */
8807         'email' : function(v){
8808             return email.test(v);
8809         },
8810         /**
8811          * The error text to display when the email validation function returns false
8812          * @type String
8813          */
8814         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8815         /**
8816          * The keystroke filter mask to be applied on email input
8817          * @type RegExp
8818          */
8819         'emailMask' : /[a-z0-9_\.\-@]/i,
8820
8821         /**
8822          * The function used to validate URLs
8823          * @param {String} value The URL
8824          */
8825         'url' : function(v){
8826             return url.test(v);
8827         },
8828         /**
8829          * The error text to display when the url validation function returns false
8830          * @type String
8831          */
8832         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8833         
8834         /**
8835          * The function used to validate alpha values
8836          * @param {String} value The value
8837          */
8838         'alpha' : function(v){
8839             return alpha.test(v);
8840         },
8841         /**
8842          * The error text to display when the alpha validation function returns false
8843          * @type String
8844          */
8845         'alphaText' : 'This field should only contain letters and _',
8846         /**
8847          * The keystroke filter mask to be applied on alpha input
8848          * @type RegExp
8849          */
8850         'alphaMask' : /[a-z_]/i,
8851
8852         /**
8853          * The function used to validate alphanumeric values
8854          * @param {String} value The value
8855          */
8856         'alphanum' : function(v){
8857             return alphanum.test(v);
8858         },
8859         /**
8860          * The error text to display when the alphanumeric validation function returns false
8861          * @type String
8862          */
8863         'alphanumText' : 'This field should only contain letters, numbers and _',
8864         /**
8865          * The keystroke filter mask to be applied on alphanumeric input
8866          * @type RegExp
8867          */
8868         'alphanumMask' : /[a-z0-9_]/i
8869     };
8870 }();/*
8871  * - LGPL
8872  *
8873  * Input
8874  * 
8875  */
8876
8877 /**
8878  * @class Roo.bootstrap.Input
8879  * @extends Roo.bootstrap.Component
8880  * Bootstrap Input class
8881  * @cfg {Boolean} disabled is it disabled
8882  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8883  * @cfg {String} name name of the input
8884  * @cfg {string} fieldLabel - the label associated
8885  * @cfg {string} placeholder - placeholder to put in text.
8886  * @cfg {string}  before - input group add on before
8887  * @cfg {string} after - input group add on after
8888  * @cfg {string} size - (lg|sm) or leave empty..
8889  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8890  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8891  * @cfg {Number} md colspan out of 12 for computer-sized screens
8892  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8893  * @cfg {string} value default value of the input
8894  * @cfg {Number} labelWidth set the width of label 
8895  * @cfg {Number} labellg set the width of label (1-12)
8896  * @cfg {Number} labelmd set the width of label (1-12)
8897  * @cfg {Number} labelsm set the width of label (1-12)
8898  * @cfg {Number} labelxs set the width of label (1-12)
8899  * @cfg {String} labelAlign (top|left)
8900  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8901  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8902  * @cfg {String} indicatorpos (left|right) default left
8903  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8904  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8905
8906  * @cfg {String} align (left|center|right) Default left
8907  * @cfg {Boolean} forceFeedback (true|false) Default false
8908  * 
8909  * @constructor
8910  * Create a new Input
8911  * @param {Object} config The config object
8912  */
8913
8914 Roo.bootstrap.Input = function(config){
8915     
8916     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8917     
8918     this.addEvents({
8919         /**
8920          * @event focus
8921          * Fires when this field receives input focus.
8922          * @param {Roo.form.Field} this
8923          */
8924         focus : true,
8925         /**
8926          * @event blur
8927          * Fires when this field loses input focus.
8928          * @param {Roo.form.Field} this
8929          */
8930         blur : true,
8931         /**
8932          * @event specialkey
8933          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8934          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8935          * @param {Roo.form.Field} this
8936          * @param {Roo.EventObject} e The event object
8937          */
8938         specialkey : true,
8939         /**
8940          * @event change
8941          * Fires just before the field blurs if the field value has changed.
8942          * @param {Roo.form.Field} this
8943          * @param {Mixed} newValue The new value
8944          * @param {Mixed} oldValue The original value
8945          */
8946         change : true,
8947         /**
8948          * @event invalid
8949          * Fires after the field has been marked as invalid.
8950          * @param {Roo.form.Field} this
8951          * @param {String} msg The validation message
8952          */
8953         invalid : true,
8954         /**
8955          * @event valid
8956          * Fires after the field has been validated with no errors.
8957          * @param {Roo.form.Field} this
8958          */
8959         valid : true,
8960          /**
8961          * @event keyup
8962          * Fires after the key up
8963          * @param {Roo.form.Field} this
8964          * @param {Roo.EventObject}  e The event Object
8965          */
8966         keyup : true
8967     });
8968 };
8969
8970 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8971      /**
8972      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8973       automatic validation (defaults to "keyup").
8974      */
8975     validationEvent : "keyup",
8976      /**
8977      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8978      */
8979     validateOnBlur : true,
8980     /**
8981      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8982      */
8983     validationDelay : 250,
8984      /**
8985      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8986      */
8987     focusClass : "x-form-focus",  // not needed???
8988     
8989        
8990     /**
8991      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8992      */
8993     invalidClass : "has-warning",
8994     
8995     /**
8996      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8997      */
8998     validClass : "has-success",
8999     
9000     /**
9001      * @cfg {Boolean} hasFeedback (true|false) default true
9002      */
9003     hasFeedback : true,
9004     
9005     /**
9006      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9007      */
9008     invalidFeedbackClass : "glyphicon-warning-sign",
9009     
9010     /**
9011      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9012      */
9013     validFeedbackClass : "glyphicon-ok",
9014     
9015     /**
9016      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9017      */
9018     selectOnFocus : false,
9019     
9020      /**
9021      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9022      */
9023     maskRe : null,
9024        /**
9025      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9026      */
9027     vtype : null,
9028     
9029       /**
9030      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9031      */
9032     disableKeyFilter : false,
9033     
9034        /**
9035      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9036      */
9037     disabled : false,
9038      /**
9039      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9040      */
9041     allowBlank : true,
9042     /**
9043      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9044      */
9045     blankText : "Please complete this mandatory field",
9046     
9047      /**
9048      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9049      */
9050     minLength : 0,
9051     /**
9052      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9053      */
9054     maxLength : Number.MAX_VALUE,
9055     /**
9056      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9057      */
9058     minLengthText : "The minimum length for this field is {0}",
9059     /**
9060      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9061      */
9062     maxLengthText : "The maximum length for this field is {0}",
9063   
9064     
9065     /**
9066      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9067      * If available, this function will be called only after the basic validators all return true, and will be passed the
9068      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9069      */
9070     validator : null,
9071     /**
9072      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9073      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9074      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9075      */
9076     regex : null,
9077     /**
9078      * @cfg {String} regexText -- Depricated - use Invalid Text
9079      */
9080     regexText : "",
9081     
9082     /**
9083      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9084      */
9085     invalidText : "",
9086     
9087     
9088     
9089     autocomplete: false,
9090     
9091     
9092     fieldLabel : '',
9093     inputType : 'text',
9094     
9095     name : false,
9096     placeholder: false,
9097     before : false,
9098     after : false,
9099     size : false,
9100     hasFocus : false,
9101     preventMark: false,
9102     isFormField : true,
9103     value : '',
9104     labelWidth : 2,
9105     labelAlign : false,
9106     readOnly : false,
9107     align : false,
9108     formatedValue : false,
9109     forceFeedback : false,
9110     
9111     indicatorpos : 'left',
9112     
9113     labellg : 0,
9114     labelmd : 0,
9115     labelsm : 0,
9116     labelxs : 0,
9117     
9118     capture : '',
9119     accept : '',
9120     
9121     parentLabelAlign : function()
9122     {
9123         var parent = this;
9124         while (parent.parent()) {
9125             parent = parent.parent();
9126             if (typeof(parent.labelAlign) !='undefined') {
9127                 return parent.labelAlign;
9128             }
9129         }
9130         return 'left';
9131         
9132     },
9133     
9134     getAutoCreate : function()
9135     {
9136         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9137         
9138         var id = Roo.id();
9139         
9140         var cfg = {};
9141         
9142         if(this.inputType != 'hidden'){
9143             cfg.cls = 'form-group' //input-group
9144         }
9145         
9146         var input =  {
9147             tag: 'input',
9148             id : id,
9149             type : this.inputType,
9150             value : this.value,
9151             cls : 'form-control',
9152             placeholder : this.placeholder || '',
9153             autocomplete : this.autocomplete || 'new-password'
9154         };
9155         
9156         if(this.capture.length){
9157             input.capture = this.capture;
9158         }
9159         
9160         if(this.accept.length){
9161             input.accept = this.accept + "/*";
9162         }
9163         
9164         if(this.align){
9165             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9166         }
9167         
9168         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9169             input.maxLength = this.maxLength;
9170         }
9171         
9172         if (this.disabled) {
9173             input.disabled=true;
9174         }
9175         
9176         if (this.readOnly) {
9177             input.readonly=true;
9178         }
9179         
9180         if (this.name) {
9181             input.name = this.name;
9182         }
9183         
9184         if (this.size) {
9185             input.cls += ' input-' + this.size;
9186         }
9187         
9188         var settings=this;
9189         ['xs','sm','md','lg'].map(function(size){
9190             if (settings[size]) {
9191                 cfg.cls += ' col-' + size + '-' + settings[size];
9192             }
9193         });
9194         
9195         var inputblock = input;
9196         
9197         var feedback = {
9198             tag: 'span',
9199             cls: 'glyphicon form-control-feedback'
9200         };
9201             
9202         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9203             
9204             inputblock = {
9205                 cls : 'has-feedback',
9206                 cn :  [
9207                     input,
9208                     feedback
9209                 ] 
9210             };  
9211         }
9212         
9213         if (this.before || this.after) {
9214             
9215             inputblock = {
9216                 cls : 'input-group',
9217                 cn :  [] 
9218             };
9219             
9220             if (this.before && typeof(this.before) == 'string') {
9221                 
9222                 inputblock.cn.push({
9223                     tag :'span',
9224                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9225                     html : this.before
9226                 });
9227             }
9228             if (this.before && typeof(this.before) == 'object') {
9229                 this.before = Roo.factory(this.before);
9230                 
9231                 inputblock.cn.push({
9232                     tag :'span',
9233                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9234                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9235                 });
9236             }
9237             
9238             inputblock.cn.push(input);
9239             
9240             if (this.after && typeof(this.after) == 'string') {
9241                 inputblock.cn.push({
9242                     tag :'span',
9243                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9244                     html : this.after
9245                 });
9246             }
9247             if (this.after && typeof(this.after) == 'object') {
9248                 this.after = Roo.factory(this.after);
9249                 
9250                 inputblock.cn.push({
9251                     tag :'span',
9252                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9253                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9254                 });
9255             }
9256             
9257             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9258                 inputblock.cls += ' has-feedback';
9259                 inputblock.cn.push(feedback);
9260             }
9261         };
9262         var indicator = {
9263             tag : 'i',
9264             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9265             tooltip : 'This field is required'
9266         };
9267         if (Roo.bootstrap.version == 4) {
9268             indicator = {
9269                 tag : 'i',
9270                 style : 'display-none'
9271             };
9272         }
9273         if (align ==='left' && this.fieldLabel.length) {
9274             
9275             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9276             
9277             cfg.cn = [
9278                 indicator,
9279                 {
9280                     tag: 'label',
9281                     'for' :  id,
9282                     cls : 'control-label col-form-label',
9283                     html : this.fieldLabel
9284
9285                 },
9286                 {
9287                     cls : "", 
9288                     cn: [
9289                         inputblock
9290                     ]
9291                 }
9292             ];
9293             
9294             var labelCfg = cfg.cn[1];
9295             var contentCfg = cfg.cn[2];
9296             
9297             if(this.indicatorpos == 'right'){
9298                 cfg.cn = [
9299                     {
9300                         tag: 'label',
9301                         'for' :  id,
9302                         cls : 'control-label col-form-label',
9303                         cn : [
9304                             {
9305                                 tag : 'span',
9306                                 html : this.fieldLabel
9307                             },
9308                             indicator
9309                         ]
9310                     },
9311                     {
9312                         cls : "",
9313                         cn: [
9314                             inputblock
9315                         ]
9316                     }
9317
9318                 ];
9319                 
9320                 labelCfg = cfg.cn[0];
9321                 contentCfg = cfg.cn[1];
9322             
9323             }
9324             
9325             if(this.labelWidth > 12){
9326                 labelCfg.style = "width: " + this.labelWidth + 'px';
9327             }
9328             
9329             if(this.labelWidth < 13 && this.labelmd == 0){
9330                 this.labelmd = this.labelWidth;
9331             }
9332             
9333             if(this.labellg > 0){
9334                 labelCfg.cls += ' col-lg-' + this.labellg;
9335                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9336             }
9337             
9338             if(this.labelmd > 0){
9339                 labelCfg.cls += ' col-md-' + this.labelmd;
9340                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9341             }
9342             
9343             if(this.labelsm > 0){
9344                 labelCfg.cls += ' col-sm-' + this.labelsm;
9345                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9346             }
9347             
9348             if(this.labelxs > 0){
9349                 labelCfg.cls += ' col-xs-' + this.labelxs;
9350                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9351             }
9352             
9353             
9354         } else if ( this.fieldLabel.length) {
9355                 
9356             cfg.cn = [
9357                 {
9358                     tag : 'i',
9359                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9360                     tooltip : 'This field is required'
9361                 },
9362                 {
9363                     tag: 'label',
9364                    //cls : 'input-group-addon',
9365                     html : this.fieldLabel
9366
9367                 },
9368
9369                inputblock
9370
9371            ];
9372            
9373            if(this.indicatorpos == 'right'){
9374                 
9375                 cfg.cn = [
9376                     {
9377                         tag: 'label',
9378                        //cls : 'input-group-addon',
9379                         html : this.fieldLabel
9380
9381                     },
9382                     {
9383                         tag : 'i',
9384                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9385                         tooltip : 'This field is required'
9386                     },
9387
9388                    inputblock
9389
9390                ];
9391
9392             }
9393
9394         } else {
9395             
9396             cfg.cn = [
9397
9398                     inputblock
9399
9400             ];
9401                 
9402                 
9403         };
9404         
9405         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9406            cfg.cls += ' navbar-form';
9407         }
9408         
9409         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9410             // on BS4 we do this only if not form 
9411             cfg.cls += ' navbar-form';
9412             cfg.tag = 'li';
9413         }
9414         
9415         return cfg;
9416         
9417     },
9418     /**
9419      * return the real input element.
9420      */
9421     inputEl: function ()
9422     {
9423         return this.el.select('input.form-control',true).first();
9424     },
9425     
9426     tooltipEl : function()
9427     {
9428         return this.inputEl();
9429     },
9430     
9431     indicatorEl : function()
9432     {
9433         if (Roo.bootstrap.version == 4) {
9434             return false; // not enabled in v4 yet.
9435         }
9436         
9437         var indicator = this.el.select('i.roo-required-indicator',true).first();
9438         
9439         if(!indicator){
9440             return false;
9441         }
9442         
9443         return indicator;
9444         
9445     },
9446     
9447     setDisabled : function(v)
9448     {
9449         var i  = this.inputEl().dom;
9450         if (!v) {
9451             i.removeAttribute('disabled');
9452             return;
9453             
9454         }
9455         i.setAttribute('disabled','true');
9456     },
9457     initEvents : function()
9458     {
9459           
9460         this.inputEl().on("keydown" , this.fireKey,  this);
9461         this.inputEl().on("focus", this.onFocus,  this);
9462         this.inputEl().on("blur", this.onBlur,  this);
9463         
9464         this.inputEl().relayEvent('keyup', this);
9465         
9466         this.indicator = this.indicatorEl();
9467         
9468         if(this.indicator){
9469             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9470         }
9471  
9472         // reference to original value for reset
9473         this.originalValue = this.getValue();
9474         //Roo.form.TextField.superclass.initEvents.call(this);
9475         if(this.validationEvent == 'keyup'){
9476             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9477             this.inputEl().on('keyup', this.filterValidation, this);
9478         }
9479         else if(this.validationEvent !== false){
9480             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9481         }
9482         
9483         if(this.selectOnFocus){
9484             this.on("focus", this.preFocus, this);
9485             
9486         }
9487         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9488             this.inputEl().on("keypress", this.filterKeys, this);
9489         } else {
9490             this.inputEl().relayEvent('keypress', this);
9491         }
9492        /* if(this.grow){
9493             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9494             this.el.on("click", this.autoSize,  this);
9495         }
9496         */
9497         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9498             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9499         }
9500         
9501         if (typeof(this.before) == 'object') {
9502             this.before.render(this.el.select('.roo-input-before',true).first());
9503         }
9504         if (typeof(this.after) == 'object') {
9505             this.after.render(this.el.select('.roo-input-after',true).first());
9506         }
9507         
9508         this.inputEl().on('change', this.onChange, this);
9509         
9510     },
9511     filterValidation : function(e){
9512         if(!e.isNavKeyPress()){
9513             this.validationTask.delay(this.validationDelay);
9514         }
9515     },
9516      /**
9517      * Validates the field value
9518      * @return {Boolean} True if the value is valid, else false
9519      */
9520     validate : function(){
9521         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9522         if(this.disabled || this.validateValue(this.getRawValue())){
9523             this.markValid();
9524             return true;
9525         }
9526         
9527         this.markInvalid();
9528         return false;
9529     },
9530     
9531     
9532     /**
9533      * Validates a value according to the field's validation rules and marks the field as invalid
9534      * if the validation fails
9535      * @param {Mixed} value The value to validate
9536      * @return {Boolean} True if the value is valid, else false
9537      */
9538     validateValue : function(value)
9539     {
9540         if(this.getVisibilityEl().hasClass('hidden')){
9541             return true;
9542         }
9543         
9544         if(value.length < 1)  { // if it's blank
9545             if(this.allowBlank){
9546                 return true;
9547             }
9548             return false;
9549         }
9550         
9551         if(value.length < this.minLength){
9552             return false;
9553         }
9554         if(value.length > this.maxLength){
9555             return false;
9556         }
9557         if(this.vtype){
9558             var vt = Roo.form.VTypes;
9559             if(!vt[this.vtype](value, this)){
9560                 return false;
9561             }
9562         }
9563         if(typeof this.validator == "function"){
9564             var msg = this.validator(value);
9565             if(msg !== true){
9566                 return false;
9567             }
9568             if (typeof(msg) == 'string') {
9569                 this.invalidText = msg;
9570             }
9571         }
9572         
9573         if(this.regex && !this.regex.test(value)){
9574             return false;
9575         }
9576         
9577         return true;
9578     },
9579     
9580      // private
9581     fireKey : function(e){
9582         //Roo.log('field ' + e.getKey());
9583         if(e.isNavKeyPress()){
9584             this.fireEvent("specialkey", this, e);
9585         }
9586     },
9587     focus : function (selectText){
9588         if(this.rendered){
9589             this.inputEl().focus();
9590             if(selectText === true){
9591                 this.inputEl().dom.select();
9592             }
9593         }
9594         return this;
9595     } ,
9596     
9597     onFocus : function(){
9598         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9599            // this.el.addClass(this.focusClass);
9600         }
9601         if(!this.hasFocus){
9602             this.hasFocus = true;
9603             this.startValue = this.getValue();
9604             this.fireEvent("focus", this);
9605         }
9606     },
9607     
9608     beforeBlur : Roo.emptyFn,
9609
9610     
9611     // private
9612     onBlur : function(){
9613         this.beforeBlur();
9614         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9615             //this.el.removeClass(this.focusClass);
9616         }
9617         this.hasFocus = false;
9618         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9619             this.validate();
9620         }
9621         var v = this.getValue();
9622         if(String(v) !== String(this.startValue)){
9623             this.fireEvent('change', this, v, this.startValue);
9624         }
9625         this.fireEvent("blur", this);
9626     },
9627     
9628     onChange : function(e)
9629     {
9630         var v = this.getValue();
9631         if(String(v) !== String(this.startValue)){
9632             this.fireEvent('change', this, v, this.startValue);
9633         }
9634         
9635     },
9636     
9637     /**
9638      * Resets the current field value to the originally loaded value and clears any validation messages
9639      */
9640     reset : function(){
9641         this.setValue(this.originalValue);
9642         this.validate();
9643     },
9644      /**
9645      * Returns the name of the field
9646      * @return {Mixed} name The name field
9647      */
9648     getName: function(){
9649         return this.name;
9650     },
9651      /**
9652      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9653      * @return {Mixed} value The field value
9654      */
9655     getValue : function(){
9656         
9657         var v = this.inputEl().getValue();
9658         
9659         return v;
9660     },
9661     /**
9662      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9663      * @return {Mixed} value The field value
9664      */
9665     getRawValue : function(){
9666         var v = this.inputEl().getValue();
9667         
9668         return v;
9669     },
9670     
9671     /**
9672      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9673      * @param {Mixed} value The value to set
9674      */
9675     setRawValue : function(v){
9676         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9677     },
9678     
9679     selectText : function(start, end){
9680         var v = this.getRawValue();
9681         if(v.length > 0){
9682             start = start === undefined ? 0 : start;
9683             end = end === undefined ? v.length : end;
9684             var d = this.inputEl().dom;
9685             if(d.setSelectionRange){
9686                 d.setSelectionRange(start, end);
9687             }else if(d.createTextRange){
9688                 var range = d.createTextRange();
9689                 range.moveStart("character", start);
9690                 range.moveEnd("character", v.length-end);
9691                 range.select();
9692             }
9693         }
9694     },
9695     
9696     /**
9697      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9698      * @param {Mixed} value The value to set
9699      */
9700     setValue : function(v){
9701         this.value = v;
9702         if(this.rendered){
9703             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9704             this.validate();
9705         }
9706     },
9707     
9708     /*
9709     processValue : function(value){
9710         if(this.stripCharsRe){
9711             var newValue = value.replace(this.stripCharsRe, '');
9712             if(newValue !== value){
9713                 this.setRawValue(newValue);
9714                 return newValue;
9715             }
9716         }
9717         return value;
9718     },
9719   */
9720     preFocus : function(){
9721         
9722         if(this.selectOnFocus){
9723             this.inputEl().dom.select();
9724         }
9725     },
9726     filterKeys : function(e){
9727         var k = e.getKey();
9728         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9729             return;
9730         }
9731         var c = e.getCharCode(), cc = String.fromCharCode(c);
9732         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9733             return;
9734         }
9735         if(!this.maskRe.test(cc)){
9736             e.stopEvent();
9737         }
9738     },
9739      /**
9740      * Clear any invalid styles/messages for this field
9741      */
9742     clearInvalid : function(){
9743         
9744         if(!this.el || this.preventMark){ // not rendered
9745             return;
9746         }
9747         
9748      
9749         this.el.removeClass(this.invalidClass);
9750         
9751         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9752             
9753             var feedback = this.el.select('.form-control-feedback', true).first();
9754             
9755             if(feedback){
9756                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9757             }
9758             
9759         }
9760         
9761         if(this.indicator){
9762             this.indicator.removeClass('visible');
9763             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9764         }
9765         
9766         this.fireEvent('valid', this);
9767     },
9768     
9769      /**
9770      * Mark this field as valid
9771      */
9772     markValid : function()
9773     {
9774         if(!this.el  || this.preventMark){ // not rendered...
9775             return;
9776         }
9777         
9778         this.el.removeClass([this.invalidClass, this.validClass]);
9779         
9780         var feedback = this.el.select('.form-control-feedback', true).first();
9781             
9782         if(feedback){
9783             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9784         }
9785         
9786         if(this.indicator){
9787             this.indicator.removeClass('visible');
9788             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9789         }
9790         
9791         if(this.disabled){
9792             return;
9793         }
9794         
9795         if(this.allowBlank && !this.getRawValue().length){
9796             return;
9797         }
9798         
9799         this.el.addClass(this.validClass);
9800         
9801         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9802             
9803             var feedback = this.el.select('.form-control-feedback', true).first();
9804             
9805             if(feedback){
9806                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9807                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9808             }
9809             
9810         }
9811         
9812         this.fireEvent('valid', this);
9813     },
9814     
9815      /**
9816      * Mark this field as invalid
9817      * @param {String} msg The validation message
9818      */
9819     markInvalid : function(msg)
9820     {
9821         if(!this.el  || this.preventMark){ // not rendered
9822             return;
9823         }
9824         
9825         this.el.removeClass([this.invalidClass, this.validClass]);
9826         
9827         var feedback = this.el.select('.form-control-feedback', true).first();
9828             
9829         if(feedback){
9830             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9831         }
9832
9833         if(this.disabled){
9834             return;
9835         }
9836         
9837         if(this.allowBlank && !this.getRawValue().length){
9838             return;
9839         }
9840         
9841         if(this.indicator){
9842             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9843             this.indicator.addClass('visible');
9844         }
9845         
9846         this.el.addClass(this.invalidClass);
9847         
9848         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9849             
9850             var feedback = this.el.select('.form-control-feedback', true).first();
9851             
9852             if(feedback){
9853                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9854                 
9855                 if(this.getValue().length || this.forceFeedback){
9856                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9857                 }
9858                 
9859             }
9860             
9861         }
9862         
9863         this.fireEvent('invalid', this, msg);
9864     },
9865     // private
9866     SafariOnKeyDown : function(event)
9867     {
9868         // this is a workaround for a password hang bug on chrome/ webkit.
9869         if (this.inputEl().dom.type != 'password') {
9870             return;
9871         }
9872         
9873         var isSelectAll = false;
9874         
9875         if(this.inputEl().dom.selectionEnd > 0){
9876             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9877         }
9878         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9879             event.preventDefault();
9880             this.setValue('');
9881             return;
9882         }
9883         
9884         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9885             
9886             event.preventDefault();
9887             // this is very hacky as keydown always get's upper case.
9888             //
9889             var cc = String.fromCharCode(event.getCharCode());
9890             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9891             
9892         }
9893     },
9894     adjustWidth : function(tag, w){
9895         tag = tag.toLowerCase();
9896         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9897             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9898                 if(tag == 'input'){
9899                     return w + 2;
9900                 }
9901                 if(tag == 'textarea'){
9902                     return w-2;
9903                 }
9904             }else if(Roo.isOpera){
9905                 if(tag == 'input'){
9906                     return w + 2;
9907                 }
9908                 if(tag == 'textarea'){
9909                     return w-2;
9910                 }
9911             }
9912         }
9913         return w;
9914     },
9915     
9916     setFieldLabel : function(v)
9917     {
9918         if(!this.rendered){
9919             return;
9920         }
9921         
9922         if(this.indicatorEl()){
9923             var ar = this.el.select('label > span',true);
9924             
9925             if (ar.elements.length) {
9926                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9927                 this.fieldLabel = v;
9928                 return;
9929             }
9930             
9931             var br = this.el.select('label',true);
9932             
9933             if(br.elements.length) {
9934                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9935                 this.fieldLabel = v;
9936                 return;
9937             }
9938             
9939             Roo.log('Cannot Found any of label > span || label in input');
9940             return;
9941         }
9942         
9943         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9944         this.fieldLabel = v;
9945         
9946         
9947     }
9948 });
9949
9950  
9951 /*
9952  * - LGPL
9953  *
9954  * Input
9955  * 
9956  */
9957
9958 /**
9959  * @class Roo.bootstrap.TextArea
9960  * @extends Roo.bootstrap.Input
9961  * Bootstrap TextArea class
9962  * @cfg {Number} cols Specifies the visible width of a text area
9963  * @cfg {Number} rows Specifies the visible number of lines in a text area
9964  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9965  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9966  * @cfg {string} html text
9967  * 
9968  * @constructor
9969  * Create a new TextArea
9970  * @param {Object} config The config object
9971  */
9972
9973 Roo.bootstrap.TextArea = function(config){
9974     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9975    
9976 };
9977
9978 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9979      
9980     cols : false,
9981     rows : 5,
9982     readOnly : false,
9983     warp : 'soft',
9984     resize : false,
9985     value: false,
9986     html: false,
9987     
9988     getAutoCreate : function(){
9989         
9990         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9991         
9992         var id = Roo.id();
9993         
9994         var cfg = {};
9995         
9996         if(this.inputType != 'hidden'){
9997             cfg.cls = 'form-group' //input-group
9998         }
9999         
10000         var input =  {
10001             tag: 'textarea',
10002             id : id,
10003             warp : this.warp,
10004             rows : this.rows,
10005             value : this.value || '',
10006             html: this.html || '',
10007             cls : 'form-control',
10008             placeholder : this.placeholder || '' 
10009             
10010         };
10011         
10012         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10013             input.maxLength = this.maxLength;
10014         }
10015         
10016         if(this.resize){
10017             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10018         }
10019         
10020         if(this.cols){
10021             input.cols = this.cols;
10022         }
10023         
10024         if (this.readOnly) {
10025             input.readonly = true;
10026         }
10027         
10028         if (this.name) {
10029             input.name = this.name;
10030         }
10031         
10032         if (this.size) {
10033             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10034         }
10035         
10036         var settings=this;
10037         ['xs','sm','md','lg'].map(function(size){
10038             if (settings[size]) {
10039                 cfg.cls += ' col-' + size + '-' + settings[size];
10040             }
10041         });
10042         
10043         var inputblock = input;
10044         
10045         if(this.hasFeedback && !this.allowBlank){
10046             
10047             var feedback = {
10048                 tag: 'span',
10049                 cls: 'glyphicon form-control-feedback'
10050             };
10051
10052             inputblock = {
10053                 cls : 'has-feedback',
10054                 cn :  [
10055                     input,
10056                     feedback
10057                 ] 
10058             };  
10059         }
10060         
10061         
10062         if (this.before || this.after) {
10063             
10064             inputblock = {
10065                 cls : 'input-group',
10066                 cn :  [] 
10067             };
10068             if (this.before) {
10069                 inputblock.cn.push({
10070                     tag :'span',
10071                     cls : 'input-group-addon',
10072                     html : this.before
10073                 });
10074             }
10075             
10076             inputblock.cn.push(input);
10077             
10078             if(this.hasFeedback && !this.allowBlank){
10079                 inputblock.cls += ' has-feedback';
10080                 inputblock.cn.push(feedback);
10081             }
10082             
10083             if (this.after) {
10084                 inputblock.cn.push({
10085                     tag :'span',
10086                     cls : 'input-group-addon',
10087                     html : this.after
10088                 });
10089             }
10090             
10091         }
10092         
10093         if (align ==='left' && this.fieldLabel.length) {
10094             cfg.cn = [
10095                 {
10096                     tag: 'label',
10097                     'for' :  id,
10098                     cls : 'control-label',
10099                     html : this.fieldLabel
10100                 },
10101                 {
10102                     cls : "",
10103                     cn: [
10104                         inputblock
10105                     ]
10106                 }
10107
10108             ];
10109             
10110             if(this.labelWidth > 12){
10111                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10112             }
10113
10114             if(this.labelWidth < 13 && this.labelmd == 0){
10115                 this.labelmd = this.labelWidth;
10116             }
10117
10118             if(this.labellg > 0){
10119                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10120                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10121             }
10122
10123             if(this.labelmd > 0){
10124                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10125                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10126             }
10127
10128             if(this.labelsm > 0){
10129                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10130                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10131             }
10132
10133             if(this.labelxs > 0){
10134                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10135                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10136             }
10137             
10138         } else if ( this.fieldLabel.length) {
10139             cfg.cn = [
10140
10141                {
10142                    tag: 'label',
10143                    //cls : 'input-group-addon',
10144                    html : this.fieldLabel
10145
10146                },
10147
10148                inputblock
10149
10150            ];
10151
10152         } else {
10153
10154             cfg.cn = [
10155
10156                 inputblock
10157
10158             ];
10159                 
10160         }
10161         
10162         if (this.disabled) {
10163             input.disabled=true;
10164         }
10165         
10166         return cfg;
10167         
10168     },
10169     /**
10170      * return the real textarea element.
10171      */
10172     inputEl: function ()
10173     {
10174         return this.el.select('textarea.form-control',true).first();
10175     },
10176     
10177     /**
10178      * Clear any invalid styles/messages for this field
10179      */
10180     clearInvalid : function()
10181     {
10182         
10183         if(!this.el || this.preventMark){ // not rendered
10184             return;
10185         }
10186         
10187         var label = this.el.select('label', true).first();
10188         var icon = this.el.select('i.fa-star', true).first();
10189         
10190         if(label && icon){
10191             icon.remove();
10192         }
10193         
10194         this.el.removeClass(this.invalidClass);
10195         
10196         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10197             
10198             var feedback = this.el.select('.form-control-feedback', true).first();
10199             
10200             if(feedback){
10201                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10202             }
10203             
10204         }
10205         
10206         this.fireEvent('valid', this);
10207     },
10208     
10209      /**
10210      * Mark this field as valid
10211      */
10212     markValid : function()
10213     {
10214         if(!this.el  || this.preventMark){ // not rendered
10215             return;
10216         }
10217         
10218         this.el.removeClass([this.invalidClass, this.validClass]);
10219         
10220         var feedback = this.el.select('.form-control-feedback', true).first();
10221             
10222         if(feedback){
10223             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10224         }
10225
10226         if(this.disabled || this.allowBlank){
10227             return;
10228         }
10229         
10230         var label = this.el.select('label', true).first();
10231         var icon = this.el.select('i.fa-star', true).first();
10232         
10233         if(label && icon){
10234             icon.remove();
10235         }
10236         
10237         this.el.addClass(this.validClass);
10238         
10239         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10240             
10241             var feedback = this.el.select('.form-control-feedback', true).first();
10242             
10243             if(feedback){
10244                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10245                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10246             }
10247             
10248         }
10249         
10250         this.fireEvent('valid', this);
10251     },
10252     
10253      /**
10254      * Mark this field as invalid
10255      * @param {String} msg The validation message
10256      */
10257     markInvalid : function(msg)
10258     {
10259         if(!this.el  || this.preventMark){ // not rendered
10260             return;
10261         }
10262         
10263         this.el.removeClass([this.invalidClass, this.validClass]);
10264         
10265         var feedback = this.el.select('.form-control-feedback', true).first();
10266             
10267         if(feedback){
10268             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10269         }
10270
10271         if(this.disabled || this.allowBlank){
10272             return;
10273         }
10274         
10275         var label = this.el.select('label', true).first();
10276         var icon = this.el.select('i.fa-star', true).first();
10277         
10278         if(!this.getValue().length && label && !icon){
10279             this.el.createChild({
10280                 tag : 'i',
10281                 cls : 'text-danger fa fa-lg fa-star',
10282                 tooltip : 'This field is required',
10283                 style : 'margin-right:5px;'
10284             }, label, true);
10285         }
10286
10287         this.el.addClass(this.invalidClass);
10288         
10289         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10290             
10291             var feedback = this.el.select('.form-control-feedback', true).first();
10292             
10293             if(feedback){
10294                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10295                 
10296                 if(this.getValue().length || this.forceFeedback){
10297                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10298                 }
10299                 
10300             }
10301             
10302         }
10303         
10304         this.fireEvent('invalid', this, msg);
10305     }
10306 });
10307
10308  
10309 /*
10310  * - LGPL
10311  *
10312  * trigger field - base class for combo..
10313  * 
10314  */
10315  
10316 /**
10317  * @class Roo.bootstrap.TriggerField
10318  * @extends Roo.bootstrap.Input
10319  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10320  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10321  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10322  * for which you can provide a custom implementation.  For example:
10323  * <pre><code>
10324 var trigger = new Roo.bootstrap.TriggerField();
10325 trigger.onTriggerClick = myTriggerFn;
10326 trigger.applyTo('my-field');
10327 </code></pre>
10328  *
10329  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10330  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10331  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10332  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10333  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10334
10335  * @constructor
10336  * Create a new TriggerField.
10337  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10338  * to the base TextField)
10339  */
10340 Roo.bootstrap.TriggerField = function(config){
10341     this.mimicing = false;
10342     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10343 };
10344
10345 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10346     /**
10347      * @cfg {String} triggerClass A CSS class to apply to the trigger
10348      */
10349      /**
10350      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10351      */
10352     hideTrigger:false,
10353
10354     /**
10355      * @cfg {Boolean} removable (true|false) special filter default false
10356      */
10357     removable : false,
10358     
10359     /** @cfg {Boolean} grow @hide */
10360     /** @cfg {Number} growMin @hide */
10361     /** @cfg {Number} growMax @hide */
10362
10363     /**
10364      * @hide 
10365      * @method
10366      */
10367     autoSize: Roo.emptyFn,
10368     // private
10369     monitorTab : true,
10370     // private
10371     deferHeight : true,
10372
10373     
10374     actionMode : 'wrap',
10375     
10376     caret : false,
10377     
10378     
10379     getAutoCreate : function(){
10380        
10381         var align = this.labelAlign || this.parentLabelAlign();
10382         
10383         var id = Roo.id();
10384         
10385         var cfg = {
10386             cls: 'form-group' //input-group
10387         };
10388         
10389         
10390         var input =  {
10391             tag: 'input',
10392             id : id,
10393             type : this.inputType,
10394             cls : 'form-control',
10395             autocomplete: 'new-password',
10396             placeholder : this.placeholder || '' 
10397             
10398         };
10399         if (this.name) {
10400             input.name = this.name;
10401         }
10402         if (this.size) {
10403             input.cls += ' input-' + this.size;
10404         }
10405         
10406         if (this.disabled) {
10407             input.disabled=true;
10408         }
10409         
10410         var inputblock = input;
10411         
10412         if(this.hasFeedback && !this.allowBlank){
10413             
10414             var feedback = {
10415                 tag: 'span',
10416                 cls: 'glyphicon form-control-feedback'
10417             };
10418             
10419             if(this.removable && !this.editable && !this.tickable){
10420                 inputblock = {
10421                     cls : 'has-feedback',
10422                     cn :  [
10423                         inputblock,
10424                         {
10425                             tag: 'button',
10426                             html : 'x',
10427                             cls : 'roo-combo-removable-btn close'
10428                         },
10429                         feedback
10430                     ] 
10431                 };
10432             } else {
10433                 inputblock = {
10434                     cls : 'has-feedback',
10435                     cn :  [
10436                         inputblock,
10437                         feedback
10438                     ] 
10439                 };
10440             }
10441
10442         } else {
10443             if(this.removable && !this.editable && !this.tickable){
10444                 inputblock = {
10445                     cls : 'roo-removable',
10446                     cn :  [
10447                         inputblock,
10448                         {
10449                             tag: 'button',
10450                             html : 'x',
10451                             cls : 'roo-combo-removable-btn close'
10452                         }
10453                     ] 
10454                 };
10455             }
10456         }
10457         
10458         if (this.before || this.after) {
10459             
10460             inputblock = {
10461                 cls : 'input-group',
10462                 cn :  [] 
10463             };
10464             if (this.before) {
10465                 inputblock.cn.push({
10466                     tag :'span',
10467                     cls : 'input-group-addon input-group-prepend input-group-text',
10468                     html : this.before
10469                 });
10470             }
10471             
10472             inputblock.cn.push(input);
10473             
10474             if(this.hasFeedback && !this.allowBlank){
10475                 inputblock.cls += ' has-feedback';
10476                 inputblock.cn.push(feedback);
10477             }
10478             
10479             if (this.after) {
10480                 inputblock.cn.push({
10481                     tag :'span',
10482                     cls : 'input-group-addon input-group-append input-group-text',
10483                     html : this.after
10484                 });
10485             }
10486             
10487         };
10488         
10489       
10490         
10491         var ibwrap = inputblock;
10492         
10493         if(this.multiple){
10494             ibwrap = {
10495                 tag: 'ul',
10496                 cls: 'roo-select2-choices',
10497                 cn:[
10498                     {
10499                         tag: 'li',
10500                         cls: 'roo-select2-search-field',
10501                         cn: [
10502
10503                             inputblock
10504                         ]
10505                     }
10506                 ]
10507             };
10508                 
10509         }
10510         
10511         var combobox = {
10512             cls: 'roo-select2-container input-group',
10513             cn: [
10514                  {
10515                     tag: 'input',
10516                     type : 'hidden',
10517                     cls: 'form-hidden-field'
10518                 },
10519                 ibwrap
10520             ]
10521         };
10522         
10523         if(!this.multiple && this.showToggleBtn){
10524             
10525             var caret = {
10526                         tag: 'span',
10527                         cls: 'caret'
10528              };
10529             if (this.caret != false) {
10530                 caret = {
10531                      tag: 'i',
10532                      cls: 'fa fa-' + this.caret
10533                 };
10534                 
10535             }
10536             
10537             combobox.cn.push({
10538                 tag :'span',
10539                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10540                 cn : [
10541                     caret,
10542                     {
10543                         tag: 'span',
10544                         cls: 'combobox-clear',
10545                         cn  : [
10546                             {
10547                                 tag : 'i',
10548                                 cls: 'icon-remove'
10549                             }
10550                         ]
10551                     }
10552                 ]
10553
10554             })
10555         }
10556         
10557         if(this.multiple){
10558             combobox.cls += ' roo-select2-container-multi';
10559         }
10560          var indicator = {
10561             tag : 'i',
10562             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10563             tooltip : 'This field is required'
10564         };
10565         if (Roo.bootstrap.version == 4) {
10566             indicator = {
10567                 tag : 'i',
10568                 style : 'display:none'
10569             };
10570         }
10571         
10572         
10573         if (align ==='left' && this.fieldLabel.length) {
10574             
10575             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10576
10577             cfg.cn = [
10578                 indicator,
10579                 {
10580                     tag: 'label',
10581                     'for' :  id,
10582                     cls : 'control-label',
10583                     html : this.fieldLabel
10584
10585                 },
10586                 {
10587                     cls : "", 
10588                     cn: [
10589                         combobox
10590                     ]
10591                 }
10592
10593             ];
10594             
10595             var labelCfg = cfg.cn[1];
10596             var contentCfg = cfg.cn[2];
10597             
10598             if(this.indicatorpos == 'right'){
10599                 cfg.cn = [
10600                     {
10601                         tag: 'label',
10602                         'for' :  id,
10603                         cls : 'control-label',
10604                         cn : [
10605                             {
10606                                 tag : 'span',
10607                                 html : this.fieldLabel
10608                             },
10609                             indicator
10610                         ]
10611                     },
10612                     {
10613                         cls : "", 
10614                         cn: [
10615                             combobox
10616                         ]
10617                     }
10618
10619                 ];
10620                 
10621                 labelCfg = cfg.cn[0];
10622                 contentCfg = cfg.cn[1];
10623             }
10624             
10625             if(this.labelWidth > 12){
10626                 labelCfg.style = "width: " + this.labelWidth + 'px';
10627             }
10628             
10629             if(this.labelWidth < 13 && this.labelmd == 0){
10630                 this.labelmd = this.labelWidth;
10631             }
10632             
10633             if(this.labellg > 0){
10634                 labelCfg.cls += ' col-lg-' + this.labellg;
10635                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10636             }
10637             
10638             if(this.labelmd > 0){
10639                 labelCfg.cls += ' col-md-' + this.labelmd;
10640                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10641             }
10642             
10643             if(this.labelsm > 0){
10644                 labelCfg.cls += ' col-sm-' + this.labelsm;
10645                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10646             }
10647             
10648             if(this.labelxs > 0){
10649                 labelCfg.cls += ' col-xs-' + this.labelxs;
10650                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10651             }
10652             
10653         } else if ( this.fieldLabel.length) {
10654 //                Roo.log(" label");
10655             cfg.cn = [
10656                 indicator,
10657                {
10658                    tag: 'label',
10659                    //cls : 'input-group-addon',
10660                    html : this.fieldLabel
10661
10662                },
10663
10664                combobox
10665
10666             ];
10667             
10668             if(this.indicatorpos == 'right'){
10669                 
10670                 cfg.cn = [
10671                     {
10672                        tag: 'label',
10673                        cn : [
10674                            {
10675                                tag : 'span',
10676                                html : this.fieldLabel
10677                            },
10678                            indicator
10679                        ]
10680
10681                     },
10682                     combobox
10683
10684                 ];
10685
10686             }
10687
10688         } else {
10689             
10690 //                Roo.log(" no label && no align");
10691                 cfg = combobox
10692                      
10693                 
10694         }
10695         
10696         var settings=this;
10697         ['xs','sm','md','lg'].map(function(size){
10698             if (settings[size]) {
10699                 cfg.cls += ' col-' + size + '-' + settings[size];
10700             }
10701         });
10702         
10703         return cfg;
10704         
10705     },
10706     
10707     
10708     
10709     // private
10710     onResize : function(w, h){
10711 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10712 //        if(typeof w == 'number'){
10713 //            var x = w - this.trigger.getWidth();
10714 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10715 //            this.trigger.setStyle('left', x+'px');
10716 //        }
10717     },
10718
10719     // private
10720     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10721
10722     // private
10723     getResizeEl : function(){
10724         return this.inputEl();
10725     },
10726
10727     // private
10728     getPositionEl : function(){
10729         return this.inputEl();
10730     },
10731
10732     // private
10733     alignErrorIcon : function(){
10734         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10735     },
10736
10737     // private
10738     initEvents : function(){
10739         
10740         this.createList();
10741         
10742         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10743         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10744         if(!this.multiple && this.showToggleBtn){
10745             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10746             if(this.hideTrigger){
10747                 this.trigger.setDisplayed(false);
10748             }
10749             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10750         }
10751         
10752         if(this.multiple){
10753             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10754         }
10755         
10756         if(this.removable && !this.editable && !this.tickable){
10757             var close = this.closeTriggerEl();
10758             
10759             if(close){
10760                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10761                 close.on('click', this.removeBtnClick, this, close);
10762             }
10763         }
10764         
10765         //this.trigger.addClassOnOver('x-form-trigger-over');
10766         //this.trigger.addClassOnClick('x-form-trigger-click');
10767         
10768         //if(!this.width){
10769         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10770         //}
10771     },
10772     
10773     closeTriggerEl : function()
10774     {
10775         var close = this.el.select('.roo-combo-removable-btn', true).first();
10776         return close ? close : false;
10777     },
10778     
10779     removeBtnClick : function(e, h, el)
10780     {
10781         e.preventDefault();
10782         
10783         if(this.fireEvent("remove", this) !== false){
10784             this.reset();
10785             this.fireEvent("afterremove", this)
10786         }
10787     },
10788     
10789     createList : function()
10790     {
10791         this.list = Roo.get(document.body).createChild({
10792             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10793             cls: 'typeahead typeahead-long dropdown-menu',
10794             style: 'display:none'
10795         });
10796         
10797         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10798         
10799     },
10800
10801     // private
10802     initTrigger : function(){
10803        
10804     },
10805
10806     // private
10807     onDestroy : function(){
10808         if(this.trigger){
10809             this.trigger.removeAllListeners();
10810           //  this.trigger.remove();
10811         }
10812         //if(this.wrap){
10813         //    this.wrap.remove();
10814         //}
10815         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10816     },
10817
10818     // private
10819     onFocus : function(){
10820         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10821         /*
10822         if(!this.mimicing){
10823             this.wrap.addClass('x-trigger-wrap-focus');
10824             this.mimicing = true;
10825             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10826             if(this.monitorTab){
10827                 this.el.on("keydown", this.checkTab, this);
10828             }
10829         }
10830         */
10831     },
10832
10833     // private
10834     checkTab : function(e){
10835         if(e.getKey() == e.TAB){
10836             this.triggerBlur();
10837         }
10838     },
10839
10840     // private
10841     onBlur : function(){
10842         // do nothing
10843     },
10844
10845     // private
10846     mimicBlur : function(e, t){
10847         /*
10848         if(!this.wrap.contains(t) && this.validateBlur()){
10849             this.triggerBlur();
10850         }
10851         */
10852     },
10853
10854     // private
10855     triggerBlur : function(){
10856         this.mimicing = false;
10857         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10858         if(this.monitorTab){
10859             this.el.un("keydown", this.checkTab, this);
10860         }
10861         //this.wrap.removeClass('x-trigger-wrap-focus');
10862         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10863     },
10864
10865     // private
10866     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10867     validateBlur : function(e, t){
10868         return true;
10869     },
10870
10871     // private
10872     onDisable : function(){
10873         this.inputEl().dom.disabled = true;
10874         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10875         //if(this.wrap){
10876         //    this.wrap.addClass('x-item-disabled');
10877         //}
10878     },
10879
10880     // private
10881     onEnable : function(){
10882         this.inputEl().dom.disabled = false;
10883         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10884         //if(this.wrap){
10885         //    this.el.removeClass('x-item-disabled');
10886         //}
10887     },
10888
10889     // private
10890     onShow : function(){
10891         var ae = this.getActionEl();
10892         
10893         if(ae){
10894             ae.dom.style.display = '';
10895             ae.dom.style.visibility = 'visible';
10896         }
10897     },
10898
10899     // private
10900     
10901     onHide : function(){
10902         var ae = this.getActionEl();
10903         ae.dom.style.display = 'none';
10904     },
10905
10906     /**
10907      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10908      * by an implementing function.
10909      * @method
10910      * @param {EventObject} e
10911      */
10912     onTriggerClick : Roo.emptyFn
10913 });
10914  /*
10915  * Based on:
10916  * Ext JS Library 1.1.1
10917  * Copyright(c) 2006-2007, Ext JS, LLC.
10918  *
10919  * Originally Released Under LGPL - original licence link has changed is not relivant.
10920  *
10921  * Fork - LGPL
10922  * <script type="text/javascript">
10923  */
10924
10925
10926 /**
10927  * @class Roo.data.SortTypes
10928  * @singleton
10929  * Defines the default sorting (casting?) comparison functions used when sorting data.
10930  */
10931 Roo.data.SortTypes = {
10932     /**
10933      * Default sort that does nothing
10934      * @param {Mixed} s The value being converted
10935      * @return {Mixed} The comparison value
10936      */
10937     none : function(s){
10938         return s;
10939     },
10940     
10941     /**
10942      * The regular expression used to strip tags
10943      * @type {RegExp}
10944      * @property
10945      */
10946     stripTagsRE : /<\/?[^>]+>/gi,
10947     
10948     /**
10949      * Strips all HTML tags to sort on text only
10950      * @param {Mixed} s The value being converted
10951      * @return {String} The comparison value
10952      */
10953     asText : function(s){
10954         return String(s).replace(this.stripTagsRE, "");
10955     },
10956     
10957     /**
10958      * Strips all HTML tags to sort on text only - Case insensitive
10959      * @param {Mixed} s The value being converted
10960      * @return {String} The comparison value
10961      */
10962     asUCText : function(s){
10963         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10964     },
10965     
10966     /**
10967      * Case insensitive string
10968      * @param {Mixed} s The value being converted
10969      * @return {String} The comparison value
10970      */
10971     asUCString : function(s) {
10972         return String(s).toUpperCase();
10973     },
10974     
10975     /**
10976      * Date sorting
10977      * @param {Mixed} s The value being converted
10978      * @return {Number} The comparison value
10979      */
10980     asDate : function(s) {
10981         if(!s){
10982             return 0;
10983         }
10984         if(s instanceof Date){
10985             return s.getTime();
10986         }
10987         return Date.parse(String(s));
10988     },
10989     
10990     /**
10991      * Float sorting
10992      * @param {Mixed} s The value being converted
10993      * @return {Float} The comparison value
10994      */
10995     asFloat : function(s) {
10996         var val = parseFloat(String(s).replace(/,/g, ""));
10997         if(isNaN(val)) {
10998             val = 0;
10999         }
11000         return val;
11001     },
11002     
11003     /**
11004      * Integer sorting
11005      * @param {Mixed} s The value being converted
11006      * @return {Number} The comparison value
11007      */
11008     asInt : function(s) {
11009         var val = parseInt(String(s).replace(/,/g, ""));
11010         if(isNaN(val)) {
11011             val = 0;
11012         }
11013         return val;
11014     }
11015 };/*
11016  * Based on:
11017  * Ext JS Library 1.1.1
11018  * Copyright(c) 2006-2007, Ext JS, LLC.
11019  *
11020  * Originally Released Under LGPL - original licence link has changed is not relivant.
11021  *
11022  * Fork - LGPL
11023  * <script type="text/javascript">
11024  */
11025
11026 /**
11027 * @class Roo.data.Record
11028  * Instances of this class encapsulate both record <em>definition</em> information, and record
11029  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11030  * to access Records cached in an {@link Roo.data.Store} object.<br>
11031  * <p>
11032  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11033  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11034  * objects.<br>
11035  * <p>
11036  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11037  * @constructor
11038  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11039  * {@link #create}. The parameters are the same.
11040  * @param {Array} data An associative Array of data values keyed by the field name.
11041  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11042  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11043  * not specified an integer id is generated.
11044  */
11045 Roo.data.Record = function(data, id){
11046     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11047     this.data = data;
11048 };
11049
11050 /**
11051  * Generate a constructor for a specific record layout.
11052  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11053  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11054  * Each field definition object may contain the following properties: <ul>
11055  * <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,
11056  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11057  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11058  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11059  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11060  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11061  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11062  * this may be omitted.</p></li>
11063  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11064  * <ul><li>auto (Default, implies no conversion)</li>
11065  * <li>string</li>
11066  * <li>int</li>
11067  * <li>float</li>
11068  * <li>boolean</li>
11069  * <li>date</li></ul></p></li>
11070  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11071  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11072  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11073  * by the Reader into an object that will be stored in the Record. It is passed the
11074  * following parameters:<ul>
11075  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11076  * </ul></p></li>
11077  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11078  * </ul>
11079  * <br>usage:<br><pre><code>
11080 var TopicRecord = Roo.data.Record.create(
11081     {name: 'title', mapping: 'topic_title'},
11082     {name: 'author', mapping: 'username'},
11083     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11084     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11085     {name: 'lastPoster', mapping: 'user2'},
11086     {name: 'excerpt', mapping: 'post_text'}
11087 );
11088
11089 var myNewRecord = new TopicRecord({
11090     title: 'Do my job please',
11091     author: 'noobie',
11092     totalPosts: 1,
11093     lastPost: new Date(),
11094     lastPoster: 'Animal',
11095     excerpt: 'No way dude!'
11096 });
11097 myStore.add(myNewRecord);
11098 </code></pre>
11099  * @method create
11100  * @static
11101  */
11102 Roo.data.Record.create = function(o){
11103     var f = function(){
11104         f.superclass.constructor.apply(this, arguments);
11105     };
11106     Roo.extend(f, Roo.data.Record);
11107     var p = f.prototype;
11108     p.fields = new Roo.util.MixedCollection(false, function(field){
11109         return field.name;
11110     });
11111     for(var i = 0, len = o.length; i < len; i++){
11112         p.fields.add(new Roo.data.Field(o[i]));
11113     }
11114     f.getField = function(name){
11115         return p.fields.get(name);  
11116     };
11117     return f;
11118 };
11119
11120 Roo.data.Record.AUTO_ID = 1000;
11121 Roo.data.Record.EDIT = 'edit';
11122 Roo.data.Record.REJECT = 'reject';
11123 Roo.data.Record.COMMIT = 'commit';
11124
11125 Roo.data.Record.prototype = {
11126     /**
11127      * Readonly flag - true if this record has been modified.
11128      * @type Boolean
11129      */
11130     dirty : false,
11131     editing : false,
11132     error: null,
11133     modified: null,
11134
11135     // private
11136     join : function(store){
11137         this.store = store;
11138     },
11139
11140     /**
11141      * Set the named field to the specified value.
11142      * @param {String} name The name of the field to set.
11143      * @param {Object} value The value to set the field to.
11144      */
11145     set : function(name, value){
11146         if(this.data[name] == value){
11147             return;
11148         }
11149         this.dirty = true;
11150         if(!this.modified){
11151             this.modified = {};
11152         }
11153         if(typeof this.modified[name] == 'undefined'){
11154             this.modified[name] = this.data[name];
11155         }
11156         this.data[name] = value;
11157         if(!this.editing && this.store){
11158             this.store.afterEdit(this);
11159         }       
11160     },
11161
11162     /**
11163      * Get the value of the named field.
11164      * @param {String} name The name of the field to get the value of.
11165      * @return {Object} The value of the field.
11166      */
11167     get : function(name){
11168         return this.data[name]; 
11169     },
11170
11171     // private
11172     beginEdit : function(){
11173         this.editing = true;
11174         this.modified = {}; 
11175     },
11176
11177     // private
11178     cancelEdit : function(){
11179         this.editing = false;
11180         delete this.modified;
11181     },
11182
11183     // private
11184     endEdit : function(){
11185         this.editing = false;
11186         if(this.dirty && this.store){
11187             this.store.afterEdit(this);
11188         }
11189     },
11190
11191     /**
11192      * Usually called by the {@link Roo.data.Store} which owns the Record.
11193      * Rejects all changes made to the Record since either creation, or the last commit operation.
11194      * Modified fields are reverted to their original values.
11195      * <p>
11196      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11197      * of reject operations.
11198      */
11199     reject : function(){
11200         var m = this.modified;
11201         for(var n in m){
11202             if(typeof m[n] != "function"){
11203                 this.data[n] = m[n];
11204             }
11205         }
11206         this.dirty = false;
11207         delete this.modified;
11208         this.editing = false;
11209         if(this.store){
11210             this.store.afterReject(this);
11211         }
11212     },
11213
11214     /**
11215      * Usually called by the {@link Roo.data.Store} which owns the Record.
11216      * Commits all changes made to the Record since either creation, or the last commit operation.
11217      * <p>
11218      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11219      * of commit operations.
11220      */
11221     commit : function(){
11222         this.dirty = false;
11223         delete this.modified;
11224         this.editing = false;
11225         if(this.store){
11226             this.store.afterCommit(this);
11227         }
11228     },
11229
11230     // private
11231     hasError : function(){
11232         return this.error != null;
11233     },
11234
11235     // private
11236     clearError : function(){
11237         this.error = null;
11238     },
11239
11240     /**
11241      * Creates a copy of this record.
11242      * @param {String} id (optional) A new record id if you don't want to use this record's id
11243      * @return {Record}
11244      */
11245     copy : function(newId) {
11246         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11247     }
11248 };/*
11249  * Based on:
11250  * Ext JS Library 1.1.1
11251  * Copyright(c) 2006-2007, Ext JS, LLC.
11252  *
11253  * Originally Released Under LGPL - original licence link has changed is not relivant.
11254  *
11255  * Fork - LGPL
11256  * <script type="text/javascript">
11257  */
11258
11259
11260
11261 /**
11262  * @class Roo.data.Store
11263  * @extends Roo.util.Observable
11264  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11265  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11266  * <p>
11267  * 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
11268  * has no knowledge of the format of the data returned by the Proxy.<br>
11269  * <p>
11270  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11271  * instances from the data object. These records are cached and made available through accessor functions.
11272  * @constructor
11273  * Creates a new Store.
11274  * @param {Object} config A config object containing the objects needed for the Store to access data,
11275  * and read the data into Records.
11276  */
11277 Roo.data.Store = function(config){
11278     this.data = new Roo.util.MixedCollection(false);
11279     this.data.getKey = function(o){
11280         return o.id;
11281     };
11282     this.baseParams = {};
11283     // private
11284     this.paramNames = {
11285         "start" : "start",
11286         "limit" : "limit",
11287         "sort" : "sort",
11288         "dir" : "dir",
11289         "multisort" : "_multisort"
11290     };
11291
11292     if(config && config.data){
11293         this.inlineData = config.data;
11294         delete config.data;
11295     }
11296
11297     Roo.apply(this, config);
11298     
11299     if(this.reader){ // reader passed
11300         this.reader = Roo.factory(this.reader, Roo.data);
11301         this.reader.xmodule = this.xmodule || false;
11302         if(!this.recordType){
11303             this.recordType = this.reader.recordType;
11304         }
11305         if(this.reader.onMetaChange){
11306             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11307         }
11308     }
11309
11310     if(this.recordType){
11311         this.fields = this.recordType.prototype.fields;
11312     }
11313     this.modified = [];
11314
11315     this.addEvents({
11316         /**
11317          * @event datachanged
11318          * Fires when the data cache has changed, and a widget which is using this Store
11319          * as a Record cache should refresh its view.
11320          * @param {Store} this
11321          */
11322         datachanged : true,
11323         /**
11324          * @event metachange
11325          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11326          * @param {Store} this
11327          * @param {Object} meta The JSON metadata
11328          */
11329         metachange : true,
11330         /**
11331          * @event add
11332          * Fires when Records have been added to the Store
11333          * @param {Store} this
11334          * @param {Roo.data.Record[]} records The array of Records added
11335          * @param {Number} index The index at which the record(s) were added
11336          */
11337         add : true,
11338         /**
11339          * @event remove
11340          * Fires when a Record has been removed from the Store
11341          * @param {Store} this
11342          * @param {Roo.data.Record} record The Record that was removed
11343          * @param {Number} index The index at which the record was removed
11344          */
11345         remove : true,
11346         /**
11347          * @event update
11348          * Fires when a Record has been updated
11349          * @param {Store} this
11350          * @param {Roo.data.Record} record The Record that was updated
11351          * @param {String} operation The update operation being performed.  Value may be one of:
11352          * <pre><code>
11353  Roo.data.Record.EDIT
11354  Roo.data.Record.REJECT
11355  Roo.data.Record.COMMIT
11356          * </code></pre>
11357          */
11358         update : true,
11359         /**
11360          * @event clear
11361          * Fires when the data cache has been cleared.
11362          * @param {Store} this
11363          */
11364         clear : true,
11365         /**
11366          * @event beforeload
11367          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11368          * the load action will be canceled.
11369          * @param {Store} this
11370          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11371          */
11372         beforeload : true,
11373         /**
11374          * @event beforeloadadd
11375          * Fires after a new set of Records has been loaded.
11376          * @param {Store} this
11377          * @param {Roo.data.Record[]} records The Records that were loaded
11378          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11379          */
11380         beforeloadadd : true,
11381         /**
11382          * @event load
11383          * Fires after a new set of Records has been loaded, before they are added to the store.
11384          * @param {Store} this
11385          * @param {Roo.data.Record[]} records The Records that were loaded
11386          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11387          * @params {Object} return from reader
11388          */
11389         load : true,
11390         /**
11391          * @event loadexception
11392          * Fires if an exception occurs in the Proxy during loading.
11393          * Called with the signature of the Proxy's "loadexception" event.
11394          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11395          * 
11396          * @param {Proxy} 
11397          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11398          * @param {Object} load options 
11399          * @param {Object} jsonData from your request (normally this contains the Exception)
11400          */
11401         loadexception : true
11402     });
11403     
11404     if(this.proxy){
11405         this.proxy = Roo.factory(this.proxy, Roo.data);
11406         this.proxy.xmodule = this.xmodule || false;
11407         this.relayEvents(this.proxy,  ["loadexception"]);
11408     }
11409     this.sortToggle = {};
11410     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11411
11412     Roo.data.Store.superclass.constructor.call(this);
11413
11414     if(this.inlineData){
11415         this.loadData(this.inlineData);
11416         delete this.inlineData;
11417     }
11418 };
11419
11420 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11421      /**
11422     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11423     * without a remote query - used by combo/forms at present.
11424     */
11425     
11426     /**
11427     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11428     */
11429     /**
11430     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11431     */
11432     /**
11433     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11434     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11435     */
11436     /**
11437     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11438     * on any HTTP request
11439     */
11440     /**
11441     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11442     */
11443     /**
11444     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11445     */
11446     multiSort: false,
11447     /**
11448     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11449     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11450     */
11451     remoteSort : false,
11452
11453     /**
11454     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11455      * loaded or when a record is removed. (defaults to false).
11456     */
11457     pruneModifiedRecords : false,
11458
11459     // private
11460     lastOptions : null,
11461
11462     /**
11463      * Add Records to the Store and fires the add event.
11464      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11465      */
11466     add : function(records){
11467         records = [].concat(records);
11468         for(var i = 0, len = records.length; i < len; i++){
11469             records[i].join(this);
11470         }
11471         var index = this.data.length;
11472         this.data.addAll(records);
11473         this.fireEvent("add", this, records, index);
11474     },
11475
11476     /**
11477      * Remove a Record from the Store and fires the remove event.
11478      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11479      */
11480     remove : function(record){
11481         var index = this.data.indexOf(record);
11482         this.data.removeAt(index);
11483  
11484         if(this.pruneModifiedRecords){
11485             this.modified.remove(record);
11486         }
11487         this.fireEvent("remove", this, record, index);
11488     },
11489
11490     /**
11491      * Remove all Records from the Store and fires the clear event.
11492      */
11493     removeAll : function(){
11494         this.data.clear();
11495         if(this.pruneModifiedRecords){
11496             this.modified = [];
11497         }
11498         this.fireEvent("clear", this);
11499     },
11500
11501     /**
11502      * Inserts Records to the Store at the given index and fires the add event.
11503      * @param {Number} index The start index at which to insert the passed Records.
11504      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11505      */
11506     insert : function(index, records){
11507         records = [].concat(records);
11508         for(var i = 0, len = records.length; i < len; i++){
11509             this.data.insert(index, records[i]);
11510             records[i].join(this);
11511         }
11512         this.fireEvent("add", this, records, index);
11513     },
11514
11515     /**
11516      * Get the index within the cache of the passed Record.
11517      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11518      * @return {Number} The index of the passed Record. Returns -1 if not found.
11519      */
11520     indexOf : function(record){
11521         return this.data.indexOf(record);
11522     },
11523
11524     /**
11525      * Get the index within the cache of the Record with the passed id.
11526      * @param {String} id The id of the Record to find.
11527      * @return {Number} The index of the Record. Returns -1 if not found.
11528      */
11529     indexOfId : function(id){
11530         return this.data.indexOfKey(id);
11531     },
11532
11533     /**
11534      * Get the Record with the specified id.
11535      * @param {String} id The id of the Record to find.
11536      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11537      */
11538     getById : function(id){
11539         return this.data.key(id);
11540     },
11541
11542     /**
11543      * Get the Record at the specified index.
11544      * @param {Number} index The index of the Record to find.
11545      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11546      */
11547     getAt : function(index){
11548         return this.data.itemAt(index);
11549     },
11550
11551     /**
11552      * Returns a range of Records between specified indices.
11553      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11554      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11555      * @return {Roo.data.Record[]} An array of Records
11556      */
11557     getRange : function(start, end){
11558         return this.data.getRange(start, end);
11559     },
11560
11561     // private
11562     storeOptions : function(o){
11563         o = Roo.apply({}, o);
11564         delete o.callback;
11565         delete o.scope;
11566         this.lastOptions = o;
11567     },
11568
11569     /**
11570      * Loads the Record cache from the configured Proxy using the configured Reader.
11571      * <p>
11572      * If using remote paging, then the first load call must specify the <em>start</em>
11573      * and <em>limit</em> properties in the options.params property to establish the initial
11574      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11575      * <p>
11576      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11577      * and this call will return before the new data has been loaded. Perform any post-processing
11578      * in a callback function, or in a "load" event handler.</strong>
11579      * <p>
11580      * @param {Object} options An object containing properties which control loading options:<ul>
11581      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11582      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11583      * passed the following arguments:<ul>
11584      * <li>r : Roo.data.Record[]</li>
11585      * <li>options: Options object from the load call</li>
11586      * <li>success: Boolean success indicator</li></ul></li>
11587      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11588      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11589      * </ul>
11590      */
11591     load : function(options){
11592         options = options || {};
11593         if(this.fireEvent("beforeload", this, options) !== false){
11594             this.storeOptions(options);
11595             var p = Roo.apply(options.params || {}, this.baseParams);
11596             // if meta was not loaded from remote source.. try requesting it.
11597             if (!this.reader.metaFromRemote) {
11598                 p._requestMeta = 1;
11599             }
11600             if(this.sortInfo && this.remoteSort){
11601                 var pn = this.paramNames;
11602                 p[pn["sort"]] = this.sortInfo.field;
11603                 p[pn["dir"]] = this.sortInfo.direction;
11604             }
11605             if (this.multiSort) {
11606                 var pn = this.paramNames;
11607                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11608             }
11609             
11610             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11611         }
11612     },
11613
11614     /**
11615      * Reloads the Record cache from the configured Proxy using the configured Reader and
11616      * the options from the last load operation performed.
11617      * @param {Object} options (optional) An object containing properties which may override the options
11618      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11619      * the most recently used options are reused).
11620      */
11621     reload : function(options){
11622         this.load(Roo.applyIf(options||{}, this.lastOptions));
11623     },
11624
11625     // private
11626     // Called as a callback by the Reader during a load operation.
11627     loadRecords : function(o, options, success){
11628         if(!o || success === false){
11629             if(success !== false){
11630                 this.fireEvent("load", this, [], options, o);
11631             }
11632             if(options.callback){
11633                 options.callback.call(options.scope || this, [], options, false);
11634             }
11635             return;
11636         }
11637         // if data returned failure - throw an exception.
11638         if (o.success === false) {
11639             // show a message if no listener is registered.
11640             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11641                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11642             }
11643             // loadmask wil be hooked into this..
11644             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11645             return;
11646         }
11647         var r = o.records, t = o.totalRecords || r.length;
11648         
11649         this.fireEvent("beforeloadadd", this, r, options, o);
11650         
11651         if(!options || options.add !== true){
11652             if(this.pruneModifiedRecords){
11653                 this.modified = [];
11654             }
11655             for(var i = 0, len = r.length; i < len; i++){
11656                 r[i].join(this);
11657             }
11658             if(this.snapshot){
11659                 this.data = this.snapshot;
11660                 delete this.snapshot;
11661             }
11662             this.data.clear();
11663             this.data.addAll(r);
11664             this.totalLength = t;
11665             this.applySort();
11666             this.fireEvent("datachanged", this);
11667         }else{
11668             this.totalLength = Math.max(t, this.data.length+r.length);
11669             this.add(r);
11670         }
11671         
11672         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11673                 
11674             var e = new Roo.data.Record({});
11675
11676             e.set(this.parent.displayField, this.parent.emptyTitle);
11677             e.set(this.parent.valueField, '');
11678
11679             this.insert(0, e);
11680         }
11681             
11682         this.fireEvent("load", this, r, options, o);
11683         if(options.callback){
11684             options.callback.call(options.scope || this, r, options, true);
11685         }
11686     },
11687
11688
11689     /**
11690      * Loads data from a passed data block. A Reader which understands the format of the data
11691      * must have been configured in the constructor.
11692      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11693      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11694      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11695      */
11696     loadData : function(o, append){
11697         var r = this.reader.readRecords(o);
11698         this.loadRecords(r, {add: append}, true);
11699     },
11700
11701     /**
11702      * Gets the number of cached records.
11703      * <p>
11704      * <em>If using paging, this may not be the total size of the dataset. If the data object
11705      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11706      * the data set size</em>
11707      */
11708     getCount : function(){
11709         return this.data.length || 0;
11710     },
11711
11712     /**
11713      * Gets the total number of records in the dataset as returned by the server.
11714      * <p>
11715      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11716      * the dataset size</em>
11717      */
11718     getTotalCount : function(){
11719         return this.totalLength || 0;
11720     },
11721
11722     /**
11723      * Returns the sort state of the Store as an object with two properties:
11724      * <pre><code>
11725  field {String} The name of the field by which the Records are sorted
11726  direction {String} The sort order, "ASC" or "DESC"
11727      * </code></pre>
11728      */
11729     getSortState : function(){
11730         return this.sortInfo;
11731     },
11732
11733     // private
11734     applySort : function(){
11735         if(this.sortInfo && !this.remoteSort){
11736             var s = this.sortInfo, f = s.field;
11737             var st = this.fields.get(f).sortType;
11738             var fn = function(r1, r2){
11739                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11740                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11741             };
11742             this.data.sort(s.direction, fn);
11743             if(this.snapshot && this.snapshot != this.data){
11744                 this.snapshot.sort(s.direction, fn);
11745             }
11746         }
11747     },
11748
11749     /**
11750      * Sets the default sort column and order to be used by the next load operation.
11751      * @param {String} fieldName The name of the field to sort by.
11752      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11753      */
11754     setDefaultSort : function(field, dir){
11755         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11756     },
11757
11758     /**
11759      * Sort the Records.
11760      * If remote sorting is used, the sort is performed on the server, and the cache is
11761      * reloaded. If local sorting is used, the cache is sorted internally.
11762      * @param {String} fieldName The name of the field to sort by.
11763      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11764      */
11765     sort : function(fieldName, dir){
11766         var f = this.fields.get(fieldName);
11767         if(!dir){
11768             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11769             
11770             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11771                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11772             }else{
11773                 dir = f.sortDir;
11774             }
11775         }
11776         this.sortToggle[f.name] = dir;
11777         this.sortInfo = {field: f.name, direction: dir};
11778         if(!this.remoteSort){
11779             this.applySort();
11780             this.fireEvent("datachanged", this);
11781         }else{
11782             this.load(this.lastOptions);
11783         }
11784     },
11785
11786     /**
11787      * Calls the specified function for each of the Records in the cache.
11788      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11789      * Returning <em>false</em> aborts and exits the iteration.
11790      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11791      */
11792     each : function(fn, scope){
11793         this.data.each(fn, scope);
11794     },
11795
11796     /**
11797      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11798      * (e.g., during paging).
11799      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11800      */
11801     getModifiedRecords : function(){
11802         return this.modified;
11803     },
11804
11805     // private
11806     createFilterFn : function(property, value, anyMatch){
11807         if(!value.exec){ // not a regex
11808             value = String(value);
11809             if(value.length == 0){
11810                 return false;
11811             }
11812             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11813         }
11814         return function(r){
11815             return value.test(r.data[property]);
11816         };
11817     },
11818
11819     /**
11820      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11821      * @param {String} property A field on your records
11822      * @param {Number} start The record index to start at (defaults to 0)
11823      * @param {Number} end The last record index to include (defaults to length - 1)
11824      * @return {Number} The sum
11825      */
11826     sum : function(property, start, end){
11827         var rs = this.data.items, v = 0;
11828         start = start || 0;
11829         end = (end || end === 0) ? end : rs.length-1;
11830
11831         for(var i = start; i <= end; i++){
11832             v += (rs[i].data[property] || 0);
11833         }
11834         return v;
11835     },
11836
11837     /**
11838      * Filter the records by a specified property.
11839      * @param {String} field A field on your records
11840      * @param {String/RegExp} value Either a string that the field
11841      * should start with or a RegExp to test against the field
11842      * @param {Boolean} anyMatch True to match any part not just the beginning
11843      */
11844     filter : function(property, value, anyMatch){
11845         var fn = this.createFilterFn(property, value, anyMatch);
11846         return fn ? this.filterBy(fn) : this.clearFilter();
11847     },
11848
11849     /**
11850      * Filter by a function. The specified function will be called with each
11851      * record in this data source. If the function returns true the record is included,
11852      * otherwise it is filtered.
11853      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11854      * @param {Object} scope (optional) The scope of the function (defaults to this)
11855      */
11856     filterBy : function(fn, scope){
11857         this.snapshot = this.snapshot || this.data;
11858         this.data = this.queryBy(fn, scope||this);
11859         this.fireEvent("datachanged", this);
11860     },
11861
11862     /**
11863      * Query the records by a specified property.
11864      * @param {String} field A field on your records
11865      * @param {String/RegExp} value Either a string that the field
11866      * should start with or a RegExp to test against the field
11867      * @param {Boolean} anyMatch True to match any part not just the beginning
11868      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11869      */
11870     query : function(property, value, anyMatch){
11871         var fn = this.createFilterFn(property, value, anyMatch);
11872         return fn ? this.queryBy(fn) : this.data.clone();
11873     },
11874
11875     /**
11876      * Query by a function. The specified function will be called with each
11877      * record in this data source. If the function returns true the record is included
11878      * in the results.
11879      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11880      * @param {Object} scope (optional) The scope of the function (defaults to this)
11881       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11882      **/
11883     queryBy : function(fn, scope){
11884         var data = this.snapshot || this.data;
11885         return data.filterBy(fn, scope||this);
11886     },
11887
11888     /**
11889      * Collects unique values for a particular dataIndex from this store.
11890      * @param {String} dataIndex The property to collect
11891      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11892      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11893      * @return {Array} An array of the unique values
11894      **/
11895     collect : function(dataIndex, allowNull, bypassFilter){
11896         var d = (bypassFilter === true && this.snapshot) ?
11897                 this.snapshot.items : this.data.items;
11898         var v, sv, r = [], l = {};
11899         for(var i = 0, len = d.length; i < len; i++){
11900             v = d[i].data[dataIndex];
11901             sv = String(v);
11902             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11903                 l[sv] = true;
11904                 r[r.length] = v;
11905             }
11906         }
11907         return r;
11908     },
11909
11910     /**
11911      * Revert to a view of the Record cache with no filtering applied.
11912      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11913      */
11914     clearFilter : function(suppressEvent){
11915         if(this.snapshot && this.snapshot != this.data){
11916             this.data = this.snapshot;
11917             delete this.snapshot;
11918             if(suppressEvent !== true){
11919                 this.fireEvent("datachanged", this);
11920             }
11921         }
11922     },
11923
11924     // private
11925     afterEdit : function(record){
11926         if(this.modified.indexOf(record) == -1){
11927             this.modified.push(record);
11928         }
11929         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11930     },
11931     
11932     // private
11933     afterReject : function(record){
11934         this.modified.remove(record);
11935         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11936     },
11937
11938     // private
11939     afterCommit : function(record){
11940         this.modified.remove(record);
11941         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11942     },
11943
11944     /**
11945      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11946      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11947      */
11948     commitChanges : function(){
11949         var m = this.modified.slice(0);
11950         this.modified = [];
11951         for(var i = 0, len = m.length; i < len; i++){
11952             m[i].commit();
11953         }
11954     },
11955
11956     /**
11957      * Cancel outstanding changes on all changed records.
11958      */
11959     rejectChanges : function(){
11960         var m = this.modified.slice(0);
11961         this.modified = [];
11962         for(var i = 0, len = m.length; i < len; i++){
11963             m[i].reject();
11964         }
11965     },
11966
11967     onMetaChange : function(meta, rtype, o){
11968         this.recordType = rtype;
11969         this.fields = rtype.prototype.fields;
11970         delete this.snapshot;
11971         this.sortInfo = meta.sortInfo || this.sortInfo;
11972         this.modified = [];
11973         this.fireEvent('metachange', this, this.reader.meta);
11974     },
11975     
11976     moveIndex : function(data, type)
11977     {
11978         var index = this.indexOf(data);
11979         
11980         var newIndex = index + type;
11981         
11982         this.remove(data);
11983         
11984         this.insert(newIndex, data);
11985         
11986     }
11987 });/*
11988  * Based on:
11989  * Ext JS Library 1.1.1
11990  * Copyright(c) 2006-2007, Ext JS, LLC.
11991  *
11992  * Originally Released Under LGPL - original licence link has changed is not relivant.
11993  *
11994  * Fork - LGPL
11995  * <script type="text/javascript">
11996  */
11997
11998 /**
11999  * @class Roo.data.SimpleStore
12000  * @extends Roo.data.Store
12001  * Small helper class to make creating Stores from Array data easier.
12002  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12003  * @cfg {Array} fields An array of field definition objects, or field name strings.
12004  * @cfg {Array} data The multi-dimensional array of data
12005  * @constructor
12006  * @param {Object} config
12007  */
12008 Roo.data.SimpleStore = function(config){
12009     Roo.data.SimpleStore.superclass.constructor.call(this, {
12010         isLocal : true,
12011         reader: new Roo.data.ArrayReader({
12012                 id: config.id
12013             },
12014             Roo.data.Record.create(config.fields)
12015         ),
12016         proxy : new Roo.data.MemoryProxy(config.data)
12017     });
12018     this.load();
12019 };
12020 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12021  * Based on:
12022  * Ext JS Library 1.1.1
12023  * Copyright(c) 2006-2007, Ext JS, LLC.
12024  *
12025  * Originally Released Under LGPL - original licence link has changed is not relivant.
12026  *
12027  * Fork - LGPL
12028  * <script type="text/javascript">
12029  */
12030
12031 /**
12032 /**
12033  * @extends Roo.data.Store
12034  * @class Roo.data.JsonStore
12035  * Small helper class to make creating Stores for JSON data easier. <br/>
12036 <pre><code>
12037 var store = new Roo.data.JsonStore({
12038     url: 'get-images.php',
12039     root: 'images',
12040     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12041 });
12042 </code></pre>
12043  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12044  * JsonReader and HttpProxy (unless inline data is provided).</b>
12045  * @cfg {Array} fields An array of field definition objects, or field name strings.
12046  * @constructor
12047  * @param {Object} config
12048  */
12049 Roo.data.JsonStore = function(c){
12050     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12051         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12052         reader: new Roo.data.JsonReader(c, c.fields)
12053     }));
12054 };
12055 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12056  * Based on:
12057  * Ext JS Library 1.1.1
12058  * Copyright(c) 2006-2007, Ext JS, LLC.
12059  *
12060  * Originally Released Under LGPL - original licence link has changed is not relivant.
12061  *
12062  * Fork - LGPL
12063  * <script type="text/javascript">
12064  */
12065
12066  
12067 Roo.data.Field = function(config){
12068     if(typeof config == "string"){
12069         config = {name: config};
12070     }
12071     Roo.apply(this, config);
12072     
12073     if(!this.type){
12074         this.type = "auto";
12075     }
12076     
12077     var st = Roo.data.SortTypes;
12078     // named sortTypes are supported, here we look them up
12079     if(typeof this.sortType == "string"){
12080         this.sortType = st[this.sortType];
12081     }
12082     
12083     // set default sortType for strings and dates
12084     if(!this.sortType){
12085         switch(this.type){
12086             case "string":
12087                 this.sortType = st.asUCString;
12088                 break;
12089             case "date":
12090                 this.sortType = st.asDate;
12091                 break;
12092             default:
12093                 this.sortType = st.none;
12094         }
12095     }
12096
12097     // define once
12098     var stripRe = /[\$,%]/g;
12099
12100     // prebuilt conversion function for this field, instead of
12101     // switching every time we're reading a value
12102     if(!this.convert){
12103         var cv, dateFormat = this.dateFormat;
12104         switch(this.type){
12105             case "":
12106             case "auto":
12107             case undefined:
12108                 cv = function(v){ return v; };
12109                 break;
12110             case "string":
12111                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12112                 break;
12113             case "int":
12114                 cv = function(v){
12115                     return v !== undefined && v !== null && v !== '' ?
12116                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12117                     };
12118                 break;
12119             case "float":
12120                 cv = function(v){
12121                     return v !== undefined && v !== null && v !== '' ?
12122                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12123                     };
12124                 break;
12125             case "bool":
12126             case "boolean":
12127                 cv = function(v){ return v === true || v === "true" || v == 1; };
12128                 break;
12129             case "date":
12130                 cv = function(v){
12131                     if(!v){
12132                         return '';
12133                     }
12134                     if(v instanceof Date){
12135                         return v;
12136                     }
12137                     if(dateFormat){
12138                         if(dateFormat == "timestamp"){
12139                             return new Date(v*1000);
12140                         }
12141                         return Date.parseDate(v, dateFormat);
12142                     }
12143                     var parsed = Date.parse(v);
12144                     return parsed ? new Date(parsed) : null;
12145                 };
12146              break;
12147             
12148         }
12149         this.convert = cv;
12150     }
12151 };
12152
12153 Roo.data.Field.prototype = {
12154     dateFormat: null,
12155     defaultValue: "",
12156     mapping: null,
12157     sortType : null,
12158     sortDir : "ASC"
12159 };/*
12160  * Based on:
12161  * Ext JS Library 1.1.1
12162  * Copyright(c) 2006-2007, Ext JS, LLC.
12163  *
12164  * Originally Released Under LGPL - original licence link has changed is not relivant.
12165  *
12166  * Fork - LGPL
12167  * <script type="text/javascript">
12168  */
12169  
12170 // Base class for reading structured data from a data source.  This class is intended to be
12171 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12172
12173 /**
12174  * @class Roo.data.DataReader
12175  * Base class for reading structured data from a data source.  This class is intended to be
12176  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12177  */
12178
12179 Roo.data.DataReader = function(meta, recordType){
12180     
12181     this.meta = meta;
12182     
12183     this.recordType = recordType instanceof Array ? 
12184         Roo.data.Record.create(recordType) : recordType;
12185 };
12186
12187 Roo.data.DataReader.prototype = {
12188      /**
12189      * Create an empty record
12190      * @param {Object} data (optional) - overlay some values
12191      * @return {Roo.data.Record} record created.
12192      */
12193     newRow :  function(d) {
12194         var da =  {};
12195         this.recordType.prototype.fields.each(function(c) {
12196             switch( c.type) {
12197                 case 'int' : da[c.name] = 0; break;
12198                 case 'date' : da[c.name] = new Date(); break;
12199                 case 'float' : da[c.name] = 0.0; break;
12200                 case 'boolean' : da[c.name] = false; break;
12201                 default : da[c.name] = ""; break;
12202             }
12203             
12204         });
12205         return new this.recordType(Roo.apply(da, d));
12206     }
12207     
12208 };/*
12209  * Based on:
12210  * Ext JS Library 1.1.1
12211  * Copyright(c) 2006-2007, Ext JS, LLC.
12212  *
12213  * Originally Released Under LGPL - original licence link has changed is not relivant.
12214  *
12215  * Fork - LGPL
12216  * <script type="text/javascript">
12217  */
12218
12219 /**
12220  * @class Roo.data.DataProxy
12221  * @extends Roo.data.Observable
12222  * This class is an abstract base class for implementations which provide retrieval of
12223  * unformatted data objects.<br>
12224  * <p>
12225  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12226  * (of the appropriate type which knows how to parse the data object) to provide a block of
12227  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12228  * <p>
12229  * Custom implementations must implement the load method as described in
12230  * {@link Roo.data.HttpProxy#load}.
12231  */
12232 Roo.data.DataProxy = function(){
12233     this.addEvents({
12234         /**
12235          * @event beforeload
12236          * Fires before a network request is made to retrieve a data object.
12237          * @param {Object} This DataProxy object.
12238          * @param {Object} params The params parameter to the load function.
12239          */
12240         beforeload : true,
12241         /**
12242          * @event load
12243          * Fires before the load method's callback is called.
12244          * @param {Object} This DataProxy object.
12245          * @param {Object} o The data object.
12246          * @param {Object} arg The callback argument object passed to the load function.
12247          */
12248         load : true,
12249         /**
12250          * @event loadexception
12251          * Fires if an Exception occurs during data retrieval.
12252          * @param {Object} This DataProxy object.
12253          * @param {Object} o The data object.
12254          * @param {Object} arg The callback argument object passed to the load function.
12255          * @param {Object} e The Exception.
12256          */
12257         loadexception : true
12258     });
12259     Roo.data.DataProxy.superclass.constructor.call(this);
12260 };
12261
12262 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12263
12264     /**
12265      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12266      */
12267 /*
12268  * Based on:
12269  * Ext JS Library 1.1.1
12270  * Copyright(c) 2006-2007, Ext JS, LLC.
12271  *
12272  * Originally Released Under LGPL - original licence link has changed is not relivant.
12273  *
12274  * Fork - LGPL
12275  * <script type="text/javascript">
12276  */
12277 /**
12278  * @class Roo.data.MemoryProxy
12279  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12280  * to the Reader when its load method is called.
12281  * @constructor
12282  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12283  */
12284 Roo.data.MemoryProxy = function(data){
12285     if (data.data) {
12286         data = data.data;
12287     }
12288     Roo.data.MemoryProxy.superclass.constructor.call(this);
12289     this.data = data;
12290 };
12291
12292 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12293     
12294     /**
12295      * Load data from the requested source (in this case an in-memory
12296      * data object passed to the constructor), read the data object into
12297      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12298      * process that block using the passed callback.
12299      * @param {Object} params This parameter is not used by the MemoryProxy class.
12300      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12301      * object into a block of Roo.data.Records.
12302      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12303      * The function must be passed <ul>
12304      * <li>The Record block object</li>
12305      * <li>The "arg" argument from the load function</li>
12306      * <li>A boolean success indicator</li>
12307      * </ul>
12308      * @param {Object} scope The scope in which to call the callback
12309      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12310      */
12311     load : function(params, reader, callback, scope, arg){
12312         params = params || {};
12313         var result;
12314         try {
12315             result = reader.readRecords(this.data);
12316         }catch(e){
12317             this.fireEvent("loadexception", this, arg, null, e);
12318             callback.call(scope, null, arg, false);
12319             return;
12320         }
12321         callback.call(scope, result, arg, true);
12322     },
12323     
12324     // private
12325     update : function(params, records){
12326         
12327     }
12328 });/*
12329  * Based on:
12330  * Ext JS Library 1.1.1
12331  * Copyright(c) 2006-2007, Ext JS, LLC.
12332  *
12333  * Originally Released Under LGPL - original licence link has changed is not relivant.
12334  *
12335  * Fork - LGPL
12336  * <script type="text/javascript">
12337  */
12338 /**
12339  * @class Roo.data.HttpProxy
12340  * @extends Roo.data.DataProxy
12341  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12342  * configured to reference a certain URL.<br><br>
12343  * <p>
12344  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12345  * from which the running page was served.<br><br>
12346  * <p>
12347  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12348  * <p>
12349  * Be aware that to enable the browser to parse an XML document, the server must set
12350  * the Content-Type header in the HTTP response to "text/xml".
12351  * @constructor
12352  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12353  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12354  * will be used to make the request.
12355  */
12356 Roo.data.HttpProxy = function(conn){
12357     Roo.data.HttpProxy.superclass.constructor.call(this);
12358     // is conn a conn config or a real conn?
12359     this.conn = conn;
12360     this.useAjax = !conn || !conn.events;
12361   
12362 };
12363
12364 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12365     // thse are take from connection...
12366     
12367     /**
12368      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12369      */
12370     /**
12371      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12372      * extra parameters to each request made by this object. (defaults to undefined)
12373      */
12374     /**
12375      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12376      *  to each request made by this object. (defaults to undefined)
12377      */
12378     /**
12379      * @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)
12380      */
12381     /**
12382      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12383      */
12384      /**
12385      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12386      * @type Boolean
12387      */
12388   
12389
12390     /**
12391      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12392      * @type Boolean
12393      */
12394     /**
12395      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12396      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12397      * a finer-grained basis than the DataProxy events.
12398      */
12399     getConnection : function(){
12400         return this.useAjax ? Roo.Ajax : this.conn;
12401     },
12402
12403     /**
12404      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12405      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12406      * process that block using the passed callback.
12407      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12408      * for the request to the remote server.
12409      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12410      * object into a block of Roo.data.Records.
12411      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12412      * The function must be passed <ul>
12413      * <li>The Record block object</li>
12414      * <li>The "arg" argument from the load function</li>
12415      * <li>A boolean success indicator</li>
12416      * </ul>
12417      * @param {Object} scope The scope in which to call the callback
12418      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12419      */
12420     load : function(params, reader, callback, scope, arg){
12421         if(this.fireEvent("beforeload", this, params) !== false){
12422             var  o = {
12423                 params : params || {},
12424                 request: {
12425                     callback : callback,
12426                     scope : scope,
12427                     arg : arg
12428                 },
12429                 reader: reader,
12430                 callback : this.loadResponse,
12431                 scope: this
12432             };
12433             if(this.useAjax){
12434                 Roo.applyIf(o, this.conn);
12435                 if(this.activeRequest){
12436                     Roo.Ajax.abort(this.activeRequest);
12437                 }
12438                 this.activeRequest = Roo.Ajax.request(o);
12439             }else{
12440                 this.conn.request(o);
12441             }
12442         }else{
12443             callback.call(scope||this, null, arg, false);
12444         }
12445     },
12446
12447     // private
12448     loadResponse : function(o, success, response){
12449         delete this.activeRequest;
12450         if(!success){
12451             this.fireEvent("loadexception", this, o, response);
12452             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12453             return;
12454         }
12455         var result;
12456         try {
12457             result = o.reader.read(response);
12458         }catch(e){
12459             this.fireEvent("loadexception", this, o, response, e);
12460             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12461             return;
12462         }
12463         
12464         this.fireEvent("load", this, o, o.request.arg);
12465         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12466     },
12467
12468     // private
12469     update : function(dataSet){
12470
12471     },
12472
12473     // private
12474     updateResponse : function(dataSet){
12475
12476     }
12477 });/*
12478  * Based on:
12479  * Ext JS Library 1.1.1
12480  * Copyright(c) 2006-2007, Ext JS, LLC.
12481  *
12482  * Originally Released Under LGPL - original licence link has changed is not relivant.
12483  *
12484  * Fork - LGPL
12485  * <script type="text/javascript">
12486  */
12487
12488 /**
12489  * @class Roo.data.ScriptTagProxy
12490  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12491  * other than the originating domain of the running page.<br><br>
12492  * <p>
12493  * <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
12494  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12495  * <p>
12496  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12497  * source code that is used as the source inside a &lt;script> tag.<br><br>
12498  * <p>
12499  * In order for the browser to process the returned data, the server must wrap the data object
12500  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12501  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12502  * depending on whether the callback name was passed:
12503  * <p>
12504  * <pre><code>
12505 boolean scriptTag = false;
12506 String cb = request.getParameter("callback");
12507 if (cb != null) {
12508     scriptTag = true;
12509     response.setContentType("text/javascript");
12510 } else {
12511     response.setContentType("application/x-json");
12512 }
12513 Writer out = response.getWriter();
12514 if (scriptTag) {
12515     out.write(cb + "(");
12516 }
12517 out.print(dataBlock.toJsonString());
12518 if (scriptTag) {
12519     out.write(");");
12520 }
12521 </pre></code>
12522  *
12523  * @constructor
12524  * @param {Object} config A configuration object.
12525  */
12526 Roo.data.ScriptTagProxy = function(config){
12527     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12528     Roo.apply(this, config);
12529     this.head = document.getElementsByTagName("head")[0];
12530 };
12531
12532 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12533
12534 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12535     /**
12536      * @cfg {String} url The URL from which to request the data object.
12537      */
12538     /**
12539      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12540      */
12541     timeout : 30000,
12542     /**
12543      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12544      * the server the name of the callback function set up by the load call to process the returned data object.
12545      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12546      * javascript output which calls this named function passing the data object as its only parameter.
12547      */
12548     callbackParam : "callback",
12549     /**
12550      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12551      * name to the request.
12552      */
12553     nocache : true,
12554
12555     /**
12556      * Load data from the configured URL, read the data object into
12557      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12558      * process that block using the passed callback.
12559      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12560      * for the request to the remote server.
12561      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12562      * object into a block of Roo.data.Records.
12563      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12564      * The function must be passed <ul>
12565      * <li>The Record block object</li>
12566      * <li>The "arg" argument from the load function</li>
12567      * <li>A boolean success indicator</li>
12568      * </ul>
12569      * @param {Object} scope The scope in which to call the callback
12570      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12571      */
12572     load : function(params, reader, callback, scope, arg){
12573         if(this.fireEvent("beforeload", this, params) !== false){
12574
12575             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12576
12577             var url = this.url;
12578             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12579             if(this.nocache){
12580                 url += "&_dc=" + (new Date().getTime());
12581             }
12582             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12583             var trans = {
12584                 id : transId,
12585                 cb : "stcCallback"+transId,
12586                 scriptId : "stcScript"+transId,
12587                 params : params,
12588                 arg : arg,
12589                 url : url,
12590                 callback : callback,
12591                 scope : scope,
12592                 reader : reader
12593             };
12594             var conn = this;
12595
12596             window[trans.cb] = function(o){
12597                 conn.handleResponse(o, trans);
12598             };
12599
12600             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12601
12602             if(this.autoAbort !== false){
12603                 this.abort();
12604             }
12605
12606             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12607
12608             var script = document.createElement("script");
12609             script.setAttribute("src", url);
12610             script.setAttribute("type", "text/javascript");
12611             script.setAttribute("id", trans.scriptId);
12612             this.head.appendChild(script);
12613
12614             this.trans = trans;
12615         }else{
12616             callback.call(scope||this, null, arg, false);
12617         }
12618     },
12619
12620     // private
12621     isLoading : function(){
12622         return this.trans ? true : false;
12623     },
12624
12625     /**
12626      * Abort the current server request.
12627      */
12628     abort : function(){
12629         if(this.isLoading()){
12630             this.destroyTrans(this.trans);
12631         }
12632     },
12633
12634     // private
12635     destroyTrans : function(trans, isLoaded){
12636         this.head.removeChild(document.getElementById(trans.scriptId));
12637         clearTimeout(trans.timeoutId);
12638         if(isLoaded){
12639             window[trans.cb] = undefined;
12640             try{
12641                 delete window[trans.cb];
12642             }catch(e){}
12643         }else{
12644             // if hasn't been loaded, wait for load to remove it to prevent script error
12645             window[trans.cb] = function(){
12646                 window[trans.cb] = undefined;
12647                 try{
12648                     delete window[trans.cb];
12649                 }catch(e){}
12650             };
12651         }
12652     },
12653
12654     // private
12655     handleResponse : function(o, trans){
12656         this.trans = false;
12657         this.destroyTrans(trans, true);
12658         var result;
12659         try {
12660             result = trans.reader.readRecords(o);
12661         }catch(e){
12662             this.fireEvent("loadexception", this, o, trans.arg, e);
12663             trans.callback.call(trans.scope||window, null, trans.arg, false);
12664             return;
12665         }
12666         this.fireEvent("load", this, o, trans.arg);
12667         trans.callback.call(trans.scope||window, result, trans.arg, true);
12668     },
12669
12670     // private
12671     handleFailure : function(trans){
12672         this.trans = false;
12673         this.destroyTrans(trans, false);
12674         this.fireEvent("loadexception", this, null, trans.arg);
12675         trans.callback.call(trans.scope||window, null, trans.arg, false);
12676     }
12677 });/*
12678  * Based on:
12679  * Ext JS Library 1.1.1
12680  * Copyright(c) 2006-2007, Ext JS, LLC.
12681  *
12682  * Originally Released Under LGPL - original licence link has changed is not relivant.
12683  *
12684  * Fork - LGPL
12685  * <script type="text/javascript">
12686  */
12687
12688 /**
12689  * @class Roo.data.JsonReader
12690  * @extends Roo.data.DataReader
12691  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12692  * based on mappings in a provided Roo.data.Record constructor.
12693  * 
12694  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12695  * in the reply previously. 
12696  * 
12697  * <p>
12698  * Example code:
12699  * <pre><code>
12700 var RecordDef = Roo.data.Record.create([
12701     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12702     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12703 ]);
12704 var myReader = new Roo.data.JsonReader({
12705     totalProperty: "results",    // The property which contains the total dataset size (optional)
12706     root: "rows",                // The property which contains an Array of row objects
12707     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12708 }, RecordDef);
12709 </code></pre>
12710  * <p>
12711  * This would consume a JSON file like this:
12712  * <pre><code>
12713 { 'results': 2, 'rows': [
12714     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12715     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12716 }
12717 </code></pre>
12718  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12719  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12720  * paged from the remote server.
12721  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12722  * @cfg {String} root name of the property which contains the Array of row objects.
12723  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12724  * @cfg {Array} fields Array of field definition objects
12725  * @constructor
12726  * Create a new JsonReader
12727  * @param {Object} meta Metadata configuration options
12728  * @param {Object} recordType Either an Array of field definition objects,
12729  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12730  */
12731 Roo.data.JsonReader = function(meta, recordType){
12732     
12733     meta = meta || {};
12734     // set some defaults:
12735     Roo.applyIf(meta, {
12736         totalProperty: 'total',
12737         successProperty : 'success',
12738         root : 'data',
12739         id : 'id'
12740     });
12741     
12742     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12743 };
12744 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12745     
12746     /**
12747      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12748      * Used by Store query builder to append _requestMeta to params.
12749      * 
12750      */
12751     metaFromRemote : false,
12752     /**
12753      * This method is only used by a DataProxy which has retrieved data from a remote server.
12754      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12755      * @return {Object} data A data block which is used by an Roo.data.Store object as
12756      * a cache of Roo.data.Records.
12757      */
12758     read : function(response){
12759         var json = response.responseText;
12760        
12761         var o = /* eval:var:o */ eval("("+json+")");
12762         if(!o) {
12763             throw {message: "JsonReader.read: Json object not found"};
12764         }
12765         
12766         if(o.metaData){
12767             
12768             delete this.ef;
12769             this.metaFromRemote = true;
12770             this.meta = o.metaData;
12771             this.recordType = Roo.data.Record.create(o.metaData.fields);
12772             this.onMetaChange(this.meta, this.recordType, o);
12773         }
12774         return this.readRecords(o);
12775     },
12776
12777     // private function a store will implement
12778     onMetaChange : function(meta, recordType, o){
12779
12780     },
12781
12782     /**
12783          * @ignore
12784          */
12785     simpleAccess: function(obj, subsc) {
12786         return obj[subsc];
12787     },
12788
12789         /**
12790          * @ignore
12791          */
12792     getJsonAccessor: function(){
12793         var re = /[\[\.]/;
12794         return function(expr) {
12795             try {
12796                 return(re.test(expr))
12797                     ? new Function("obj", "return obj." + expr)
12798                     : function(obj){
12799                         return obj[expr];
12800                     };
12801             } catch(e){}
12802             return Roo.emptyFn;
12803         };
12804     }(),
12805
12806     /**
12807      * Create a data block containing Roo.data.Records from an XML document.
12808      * @param {Object} o An object which contains an Array of row objects in the property specified
12809      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12810      * which contains the total size of the dataset.
12811      * @return {Object} data A data block which is used by an Roo.data.Store object as
12812      * a cache of Roo.data.Records.
12813      */
12814     readRecords : function(o){
12815         /**
12816          * After any data loads, the raw JSON data is available for further custom processing.
12817          * @type Object
12818          */
12819         this.o = o;
12820         var s = this.meta, Record = this.recordType,
12821             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12822
12823 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12824         if (!this.ef) {
12825             if(s.totalProperty) {
12826                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12827                 }
12828                 if(s.successProperty) {
12829                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12830                 }
12831                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12832                 if (s.id) {
12833                         var g = this.getJsonAccessor(s.id);
12834                         this.getId = function(rec) {
12835                                 var r = g(rec);  
12836                                 return (r === undefined || r === "") ? null : r;
12837                         };
12838                 } else {
12839                         this.getId = function(){return null;};
12840                 }
12841             this.ef = [];
12842             for(var jj = 0; jj < fl; jj++){
12843                 f = fi[jj];
12844                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12845                 this.ef[jj] = this.getJsonAccessor(map);
12846             }
12847         }
12848
12849         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12850         if(s.totalProperty){
12851             var vt = parseInt(this.getTotal(o), 10);
12852             if(!isNaN(vt)){
12853                 totalRecords = vt;
12854             }
12855         }
12856         if(s.successProperty){
12857             var vs = this.getSuccess(o);
12858             if(vs === false || vs === 'false'){
12859                 success = false;
12860             }
12861         }
12862         var records = [];
12863         for(var i = 0; i < c; i++){
12864                 var n = root[i];
12865             var values = {};
12866             var id = this.getId(n);
12867             for(var j = 0; j < fl; j++){
12868                 f = fi[j];
12869             var v = this.ef[j](n);
12870             if (!f.convert) {
12871                 Roo.log('missing convert for ' + f.name);
12872                 Roo.log(f);
12873                 continue;
12874             }
12875             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12876             }
12877             var record = new Record(values, id);
12878             record.json = n;
12879             records[i] = record;
12880         }
12881         return {
12882             raw : o,
12883             success : success,
12884             records : records,
12885             totalRecords : totalRecords
12886         };
12887     }
12888 });/*
12889  * Based on:
12890  * Ext JS Library 1.1.1
12891  * Copyright(c) 2006-2007, Ext JS, LLC.
12892  *
12893  * Originally Released Under LGPL - original licence link has changed is not relivant.
12894  *
12895  * Fork - LGPL
12896  * <script type="text/javascript">
12897  */
12898
12899 /**
12900  * @class Roo.data.ArrayReader
12901  * @extends Roo.data.DataReader
12902  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12903  * Each element of that Array represents a row of data fields. The
12904  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12905  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12906  * <p>
12907  * Example code:.
12908  * <pre><code>
12909 var RecordDef = Roo.data.Record.create([
12910     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12911     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12912 ]);
12913 var myReader = new Roo.data.ArrayReader({
12914     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12915 }, RecordDef);
12916 </code></pre>
12917  * <p>
12918  * This would consume an Array like this:
12919  * <pre><code>
12920 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12921   </code></pre>
12922  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12923  * @constructor
12924  * Create a new JsonReader
12925  * @param {Object} meta Metadata configuration options.
12926  * @param {Object} recordType Either an Array of field definition objects
12927  * as specified to {@link Roo.data.Record#create},
12928  * or an {@link Roo.data.Record} object
12929  * created using {@link Roo.data.Record#create}.
12930  */
12931 Roo.data.ArrayReader = function(meta, recordType){
12932     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12933 };
12934
12935 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12936     /**
12937      * Create a data block containing Roo.data.Records from an XML document.
12938      * @param {Object} o An Array of row objects which represents the dataset.
12939      * @return {Object} data A data block which is used by an Roo.data.Store object as
12940      * a cache of Roo.data.Records.
12941      */
12942     readRecords : function(o){
12943         var sid = this.meta ? this.meta.id : null;
12944         var recordType = this.recordType, fields = recordType.prototype.fields;
12945         var records = [];
12946         var root = o;
12947             for(var i = 0; i < root.length; i++){
12948                     var n = root[i];
12949                 var values = {};
12950                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12951                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12952                 var f = fields.items[j];
12953                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12954                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12955                 v = f.convert(v);
12956                 values[f.name] = v;
12957             }
12958                 var record = new recordType(values, id);
12959                 record.json = n;
12960                 records[records.length] = record;
12961             }
12962             return {
12963                 records : records,
12964                 totalRecords : records.length
12965             };
12966     }
12967 });/*
12968  * - LGPL
12969  * * 
12970  */
12971
12972 /**
12973  * @class Roo.bootstrap.ComboBox
12974  * @extends Roo.bootstrap.TriggerField
12975  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12976  * @cfg {Boolean} append (true|false) default false
12977  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12978  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12979  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12980  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12981  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12982  * @cfg {Boolean} animate default true
12983  * @cfg {Boolean} emptyResultText only for touch device
12984  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12985  * @cfg {String} emptyTitle default ''
12986  * @constructor
12987  * Create a new ComboBox.
12988  * @param {Object} config Configuration options
12989  */
12990 Roo.bootstrap.ComboBox = function(config){
12991     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12992     this.addEvents({
12993         /**
12994          * @event expand
12995          * Fires when the dropdown list is expanded
12996         * @param {Roo.bootstrap.ComboBox} combo This combo box
12997         */
12998         'expand' : true,
12999         /**
13000          * @event collapse
13001          * Fires when the dropdown list is collapsed
13002         * @param {Roo.bootstrap.ComboBox} combo This combo box
13003         */
13004         'collapse' : true,
13005         /**
13006          * @event beforeselect
13007          * Fires before a list item is selected. Return false to cancel the selection.
13008         * @param {Roo.bootstrap.ComboBox} combo This combo box
13009         * @param {Roo.data.Record} record The data record returned from the underlying store
13010         * @param {Number} index The index of the selected item in the dropdown list
13011         */
13012         'beforeselect' : true,
13013         /**
13014          * @event select
13015          * Fires when a list item is selected
13016         * @param {Roo.bootstrap.ComboBox} combo This combo box
13017         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13018         * @param {Number} index The index of the selected item in the dropdown list
13019         */
13020         'select' : true,
13021         /**
13022          * @event beforequery
13023          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13024          * The event object passed has these properties:
13025         * @param {Roo.bootstrap.ComboBox} combo This combo box
13026         * @param {String} query The query
13027         * @param {Boolean} forceAll true to force "all" query
13028         * @param {Boolean} cancel true to cancel the query
13029         * @param {Object} e The query event object
13030         */
13031         'beforequery': true,
13032          /**
13033          * @event add
13034          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13035         * @param {Roo.bootstrap.ComboBox} combo This combo box
13036         */
13037         'add' : true,
13038         /**
13039          * @event edit
13040          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13041         * @param {Roo.bootstrap.ComboBox} combo This combo box
13042         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13043         */
13044         'edit' : true,
13045         /**
13046          * @event remove
13047          * Fires when the remove value from the combobox array
13048         * @param {Roo.bootstrap.ComboBox} combo This combo box
13049         */
13050         'remove' : true,
13051         /**
13052          * @event afterremove
13053          * Fires when the remove value from the combobox array
13054         * @param {Roo.bootstrap.ComboBox} combo This combo box
13055         */
13056         'afterremove' : true,
13057         /**
13058          * @event specialfilter
13059          * Fires when specialfilter
13060             * @param {Roo.bootstrap.ComboBox} combo This combo box
13061             */
13062         'specialfilter' : true,
13063         /**
13064          * @event tick
13065          * Fires when tick the element
13066             * @param {Roo.bootstrap.ComboBox} combo This combo box
13067             */
13068         'tick' : true,
13069         /**
13070          * @event touchviewdisplay
13071          * Fires when touch view require special display (default is using displayField)
13072             * @param {Roo.bootstrap.ComboBox} combo This combo box
13073             * @param {Object} cfg set html .
13074             */
13075         'touchviewdisplay' : true
13076         
13077     });
13078     
13079     this.item = [];
13080     this.tickItems = [];
13081     
13082     this.selectedIndex = -1;
13083     if(this.mode == 'local'){
13084         if(config.queryDelay === undefined){
13085             this.queryDelay = 10;
13086         }
13087         if(config.minChars === undefined){
13088             this.minChars = 0;
13089         }
13090     }
13091 };
13092
13093 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13094      
13095     /**
13096      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13097      * rendering into an Roo.Editor, defaults to false)
13098      */
13099     /**
13100      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13101      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13102      */
13103     /**
13104      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13105      */
13106     /**
13107      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13108      * the dropdown list (defaults to undefined, with no header element)
13109      */
13110
13111      /**
13112      * @cfg {String/Roo.Template} tpl The template to use to render the output
13113      */
13114      
13115      /**
13116      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13117      */
13118     listWidth: undefined,
13119     /**
13120      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13121      * mode = 'remote' or 'text' if mode = 'local')
13122      */
13123     displayField: undefined,
13124     
13125     /**
13126      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13127      * mode = 'remote' or 'value' if mode = 'local'). 
13128      * Note: use of a valueField requires the user make a selection
13129      * in order for a value to be mapped.
13130      */
13131     valueField: undefined,
13132     /**
13133      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13134      */
13135     modalTitle : '',
13136     
13137     /**
13138      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13139      * field's data value (defaults to the underlying DOM element's name)
13140      */
13141     hiddenName: undefined,
13142     /**
13143      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13144      */
13145     listClass: '',
13146     /**
13147      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13148      */
13149     selectedClass: 'active',
13150     
13151     /**
13152      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13153      */
13154     shadow:'sides',
13155     /**
13156      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13157      * anchor positions (defaults to 'tl-bl')
13158      */
13159     listAlign: 'tl-bl?',
13160     /**
13161      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13162      */
13163     maxHeight: 300,
13164     /**
13165      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13166      * query specified by the allQuery config option (defaults to 'query')
13167      */
13168     triggerAction: 'query',
13169     /**
13170      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13171      * (defaults to 4, does not apply if editable = false)
13172      */
13173     minChars : 4,
13174     /**
13175      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13176      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13177      */
13178     typeAhead: false,
13179     /**
13180      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13181      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13182      */
13183     queryDelay: 500,
13184     /**
13185      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13186      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13187      */
13188     pageSize: 0,
13189     /**
13190      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13191      * when editable = true (defaults to false)
13192      */
13193     selectOnFocus:false,
13194     /**
13195      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13196      */
13197     queryParam: 'query',
13198     /**
13199      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13200      * when mode = 'remote' (defaults to 'Loading...')
13201      */
13202     loadingText: 'Loading...',
13203     /**
13204      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13205      */
13206     resizable: false,
13207     /**
13208      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13209      */
13210     handleHeight : 8,
13211     /**
13212      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13213      * traditional select (defaults to true)
13214      */
13215     editable: true,
13216     /**
13217      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13218      */
13219     allQuery: '',
13220     /**
13221      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13222      */
13223     mode: 'remote',
13224     /**
13225      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13226      * listWidth has a higher value)
13227      */
13228     minListWidth : 70,
13229     /**
13230      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13231      * allow the user to set arbitrary text into the field (defaults to false)
13232      */
13233     forceSelection:false,
13234     /**
13235      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13236      * if typeAhead = true (defaults to 250)
13237      */
13238     typeAheadDelay : 250,
13239     /**
13240      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13241      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13242      */
13243     valueNotFoundText : undefined,
13244     /**
13245      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13246      */
13247     blockFocus : false,
13248     
13249     /**
13250      * @cfg {Boolean} disableClear Disable showing of clear button.
13251      */
13252     disableClear : false,
13253     /**
13254      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13255      */
13256     alwaysQuery : false,
13257     
13258     /**
13259      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13260      */
13261     multiple : false,
13262     
13263     /**
13264      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13265      */
13266     invalidClass : "has-warning",
13267     
13268     /**
13269      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13270      */
13271     validClass : "has-success",
13272     
13273     /**
13274      * @cfg {Boolean} specialFilter (true|false) special filter default false
13275      */
13276     specialFilter : false,
13277     
13278     /**
13279      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13280      */
13281     mobileTouchView : true,
13282     
13283     /**
13284      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13285      */
13286     useNativeIOS : false,
13287     
13288     /**
13289      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13290      */
13291     mobile_restrict_height : false,
13292     
13293     ios_options : false,
13294     
13295     //private
13296     addicon : false,
13297     editicon: false,
13298     
13299     page: 0,
13300     hasQuery: false,
13301     append: false,
13302     loadNext: false,
13303     autoFocus : true,
13304     tickable : false,
13305     btnPosition : 'right',
13306     triggerList : true,
13307     showToggleBtn : true,
13308     animate : true,
13309     emptyResultText: 'Empty',
13310     triggerText : 'Select',
13311     emptyTitle : '',
13312     
13313     // element that contains real text value.. (when hidden is used..)
13314     
13315     getAutoCreate : function()
13316     {   
13317         var cfg = false;
13318         //render
13319         /*
13320          * Render classic select for iso
13321          */
13322         
13323         if(Roo.isIOS && this.useNativeIOS){
13324             cfg = this.getAutoCreateNativeIOS();
13325             return cfg;
13326         }
13327         
13328         /*
13329          * Touch Devices
13330          */
13331         
13332         if(Roo.isTouch && this.mobileTouchView){
13333             cfg = this.getAutoCreateTouchView();
13334             return cfg;;
13335         }
13336         
13337         /*
13338          *  Normal ComboBox
13339          */
13340         if(!this.tickable){
13341             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13342             return cfg;
13343         }
13344         
13345         /*
13346          *  ComboBox with tickable selections
13347          */
13348              
13349         var align = this.labelAlign || this.parentLabelAlign();
13350         
13351         cfg = {
13352             cls : 'form-group roo-combobox-tickable' //input-group
13353         };
13354         
13355         var btn_text_select = '';
13356         var btn_text_done = '';
13357         var btn_text_cancel = '';
13358         
13359         if (this.btn_text_show) {
13360             btn_text_select = 'Select';
13361             btn_text_done = 'Done';
13362             btn_text_cancel = 'Cancel'; 
13363         }
13364         
13365         var buttons = {
13366             tag : 'div',
13367             cls : 'tickable-buttons',
13368             cn : [
13369                 {
13370                     tag : 'button',
13371                     type : 'button',
13372                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13373                     //html : this.triggerText
13374                     html: btn_text_select
13375                 },
13376                 {
13377                     tag : 'button',
13378                     type : 'button',
13379                     name : 'ok',
13380                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13381                     //html : 'Done'
13382                     html: btn_text_done
13383                 },
13384                 {
13385                     tag : 'button',
13386                     type : 'button',
13387                     name : 'cancel',
13388                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13389                     //html : 'Cancel'
13390                     html: btn_text_cancel
13391                 }
13392             ]
13393         };
13394         
13395         if(this.editable){
13396             buttons.cn.unshift({
13397                 tag: 'input',
13398                 cls: 'roo-select2-search-field-input'
13399             });
13400         }
13401         
13402         var _this = this;
13403         
13404         Roo.each(buttons.cn, function(c){
13405             if (_this.size) {
13406                 c.cls += ' btn-' + _this.size;
13407             }
13408
13409             if (_this.disabled) {
13410                 c.disabled = true;
13411             }
13412         });
13413         
13414         var box = {
13415             tag: 'div',
13416             style : 'display: contents',
13417             cn: [
13418                 {
13419                     tag: 'input',
13420                     type : 'hidden',
13421                     cls: 'form-hidden-field'
13422                 },
13423                 {
13424                     tag: 'ul',
13425                     cls: 'roo-select2-choices',
13426                     cn:[
13427                         {
13428                             tag: 'li',
13429                             cls: 'roo-select2-search-field',
13430                             cn: [
13431                                 buttons
13432                             ]
13433                         }
13434                     ]
13435                 }
13436             ]
13437         };
13438         
13439         var combobox = {
13440             cls: 'roo-select2-container input-group roo-select2-container-multi',
13441             cn: [
13442                 
13443                 box
13444 //                {
13445 //                    tag: 'ul',
13446 //                    cls: 'typeahead typeahead-long dropdown-menu',
13447 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13448 //                }
13449             ]
13450         };
13451         
13452         if(this.hasFeedback && !this.allowBlank){
13453             
13454             var feedback = {
13455                 tag: 'span',
13456                 cls: 'glyphicon form-control-feedback'
13457             };
13458
13459             combobox.cn.push(feedback);
13460         }
13461         
13462         var indicator = {
13463             tag : 'i',
13464             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13465             tooltip : 'This field is required'
13466         };
13467         if (Roo.bootstrap.version == 4) {
13468             indicator = {
13469                 tag : 'i',
13470                 style : 'display:none'
13471             };
13472         }
13473         if (align ==='left' && this.fieldLabel.length) {
13474             
13475             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13476             
13477             cfg.cn = [
13478                 indicator,
13479                 {
13480                     tag: 'label',
13481                     'for' :  id,
13482                     cls : 'control-label col-form-label',
13483                     html : this.fieldLabel
13484
13485                 },
13486                 {
13487                     cls : "", 
13488                     cn: [
13489                         combobox
13490                     ]
13491                 }
13492
13493             ];
13494             
13495             var labelCfg = cfg.cn[1];
13496             var contentCfg = cfg.cn[2];
13497             
13498
13499             if(this.indicatorpos == 'right'){
13500                 
13501                 cfg.cn = [
13502                     {
13503                         tag: 'label',
13504                         'for' :  id,
13505                         cls : 'control-label col-form-label',
13506                         cn : [
13507                             {
13508                                 tag : 'span',
13509                                 html : this.fieldLabel
13510                             },
13511                             indicator
13512                         ]
13513                     },
13514                     {
13515                         cls : "",
13516                         cn: [
13517                             combobox
13518                         ]
13519                     }
13520
13521                 ];
13522                 
13523                 
13524                 
13525                 labelCfg = cfg.cn[0];
13526                 contentCfg = cfg.cn[1];
13527             
13528             }
13529             
13530             if(this.labelWidth > 12){
13531                 labelCfg.style = "width: " + this.labelWidth + 'px';
13532             }
13533             
13534             if(this.labelWidth < 13 && this.labelmd == 0){
13535                 this.labelmd = this.labelWidth;
13536             }
13537             
13538             if(this.labellg > 0){
13539                 labelCfg.cls += ' col-lg-' + this.labellg;
13540                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13541             }
13542             
13543             if(this.labelmd > 0){
13544                 labelCfg.cls += ' col-md-' + this.labelmd;
13545                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13546             }
13547             
13548             if(this.labelsm > 0){
13549                 labelCfg.cls += ' col-sm-' + this.labelsm;
13550                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13551             }
13552             
13553             if(this.labelxs > 0){
13554                 labelCfg.cls += ' col-xs-' + this.labelxs;
13555                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13556             }
13557                 
13558                 
13559         } else if ( this.fieldLabel.length) {
13560 //                Roo.log(" label");
13561                  cfg.cn = [
13562                    indicator,
13563                     {
13564                         tag: 'label',
13565                         //cls : 'input-group-addon',
13566                         html : this.fieldLabel
13567                     },
13568                     combobox
13569                 ];
13570                 
13571                 if(this.indicatorpos == 'right'){
13572                     cfg.cn = [
13573                         {
13574                             tag: 'label',
13575                             //cls : 'input-group-addon',
13576                             html : this.fieldLabel
13577                         },
13578                         indicator,
13579                         combobox
13580                     ];
13581                     
13582                 }
13583
13584         } else {
13585             
13586 //                Roo.log(" no label && no align");
13587                 cfg = combobox
13588                      
13589                 
13590         }
13591          
13592         var settings=this;
13593         ['xs','sm','md','lg'].map(function(size){
13594             if (settings[size]) {
13595                 cfg.cls += ' col-' + size + '-' + settings[size];
13596             }
13597         });
13598         
13599         return cfg;
13600         
13601     },
13602     
13603     _initEventsCalled : false,
13604     
13605     // private
13606     initEvents: function()
13607     {   
13608         if (this._initEventsCalled) { // as we call render... prevent looping...
13609             return;
13610         }
13611         this._initEventsCalled = true;
13612         
13613         if (!this.store) {
13614             throw "can not find store for combo";
13615         }
13616         
13617         this.indicator = this.indicatorEl();
13618         
13619         this.store = Roo.factory(this.store, Roo.data);
13620         this.store.parent = this;
13621         
13622         // if we are building from html. then this element is so complex, that we can not really
13623         // use the rendered HTML.
13624         // so we have to trash and replace the previous code.
13625         if (Roo.XComponent.build_from_html) {
13626             // remove this element....
13627             var e = this.el.dom, k=0;
13628             while (e ) { e = e.previousSibling;  ++k;}
13629
13630             this.el.remove();
13631             
13632             this.el=false;
13633             this.rendered = false;
13634             
13635             this.render(this.parent().getChildContainer(true), k);
13636         }
13637         
13638         if(Roo.isIOS && this.useNativeIOS){
13639             this.initIOSView();
13640             return;
13641         }
13642         
13643         /*
13644          * Touch Devices
13645          */
13646         
13647         if(Roo.isTouch && this.mobileTouchView){
13648             this.initTouchView();
13649             return;
13650         }
13651         
13652         if(this.tickable){
13653             this.initTickableEvents();
13654             return;
13655         }
13656         
13657         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13658         
13659         if(this.hiddenName){
13660             
13661             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13662             
13663             this.hiddenField.dom.value =
13664                 this.hiddenValue !== undefined ? this.hiddenValue :
13665                 this.value !== undefined ? this.value : '';
13666
13667             // prevent input submission
13668             this.el.dom.removeAttribute('name');
13669             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13670              
13671              
13672         }
13673         //if(Roo.isGecko){
13674         //    this.el.dom.setAttribute('autocomplete', 'off');
13675         //}
13676         
13677         var cls = 'x-combo-list';
13678         
13679         //this.list = new Roo.Layer({
13680         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13681         //});
13682         
13683         var _this = this;
13684         
13685         (function(){
13686             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13687             _this.list.setWidth(lw);
13688         }).defer(100);
13689         
13690         this.list.on('mouseover', this.onViewOver, this);
13691         this.list.on('mousemove', this.onViewMove, this);
13692         this.list.on('scroll', this.onViewScroll, this);
13693         
13694         /*
13695         this.list.swallowEvent('mousewheel');
13696         this.assetHeight = 0;
13697
13698         if(this.title){
13699             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13700             this.assetHeight += this.header.getHeight();
13701         }
13702
13703         this.innerList = this.list.createChild({cls:cls+'-inner'});
13704         this.innerList.on('mouseover', this.onViewOver, this);
13705         this.innerList.on('mousemove', this.onViewMove, this);
13706         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13707         
13708         if(this.allowBlank && !this.pageSize && !this.disableClear){
13709             this.footer = this.list.createChild({cls:cls+'-ft'});
13710             this.pageTb = new Roo.Toolbar(this.footer);
13711            
13712         }
13713         if(this.pageSize){
13714             this.footer = this.list.createChild({cls:cls+'-ft'});
13715             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13716                     {pageSize: this.pageSize});
13717             
13718         }
13719         
13720         if (this.pageTb && this.allowBlank && !this.disableClear) {
13721             var _this = this;
13722             this.pageTb.add(new Roo.Toolbar.Fill(), {
13723                 cls: 'x-btn-icon x-btn-clear',
13724                 text: '&#160;',
13725                 handler: function()
13726                 {
13727                     _this.collapse();
13728                     _this.clearValue();
13729                     _this.onSelect(false, -1);
13730                 }
13731             });
13732         }
13733         if (this.footer) {
13734             this.assetHeight += this.footer.getHeight();
13735         }
13736         */
13737             
13738         if(!this.tpl){
13739             this.tpl = Roo.bootstrap.version == 4 ?
13740                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13741                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13742         }
13743
13744         this.view = new Roo.View(this.list, this.tpl, {
13745             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13746         });
13747         //this.view.wrapEl.setDisplayed(false);
13748         this.view.on('click', this.onViewClick, this);
13749         
13750         
13751         this.store.on('beforeload', this.onBeforeLoad, this);
13752         this.store.on('load', this.onLoad, this);
13753         this.store.on('loadexception', this.onLoadException, this);
13754         /*
13755         if(this.resizable){
13756             this.resizer = new Roo.Resizable(this.list,  {
13757                pinned:true, handles:'se'
13758             });
13759             this.resizer.on('resize', function(r, w, h){
13760                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13761                 this.listWidth = w;
13762                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13763                 this.restrictHeight();
13764             }, this);
13765             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13766         }
13767         */
13768         if(!this.editable){
13769             this.editable = true;
13770             this.setEditable(false);
13771         }
13772         
13773         /*
13774         
13775         if (typeof(this.events.add.listeners) != 'undefined') {
13776             
13777             this.addicon = this.wrap.createChild(
13778                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13779        
13780             this.addicon.on('click', function(e) {
13781                 this.fireEvent('add', this);
13782             }, this);
13783         }
13784         if (typeof(this.events.edit.listeners) != 'undefined') {
13785             
13786             this.editicon = this.wrap.createChild(
13787                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13788             if (this.addicon) {
13789                 this.editicon.setStyle('margin-left', '40px');
13790             }
13791             this.editicon.on('click', function(e) {
13792                 
13793                 // we fire even  if inothing is selected..
13794                 this.fireEvent('edit', this, this.lastData );
13795                 
13796             }, this);
13797         }
13798         */
13799         
13800         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13801             "up" : function(e){
13802                 this.inKeyMode = true;
13803                 this.selectPrev();
13804             },
13805
13806             "down" : function(e){
13807                 if(!this.isExpanded()){
13808                     this.onTriggerClick();
13809                 }else{
13810                     this.inKeyMode = true;
13811                     this.selectNext();
13812                 }
13813             },
13814
13815             "enter" : function(e){
13816 //                this.onViewClick();
13817                 //return true;
13818                 this.collapse();
13819                 
13820                 if(this.fireEvent("specialkey", this, e)){
13821                     this.onViewClick(false);
13822                 }
13823                 
13824                 return true;
13825             },
13826
13827             "esc" : function(e){
13828                 this.collapse();
13829             },
13830
13831             "tab" : function(e){
13832                 this.collapse();
13833                 
13834                 if(this.fireEvent("specialkey", this, e)){
13835                     this.onViewClick(false);
13836                 }
13837                 
13838                 return true;
13839             },
13840
13841             scope : this,
13842
13843             doRelay : function(foo, bar, hname){
13844                 if(hname == 'down' || this.scope.isExpanded()){
13845                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13846                 }
13847                 return true;
13848             },
13849
13850             forceKeyDown: true
13851         });
13852         
13853         
13854         this.queryDelay = Math.max(this.queryDelay || 10,
13855                 this.mode == 'local' ? 10 : 250);
13856         
13857         
13858         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13859         
13860         if(this.typeAhead){
13861             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13862         }
13863         if(this.editable !== false){
13864             this.inputEl().on("keyup", this.onKeyUp, this);
13865         }
13866         if(this.forceSelection){
13867             this.inputEl().on('blur', this.doForce, this);
13868         }
13869         
13870         if(this.multiple){
13871             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13872             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13873         }
13874     },
13875     
13876     initTickableEvents: function()
13877     {   
13878         this.createList();
13879         
13880         if(this.hiddenName){
13881             
13882             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13883             
13884             this.hiddenField.dom.value =
13885                 this.hiddenValue !== undefined ? this.hiddenValue :
13886                 this.value !== undefined ? this.value : '';
13887
13888             // prevent input submission
13889             this.el.dom.removeAttribute('name');
13890             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13891              
13892              
13893         }
13894         
13895 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13896         
13897         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13898         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13899         if(this.triggerList){
13900             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13901         }
13902          
13903         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13904         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13905         
13906         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13907         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13908         
13909         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13910         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13911         
13912         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13913         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13914         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13915         
13916         this.okBtn.hide();
13917         this.cancelBtn.hide();
13918         
13919         var _this = this;
13920         
13921         (function(){
13922             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13923             _this.list.setWidth(lw);
13924         }).defer(100);
13925         
13926         this.list.on('mouseover', this.onViewOver, this);
13927         this.list.on('mousemove', this.onViewMove, this);
13928         
13929         this.list.on('scroll', this.onViewScroll, this);
13930         
13931         if(!this.tpl){
13932             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13933                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13934         }
13935
13936         this.view = new Roo.View(this.list, this.tpl, {
13937             singleSelect:true,
13938             tickable:true,
13939             parent:this,
13940             store: this.store,
13941             selectedClass: this.selectedClass
13942         });
13943         
13944         //this.view.wrapEl.setDisplayed(false);
13945         this.view.on('click', this.onViewClick, this);
13946         
13947         
13948         
13949         this.store.on('beforeload', this.onBeforeLoad, this);
13950         this.store.on('load', this.onLoad, this);
13951         this.store.on('loadexception', this.onLoadException, this);
13952         
13953         if(this.editable){
13954             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13955                 "up" : function(e){
13956                     this.inKeyMode = true;
13957                     this.selectPrev();
13958                 },
13959
13960                 "down" : function(e){
13961                     this.inKeyMode = true;
13962                     this.selectNext();
13963                 },
13964
13965                 "enter" : function(e){
13966                     if(this.fireEvent("specialkey", this, e)){
13967                         this.onViewClick(false);
13968                     }
13969                     
13970                     return true;
13971                 },
13972
13973                 "esc" : function(e){
13974                     this.onTickableFooterButtonClick(e, false, false);
13975                 },
13976
13977                 "tab" : function(e){
13978                     this.fireEvent("specialkey", this, e);
13979                     
13980                     this.onTickableFooterButtonClick(e, false, false);
13981                     
13982                     return true;
13983                 },
13984
13985                 scope : this,
13986
13987                 doRelay : function(e, fn, key){
13988                     if(this.scope.isExpanded()){
13989                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13990                     }
13991                     return true;
13992                 },
13993
13994                 forceKeyDown: true
13995             });
13996         }
13997         
13998         this.queryDelay = Math.max(this.queryDelay || 10,
13999                 this.mode == 'local' ? 10 : 250);
14000         
14001         
14002         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14003         
14004         if(this.typeAhead){
14005             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14006         }
14007         
14008         if(this.editable !== false){
14009             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14010         }
14011         
14012         this.indicator = this.indicatorEl();
14013         
14014         if(this.indicator){
14015             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14016             this.indicator.hide();
14017         }
14018         
14019     },
14020
14021     onDestroy : function(){
14022         if(this.view){
14023             this.view.setStore(null);
14024             this.view.el.removeAllListeners();
14025             this.view.el.remove();
14026             this.view.purgeListeners();
14027         }
14028         if(this.list){
14029             this.list.dom.innerHTML  = '';
14030         }
14031         
14032         if(this.store){
14033             this.store.un('beforeload', this.onBeforeLoad, this);
14034             this.store.un('load', this.onLoad, this);
14035             this.store.un('loadexception', this.onLoadException, this);
14036         }
14037         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14038     },
14039
14040     // private
14041     fireKey : function(e){
14042         if(e.isNavKeyPress() && !this.list.isVisible()){
14043             this.fireEvent("specialkey", this, e);
14044         }
14045     },
14046
14047     // private
14048     onResize: function(w, h){
14049 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14050 //        
14051 //        if(typeof w != 'number'){
14052 //            // we do not handle it!?!?
14053 //            return;
14054 //        }
14055 //        var tw = this.trigger.getWidth();
14056 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14057 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14058 //        var x = w - tw;
14059 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14060 //            
14061 //        //this.trigger.setStyle('left', x+'px');
14062 //        
14063 //        if(this.list && this.listWidth === undefined){
14064 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14065 //            this.list.setWidth(lw);
14066 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14067 //        }
14068         
14069     
14070         
14071     },
14072
14073     /**
14074      * Allow or prevent the user from directly editing the field text.  If false is passed,
14075      * the user will only be able to select from the items defined in the dropdown list.  This method
14076      * is the runtime equivalent of setting the 'editable' config option at config time.
14077      * @param {Boolean} value True to allow the user to directly edit the field text
14078      */
14079     setEditable : function(value){
14080         if(value == this.editable){
14081             return;
14082         }
14083         this.editable = value;
14084         if(!value){
14085             this.inputEl().dom.setAttribute('readOnly', true);
14086             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14087             this.inputEl().addClass('x-combo-noedit');
14088         }else{
14089             this.inputEl().dom.setAttribute('readOnly', false);
14090             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14091             this.inputEl().removeClass('x-combo-noedit');
14092         }
14093     },
14094
14095     // private
14096     
14097     onBeforeLoad : function(combo,opts){
14098         if(!this.hasFocus){
14099             return;
14100         }
14101          if (!opts.add) {
14102             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14103          }
14104         this.restrictHeight();
14105         this.selectedIndex = -1;
14106     },
14107
14108     // private
14109     onLoad : function(){
14110         
14111         this.hasQuery = false;
14112         
14113         if(!this.hasFocus){
14114             return;
14115         }
14116         
14117         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14118             this.loading.hide();
14119         }
14120         
14121         if(this.store.getCount() > 0){
14122             
14123             this.expand();
14124             this.restrictHeight();
14125             if(this.lastQuery == this.allQuery){
14126                 if(this.editable && !this.tickable){
14127                     this.inputEl().dom.select();
14128                 }
14129                 
14130                 if(
14131                     !this.selectByValue(this.value, true) &&
14132                     this.autoFocus && 
14133                     (
14134                         !this.store.lastOptions ||
14135                         typeof(this.store.lastOptions.add) == 'undefined' || 
14136                         this.store.lastOptions.add != true
14137                     )
14138                 ){
14139                     this.select(0, true);
14140                 }
14141             }else{
14142                 if(this.autoFocus){
14143                     this.selectNext();
14144                 }
14145                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14146                     this.taTask.delay(this.typeAheadDelay);
14147                 }
14148             }
14149         }else{
14150             this.onEmptyResults();
14151         }
14152         
14153         //this.el.focus();
14154     },
14155     // private
14156     onLoadException : function()
14157     {
14158         this.hasQuery = false;
14159         
14160         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14161             this.loading.hide();
14162         }
14163         
14164         if(this.tickable && this.editable){
14165             return;
14166         }
14167         
14168         this.collapse();
14169         // only causes errors at present
14170         //Roo.log(this.store.reader.jsonData);
14171         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14172             // fixme
14173             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14174         //}
14175         
14176         
14177     },
14178     // private
14179     onTypeAhead : function(){
14180         if(this.store.getCount() > 0){
14181             var r = this.store.getAt(0);
14182             var newValue = r.data[this.displayField];
14183             var len = newValue.length;
14184             var selStart = this.getRawValue().length;
14185             
14186             if(selStart != len){
14187                 this.setRawValue(newValue);
14188                 this.selectText(selStart, newValue.length);
14189             }
14190         }
14191     },
14192
14193     // private
14194     onSelect : function(record, index){
14195         
14196         if(this.fireEvent('beforeselect', this, record, index) !== false){
14197         
14198             this.setFromData(index > -1 ? record.data : false);
14199             
14200             this.collapse();
14201             this.fireEvent('select', this, record, index);
14202         }
14203     },
14204
14205     /**
14206      * Returns the currently selected field value or empty string if no value is set.
14207      * @return {String} value The selected value
14208      */
14209     getValue : function()
14210     {
14211         if(Roo.isIOS && this.useNativeIOS){
14212             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14213         }
14214         
14215         if(this.multiple){
14216             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14217         }
14218         
14219         if(this.valueField){
14220             return typeof this.value != 'undefined' ? this.value : '';
14221         }else{
14222             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14223         }
14224     },
14225     
14226     getRawValue : function()
14227     {
14228         if(Roo.isIOS && this.useNativeIOS){
14229             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14230         }
14231         
14232         var v = this.inputEl().getValue();
14233         
14234         return v;
14235     },
14236
14237     /**
14238      * Clears any text/value currently set in the field
14239      */
14240     clearValue : function(){
14241         
14242         if(this.hiddenField){
14243             this.hiddenField.dom.value = '';
14244         }
14245         this.value = '';
14246         this.setRawValue('');
14247         this.lastSelectionText = '';
14248         this.lastData = false;
14249         
14250         var close = this.closeTriggerEl();
14251         
14252         if(close){
14253             close.hide();
14254         }
14255         
14256         this.validate();
14257         
14258     },
14259
14260     /**
14261      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14262      * will be displayed in the field.  If the value does not match the data value of an existing item,
14263      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14264      * Otherwise the field will be blank (although the value will still be set).
14265      * @param {String} value The value to match
14266      */
14267     setValue : function(v)
14268     {
14269         if(Roo.isIOS && this.useNativeIOS){
14270             this.setIOSValue(v);
14271             return;
14272         }
14273         
14274         if(this.multiple){
14275             this.syncValue();
14276             return;
14277         }
14278         
14279         var text = v;
14280         if(this.valueField){
14281             var r = this.findRecord(this.valueField, v);
14282             if(r){
14283                 text = r.data[this.displayField];
14284             }else if(this.valueNotFoundText !== undefined){
14285                 text = this.valueNotFoundText;
14286             }
14287         }
14288         this.lastSelectionText = text;
14289         if(this.hiddenField){
14290             this.hiddenField.dom.value = v;
14291         }
14292         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14293         this.value = v;
14294         
14295         var close = this.closeTriggerEl();
14296         
14297         if(close){
14298             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14299         }
14300         
14301         this.validate();
14302     },
14303     /**
14304      * @property {Object} the last set data for the element
14305      */
14306     
14307     lastData : false,
14308     /**
14309      * Sets the value of the field based on a object which is related to the record format for the store.
14310      * @param {Object} value the value to set as. or false on reset?
14311      */
14312     setFromData : function(o){
14313         
14314         if(this.multiple){
14315             this.addItem(o);
14316             return;
14317         }
14318             
14319         var dv = ''; // display value
14320         var vv = ''; // value value..
14321         this.lastData = o;
14322         if (this.displayField) {
14323             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14324         } else {
14325             // this is an error condition!!!
14326             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14327         }
14328         
14329         if(this.valueField){
14330             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14331         }
14332         
14333         var close = this.closeTriggerEl();
14334         
14335         if(close){
14336             if(dv.length || vv * 1 > 0){
14337                 close.show() ;
14338                 this.blockFocus=true;
14339             } else {
14340                 close.hide();
14341             }             
14342         }
14343         
14344         if(this.hiddenField){
14345             this.hiddenField.dom.value = vv;
14346             
14347             this.lastSelectionText = dv;
14348             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14349             this.value = vv;
14350             return;
14351         }
14352         // no hidden field.. - we store the value in 'value', but still display
14353         // display field!!!!
14354         this.lastSelectionText = dv;
14355         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14356         this.value = vv;
14357         
14358         
14359         
14360     },
14361     // private
14362     reset : function(){
14363         // overridden so that last data is reset..
14364         
14365         if(this.multiple){
14366             this.clearItem();
14367             return;
14368         }
14369         
14370         this.setValue(this.originalValue);
14371         //this.clearInvalid();
14372         this.lastData = false;
14373         if (this.view) {
14374             this.view.clearSelections();
14375         }
14376         
14377         this.validate();
14378     },
14379     // private
14380     findRecord : function(prop, value){
14381         var record;
14382         if(this.store.getCount() > 0){
14383             this.store.each(function(r){
14384                 if(r.data[prop] == value){
14385                     record = r;
14386                     return false;
14387                 }
14388                 return true;
14389             });
14390         }
14391         return record;
14392     },
14393     
14394     getName: function()
14395     {
14396         // returns hidden if it's set..
14397         if (!this.rendered) {return ''};
14398         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14399         
14400     },
14401     // private
14402     onViewMove : function(e, t){
14403         this.inKeyMode = false;
14404     },
14405
14406     // private
14407     onViewOver : function(e, t){
14408         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14409             return;
14410         }
14411         var item = this.view.findItemFromChild(t);
14412         
14413         if(item){
14414             var index = this.view.indexOf(item);
14415             this.select(index, false);
14416         }
14417     },
14418
14419     // private
14420     onViewClick : function(view, doFocus, el, e)
14421     {
14422         var index = this.view.getSelectedIndexes()[0];
14423         
14424         var r = this.store.getAt(index);
14425         
14426         if(this.tickable){
14427             
14428             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14429                 return;
14430             }
14431             
14432             var rm = false;
14433             var _this = this;
14434             
14435             Roo.each(this.tickItems, function(v,k){
14436                 
14437                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14438                     Roo.log(v);
14439                     _this.tickItems.splice(k, 1);
14440                     
14441                     if(typeof(e) == 'undefined' && view == false){
14442                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14443                     }
14444                     
14445                     rm = true;
14446                     return;
14447                 }
14448             });
14449             
14450             if(rm){
14451                 return;
14452             }
14453             
14454             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14455                 this.tickItems.push(r.data);
14456             }
14457             
14458             if(typeof(e) == 'undefined' && view == false){
14459                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14460             }
14461                     
14462             return;
14463         }
14464         
14465         if(r){
14466             this.onSelect(r, index);
14467         }
14468         if(doFocus !== false && !this.blockFocus){
14469             this.inputEl().focus();
14470         }
14471     },
14472
14473     // private
14474     restrictHeight : function(){
14475         //this.innerList.dom.style.height = '';
14476         //var inner = this.innerList.dom;
14477         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14478         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14479         //this.list.beginUpdate();
14480         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14481         this.list.alignTo(this.inputEl(), this.listAlign);
14482         this.list.alignTo(this.inputEl(), this.listAlign);
14483         //this.list.endUpdate();
14484     },
14485
14486     // private
14487     onEmptyResults : function(){
14488         
14489         if(this.tickable && this.editable){
14490             this.hasFocus = false;
14491             this.restrictHeight();
14492             return;
14493         }
14494         
14495         this.collapse();
14496     },
14497
14498     /**
14499      * Returns true if the dropdown list is expanded, else false.
14500      */
14501     isExpanded : function(){
14502         return this.list.isVisible();
14503     },
14504
14505     /**
14506      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14507      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14508      * @param {String} value The data value of the item to select
14509      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14510      * selected item if it is not currently in view (defaults to true)
14511      * @return {Boolean} True if the value matched an item in the list, else false
14512      */
14513     selectByValue : function(v, scrollIntoView){
14514         if(v !== undefined && v !== null){
14515             var r = this.findRecord(this.valueField || this.displayField, v);
14516             if(r){
14517                 this.select(this.store.indexOf(r), scrollIntoView);
14518                 return true;
14519             }
14520         }
14521         return false;
14522     },
14523
14524     /**
14525      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14526      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14527      * @param {Number} index The zero-based index of the list item to select
14528      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14529      * selected item if it is not currently in view (defaults to true)
14530      */
14531     select : function(index, scrollIntoView){
14532         this.selectedIndex = index;
14533         this.view.select(index);
14534         if(scrollIntoView !== false){
14535             var el = this.view.getNode(index);
14536             /*
14537              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14538              */
14539             if(el){
14540                 this.list.scrollChildIntoView(el, false);
14541             }
14542         }
14543     },
14544
14545     // private
14546     selectNext : function(){
14547         var ct = this.store.getCount();
14548         if(ct > 0){
14549             if(this.selectedIndex == -1){
14550                 this.select(0);
14551             }else if(this.selectedIndex < ct-1){
14552                 this.select(this.selectedIndex+1);
14553             }
14554         }
14555     },
14556
14557     // private
14558     selectPrev : function(){
14559         var ct = this.store.getCount();
14560         if(ct > 0){
14561             if(this.selectedIndex == -1){
14562                 this.select(0);
14563             }else if(this.selectedIndex != 0){
14564                 this.select(this.selectedIndex-1);
14565             }
14566         }
14567     },
14568
14569     // private
14570     onKeyUp : function(e){
14571         if(this.editable !== false && !e.isSpecialKey()){
14572             this.lastKey = e.getKey();
14573             this.dqTask.delay(this.queryDelay);
14574         }
14575     },
14576
14577     // private
14578     validateBlur : function(){
14579         return !this.list || !this.list.isVisible();   
14580     },
14581
14582     // private
14583     initQuery : function(){
14584         
14585         var v = this.getRawValue();
14586         
14587         if(this.tickable && this.editable){
14588             v = this.tickableInputEl().getValue();
14589         }
14590         
14591         this.doQuery(v);
14592     },
14593
14594     // private
14595     doForce : function(){
14596         if(this.inputEl().dom.value.length > 0){
14597             this.inputEl().dom.value =
14598                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14599              
14600         }
14601     },
14602
14603     /**
14604      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14605      * query allowing the query action to be canceled if needed.
14606      * @param {String} query The SQL query to execute
14607      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14608      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14609      * saved in the current store (defaults to false)
14610      */
14611     doQuery : function(q, forceAll){
14612         
14613         if(q === undefined || q === null){
14614             q = '';
14615         }
14616         var qe = {
14617             query: q,
14618             forceAll: forceAll,
14619             combo: this,
14620             cancel:false
14621         };
14622         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14623             return false;
14624         }
14625         q = qe.query;
14626         
14627         forceAll = qe.forceAll;
14628         if(forceAll === true || (q.length >= this.minChars)){
14629             
14630             this.hasQuery = true;
14631             
14632             if(this.lastQuery != q || this.alwaysQuery){
14633                 this.lastQuery = q;
14634                 if(this.mode == 'local'){
14635                     this.selectedIndex = -1;
14636                     if(forceAll){
14637                         this.store.clearFilter();
14638                     }else{
14639                         
14640                         if(this.specialFilter){
14641                             this.fireEvent('specialfilter', this);
14642                             this.onLoad();
14643                             return;
14644                         }
14645                         
14646                         this.store.filter(this.displayField, q);
14647                     }
14648                     
14649                     this.store.fireEvent("datachanged", this.store);
14650                     
14651                     this.onLoad();
14652                     
14653                     
14654                 }else{
14655                     
14656                     this.store.baseParams[this.queryParam] = q;
14657                     
14658                     var options = {params : this.getParams(q)};
14659                     
14660                     if(this.loadNext){
14661                         options.add = true;
14662                         options.params.start = this.page * this.pageSize;
14663                     }
14664                     
14665                     this.store.load(options);
14666                     
14667                     /*
14668                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14669                      *  we should expand the list on onLoad
14670                      *  so command out it
14671                      */
14672 //                    this.expand();
14673                 }
14674             }else{
14675                 this.selectedIndex = -1;
14676                 this.onLoad();   
14677             }
14678         }
14679         
14680         this.loadNext = false;
14681     },
14682     
14683     // private
14684     getParams : function(q){
14685         var p = {};
14686         //p[this.queryParam] = q;
14687         
14688         if(this.pageSize){
14689             p.start = 0;
14690             p.limit = this.pageSize;
14691         }
14692         return p;
14693     },
14694
14695     /**
14696      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14697      */
14698     collapse : function(){
14699         if(!this.isExpanded()){
14700             return;
14701         }
14702         
14703         this.list.hide();
14704         
14705         this.hasFocus = false;
14706         
14707         if(this.tickable){
14708             this.okBtn.hide();
14709             this.cancelBtn.hide();
14710             this.trigger.show();
14711             
14712             if(this.editable){
14713                 this.tickableInputEl().dom.value = '';
14714                 this.tickableInputEl().blur();
14715             }
14716             
14717         }
14718         
14719         Roo.get(document).un('mousedown', this.collapseIf, this);
14720         Roo.get(document).un('mousewheel', this.collapseIf, this);
14721         if (!this.editable) {
14722             Roo.get(document).un('keydown', this.listKeyPress, this);
14723         }
14724         this.fireEvent('collapse', this);
14725         
14726         this.validate();
14727     },
14728
14729     // private
14730     collapseIf : function(e){
14731         var in_combo  = e.within(this.el);
14732         var in_list =  e.within(this.list);
14733         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14734         
14735         if (in_combo || in_list || is_list) {
14736             //e.stopPropagation();
14737             return;
14738         }
14739         
14740         if(this.tickable){
14741             this.onTickableFooterButtonClick(e, false, false);
14742         }
14743
14744         this.collapse();
14745         
14746     },
14747
14748     /**
14749      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14750      */
14751     expand : function(){
14752        
14753         if(this.isExpanded() || !this.hasFocus){
14754             return;
14755         }
14756         
14757         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14758         this.list.setWidth(lw);
14759         
14760         Roo.log('expand');
14761         
14762         this.list.show();
14763         
14764         this.restrictHeight();
14765         
14766         if(this.tickable){
14767             
14768             this.tickItems = Roo.apply([], this.item);
14769             
14770             this.okBtn.show();
14771             this.cancelBtn.show();
14772             this.trigger.hide();
14773             
14774             if(this.editable){
14775                 this.tickableInputEl().focus();
14776             }
14777             
14778         }
14779         
14780         Roo.get(document).on('mousedown', this.collapseIf, this);
14781         Roo.get(document).on('mousewheel', this.collapseIf, this);
14782         if (!this.editable) {
14783             Roo.get(document).on('keydown', this.listKeyPress, this);
14784         }
14785         
14786         this.fireEvent('expand', this);
14787     },
14788
14789     // private
14790     // Implements the default empty TriggerField.onTriggerClick function
14791     onTriggerClick : function(e)
14792     {
14793         Roo.log('trigger click');
14794         
14795         if(this.disabled || !this.triggerList){
14796             return;
14797         }
14798         
14799         this.page = 0;
14800         this.loadNext = false;
14801         
14802         if(this.isExpanded()){
14803             this.collapse();
14804             if (!this.blockFocus) {
14805                 this.inputEl().focus();
14806             }
14807             
14808         }else {
14809             this.hasFocus = true;
14810             if(this.triggerAction == 'all') {
14811                 this.doQuery(this.allQuery, true);
14812             } else {
14813                 this.doQuery(this.getRawValue());
14814             }
14815             if (!this.blockFocus) {
14816                 this.inputEl().focus();
14817             }
14818         }
14819     },
14820     
14821     onTickableTriggerClick : function(e)
14822     {
14823         if(this.disabled){
14824             return;
14825         }
14826         
14827         this.page = 0;
14828         this.loadNext = false;
14829         this.hasFocus = true;
14830         
14831         if(this.triggerAction == 'all') {
14832             this.doQuery(this.allQuery, true);
14833         } else {
14834             this.doQuery(this.getRawValue());
14835         }
14836     },
14837     
14838     onSearchFieldClick : function(e)
14839     {
14840         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14841             this.onTickableFooterButtonClick(e, false, false);
14842             return;
14843         }
14844         
14845         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14846             return;
14847         }
14848         
14849         this.page = 0;
14850         this.loadNext = false;
14851         this.hasFocus = true;
14852         
14853         if(this.triggerAction == 'all') {
14854             this.doQuery(this.allQuery, true);
14855         } else {
14856             this.doQuery(this.getRawValue());
14857         }
14858     },
14859     
14860     listKeyPress : function(e)
14861     {
14862         //Roo.log('listkeypress');
14863         // scroll to first matching element based on key pres..
14864         if (e.isSpecialKey()) {
14865             return false;
14866         }
14867         var k = String.fromCharCode(e.getKey()).toUpperCase();
14868         //Roo.log(k);
14869         var match  = false;
14870         var csel = this.view.getSelectedNodes();
14871         var cselitem = false;
14872         if (csel.length) {
14873             var ix = this.view.indexOf(csel[0]);
14874             cselitem  = this.store.getAt(ix);
14875             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14876                 cselitem = false;
14877             }
14878             
14879         }
14880         
14881         this.store.each(function(v) { 
14882             if (cselitem) {
14883                 // start at existing selection.
14884                 if (cselitem.id == v.id) {
14885                     cselitem = false;
14886                 }
14887                 return true;
14888             }
14889                 
14890             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14891                 match = this.store.indexOf(v);
14892                 return false;
14893             }
14894             return true;
14895         }, this);
14896         
14897         if (match === false) {
14898             return true; // no more action?
14899         }
14900         // scroll to?
14901         this.view.select(match);
14902         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14903         sn.scrollIntoView(sn.dom.parentNode, false);
14904     },
14905     
14906     onViewScroll : function(e, t){
14907         
14908         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){
14909             return;
14910         }
14911         
14912         this.hasQuery = true;
14913         
14914         this.loading = this.list.select('.loading', true).first();
14915         
14916         if(this.loading === null){
14917             this.list.createChild({
14918                 tag: 'div',
14919                 cls: 'loading roo-select2-more-results roo-select2-active',
14920                 html: 'Loading more results...'
14921             });
14922             
14923             this.loading = this.list.select('.loading', true).first();
14924             
14925             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14926             
14927             this.loading.hide();
14928         }
14929         
14930         this.loading.show();
14931         
14932         var _combo = this;
14933         
14934         this.page++;
14935         this.loadNext = true;
14936         
14937         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14938         
14939         return;
14940     },
14941     
14942     addItem : function(o)
14943     {   
14944         var dv = ''; // display value
14945         
14946         if (this.displayField) {
14947             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14948         } else {
14949             // this is an error condition!!!
14950             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14951         }
14952         
14953         if(!dv.length){
14954             return;
14955         }
14956         
14957         var choice = this.choices.createChild({
14958             tag: 'li',
14959             cls: 'roo-select2-search-choice',
14960             cn: [
14961                 {
14962                     tag: 'div',
14963                     html: dv
14964                 },
14965                 {
14966                     tag: 'a',
14967                     href: '#',
14968                     cls: 'roo-select2-search-choice-close fa fa-times',
14969                     tabindex: '-1'
14970                 }
14971             ]
14972             
14973         }, this.searchField);
14974         
14975         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14976         
14977         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14978         
14979         this.item.push(o);
14980         
14981         this.lastData = o;
14982         
14983         this.syncValue();
14984         
14985         this.inputEl().dom.value = '';
14986         
14987         this.validate();
14988     },
14989     
14990     onRemoveItem : function(e, _self, o)
14991     {
14992         e.preventDefault();
14993         
14994         this.lastItem = Roo.apply([], this.item);
14995         
14996         var index = this.item.indexOf(o.data) * 1;
14997         
14998         if( index < 0){
14999             Roo.log('not this item?!');
15000             return;
15001         }
15002         
15003         this.item.splice(index, 1);
15004         o.item.remove();
15005         
15006         this.syncValue();
15007         
15008         this.fireEvent('remove', this, e);
15009         
15010         this.validate();
15011         
15012     },
15013     
15014     syncValue : function()
15015     {
15016         if(!this.item.length){
15017             this.clearValue();
15018             return;
15019         }
15020             
15021         var value = [];
15022         var _this = this;
15023         Roo.each(this.item, function(i){
15024             if(_this.valueField){
15025                 value.push(i[_this.valueField]);
15026                 return;
15027             }
15028
15029             value.push(i);
15030         });
15031
15032         this.value = value.join(',');
15033
15034         if(this.hiddenField){
15035             this.hiddenField.dom.value = this.value;
15036         }
15037         
15038         this.store.fireEvent("datachanged", this.store);
15039         
15040         this.validate();
15041     },
15042     
15043     clearItem : function()
15044     {
15045         if(!this.multiple){
15046             return;
15047         }
15048         
15049         this.item = [];
15050         
15051         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15052            c.remove();
15053         });
15054         
15055         this.syncValue();
15056         
15057         this.validate();
15058         
15059         if(this.tickable && !Roo.isTouch){
15060             this.view.refresh();
15061         }
15062     },
15063     
15064     inputEl: function ()
15065     {
15066         if(Roo.isIOS && this.useNativeIOS){
15067             return this.el.select('select.roo-ios-select', true).first();
15068         }
15069         
15070         if(Roo.isTouch && this.mobileTouchView){
15071             return this.el.select('input.form-control',true).first();
15072         }
15073         
15074         if(this.tickable){
15075             return this.searchField;
15076         }
15077         
15078         return this.el.select('input.form-control',true).first();
15079     },
15080     
15081     onTickableFooterButtonClick : function(e, btn, el)
15082     {
15083         e.preventDefault();
15084         
15085         this.lastItem = Roo.apply([], this.item);
15086         
15087         if(btn && btn.name == 'cancel'){
15088             this.tickItems = Roo.apply([], this.item);
15089             this.collapse();
15090             return;
15091         }
15092         
15093         this.clearItem();
15094         
15095         var _this = this;
15096         
15097         Roo.each(this.tickItems, function(o){
15098             _this.addItem(o);
15099         });
15100         
15101         this.collapse();
15102         
15103     },
15104     
15105     validate : function()
15106     {
15107         if(this.getVisibilityEl().hasClass('hidden')){
15108             return true;
15109         }
15110         
15111         var v = this.getRawValue();
15112         
15113         if(this.multiple){
15114             v = this.getValue();
15115         }
15116         
15117         if(this.disabled || this.allowBlank || v.length){
15118             this.markValid();
15119             return true;
15120         }
15121         
15122         this.markInvalid();
15123         return false;
15124     },
15125     
15126     tickableInputEl : function()
15127     {
15128         if(!this.tickable || !this.editable){
15129             return this.inputEl();
15130         }
15131         
15132         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15133     },
15134     
15135     
15136     getAutoCreateTouchView : function()
15137     {
15138         var id = Roo.id();
15139         
15140         var cfg = {
15141             cls: 'form-group' //input-group
15142         };
15143         
15144         var input =  {
15145             tag: 'input',
15146             id : id,
15147             type : this.inputType,
15148             cls : 'form-control x-combo-noedit',
15149             autocomplete: 'new-password',
15150             placeholder : this.placeholder || '',
15151             readonly : true
15152         };
15153         
15154         if (this.name) {
15155             input.name = this.name;
15156         }
15157         
15158         if (this.size) {
15159             input.cls += ' input-' + this.size;
15160         }
15161         
15162         if (this.disabled) {
15163             input.disabled = true;
15164         }
15165         
15166         var inputblock = {
15167             cls : '',
15168             cn : [
15169                 input
15170             ]
15171         };
15172         
15173         if(this.before){
15174             inputblock.cls += ' input-group';
15175             
15176             inputblock.cn.unshift({
15177                 tag :'span',
15178                 cls : 'input-group-addon input-group-prepend input-group-text',
15179                 html : this.before
15180             });
15181         }
15182         
15183         if(this.removable && !this.multiple){
15184             inputblock.cls += ' roo-removable';
15185             
15186             inputblock.cn.push({
15187                 tag: 'button',
15188                 html : 'x',
15189                 cls : 'roo-combo-removable-btn close'
15190             });
15191         }
15192
15193         if(this.hasFeedback && !this.allowBlank){
15194             
15195             inputblock.cls += ' has-feedback';
15196             
15197             inputblock.cn.push({
15198                 tag: 'span',
15199                 cls: 'glyphicon form-control-feedback'
15200             });
15201             
15202         }
15203         
15204         if (this.after) {
15205             
15206             inputblock.cls += (this.before) ? '' : ' input-group';
15207             
15208             inputblock.cn.push({
15209                 tag :'span',
15210                 cls : 'input-group-addon input-group-append input-group-text',
15211                 html : this.after
15212             });
15213         }
15214
15215         
15216         var ibwrap = inputblock;
15217         
15218         if(this.multiple){
15219             ibwrap = {
15220                 tag: 'ul',
15221                 cls: 'roo-select2-choices',
15222                 cn:[
15223                     {
15224                         tag: 'li',
15225                         cls: 'roo-select2-search-field',
15226                         cn: [
15227
15228                             inputblock
15229                         ]
15230                     }
15231                 ]
15232             };
15233         
15234             
15235         }
15236         
15237         var combobox = {
15238             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15239             cn: [
15240                 {
15241                     tag: 'input',
15242                     type : 'hidden',
15243                     cls: 'form-hidden-field'
15244                 },
15245                 ibwrap
15246             ]
15247         };
15248         
15249         if(!this.multiple && this.showToggleBtn){
15250             
15251             var caret = {
15252                         tag: 'span',
15253                         cls: 'caret'
15254             };
15255             
15256             if (this.caret != false) {
15257                 caret = {
15258                      tag: 'i',
15259                      cls: 'fa fa-' + this.caret
15260                 };
15261                 
15262             }
15263             
15264             combobox.cn.push({
15265                 tag :'span',
15266                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15267                 cn : [
15268                     caret,
15269                     {
15270                         tag: 'span',
15271                         cls: 'combobox-clear',
15272                         cn  : [
15273                             {
15274                                 tag : 'i',
15275                                 cls: 'icon-remove'
15276                             }
15277                         ]
15278                     }
15279                 ]
15280
15281             })
15282         }
15283         
15284         if(this.multiple){
15285             combobox.cls += ' roo-select2-container-multi';
15286         }
15287         
15288         var align = this.labelAlign || this.parentLabelAlign();
15289         
15290         if (align ==='left' && this.fieldLabel.length) {
15291
15292             cfg.cn = [
15293                 {
15294                    tag : 'i',
15295                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15296                    tooltip : 'This field is required'
15297                 },
15298                 {
15299                     tag: 'label',
15300                     cls : 'control-label col-form-label',
15301                     html : this.fieldLabel
15302
15303                 },
15304                 {
15305                     cls : '', 
15306                     cn: [
15307                         combobox
15308                     ]
15309                 }
15310             ];
15311             
15312             var labelCfg = cfg.cn[1];
15313             var contentCfg = cfg.cn[2];
15314             
15315
15316             if(this.indicatorpos == 'right'){
15317                 cfg.cn = [
15318                     {
15319                         tag: 'label',
15320                         'for' :  id,
15321                         cls : 'control-label col-form-label',
15322                         cn : [
15323                             {
15324                                 tag : 'span',
15325                                 html : this.fieldLabel
15326                             },
15327                             {
15328                                 tag : 'i',
15329                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15330                                 tooltip : 'This field is required'
15331                             }
15332                         ]
15333                     },
15334                     {
15335                         cls : "",
15336                         cn: [
15337                             combobox
15338                         ]
15339                     }
15340
15341                 ];
15342                 
15343                 labelCfg = cfg.cn[0];
15344                 contentCfg = cfg.cn[1];
15345             }
15346             
15347            
15348             
15349             if(this.labelWidth > 12){
15350                 labelCfg.style = "width: " + this.labelWidth + 'px';
15351             }
15352             
15353             if(this.labelWidth < 13 && this.labelmd == 0){
15354                 this.labelmd = this.labelWidth;
15355             }
15356             
15357             if(this.labellg > 0){
15358                 labelCfg.cls += ' col-lg-' + this.labellg;
15359                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15360             }
15361             
15362             if(this.labelmd > 0){
15363                 labelCfg.cls += ' col-md-' + this.labelmd;
15364                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15365             }
15366             
15367             if(this.labelsm > 0){
15368                 labelCfg.cls += ' col-sm-' + this.labelsm;
15369                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15370             }
15371             
15372             if(this.labelxs > 0){
15373                 labelCfg.cls += ' col-xs-' + this.labelxs;
15374                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15375             }
15376                 
15377                 
15378         } else if ( this.fieldLabel.length) {
15379             cfg.cn = [
15380                 {
15381                    tag : 'i',
15382                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15383                    tooltip : 'This field is required'
15384                 },
15385                 {
15386                     tag: 'label',
15387                     cls : 'control-label',
15388                     html : this.fieldLabel
15389
15390                 },
15391                 {
15392                     cls : '', 
15393                     cn: [
15394                         combobox
15395                     ]
15396                 }
15397             ];
15398             
15399             if(this.indicatorpos == 'right'){
15400                 cfg.cn = [
15401                     {
15402                         tag: 'label',
15403                         cls : 'control-label',
15404                         html : this.fieldLabel,
15405                         cn : [
15406                             {
15407                                tag : 'i',
15408                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15409                                tooltip : 'This field is required'
15410                             }
15411                         ]
15412                     },
15413                     {
15414                         cls : '', 
15415                         cn: [
15416                             combobox
15417                         ]
15418                     }
15419                 ];
15420             }
15421         } else {
15422             cfg.cn = combobox;    
15423         }
15424         
15425         
15426         var settings = this;
15427         
15428         ['xs','sm','md','lg'].map(function(size){
15429             if (settings[size]) {
15430                 cfg.cls += ' col-' + size + '-' + settings[size];
15431             }
15432         });
15433         
15434         return cfg;
15435     },
15436     
15437     initTouchView : function()
15438     {
15439         this.renderTouchView();
15440         
15441         this.touchViewEl.on('scroll', function(){
15442             this.el.dom.scrollTop = 0;
15443         }, this);
15444         
15445         this.originalValue = this.getValue();
15446         
15447         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15448         
15449         this.inputEl().on("click", this.showTouchView, this);
15450         if (this.triggerEl) {
15451             this.triggerEl.on("click", this.showTouchView, this);
15452         }
15453         
15454         
15455         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15456         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15457         
15458         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15459         
15460         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15461         this.store.on('load', this.onTouchViewLoad, this);
15462         this.store.on('loadexception', this.onTouchViewLoadException, this);
15463         
15464         if(this.hiddenName){
15465             
15466             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15467             
15468             this.hiddenField.dom.value =
15469                 this.hiddenValue !== undefined ? this.hiddenValue :
15470                 this.value !== undefined ? this.value : '';
15471         
15472             this.el.dom.removeAttribute('name');
15473             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15474         }
15475         
15476         if(this.multiple){
15477             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15478             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15479         }
15480         
15481         if(this.removable && !this.multiple){
15482             var close = this.closeTriggerEl();
15483             if(close){
15484                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15485                 close.on('click', this.removeBtnClick, this, close);
15486             }
15487         }
15488         /*
15489          * fix the bug in Safari iOS8
15490          */
15491         this.inputEl().on("focus", function(e){
15492             document.activeElement.blur();
15493         }, this);
15494         
15495         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15496         
15497         return;
15498         
15499         
15500     },
15501     
15502     renderTouchView : function()
15503     {
15504         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15505         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15506         
15507         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15508         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15509         
15510         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15511         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15512         this.touchViewBodyEl.setStyle('overflow', 'auto');
15513         
15514         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15515         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15516         
15517         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15518         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15519         
15520     },
15521     
15522     showTouchView : function()
15523     {
15524         if(this.disabled){
15525             return;
15526         }
15527         
15528         this.touchViewHeaderEl.hide();
15529
15530         if(this.modalTitle.length){
15531             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15532             this.touchViewHeaderEl.show();
15533         }
15534
15535         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15536         this.touchViewEl.show();
15537
15538         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15539         
15540         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15541         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15542
15543         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15544
15545         if(this.modalTitle.length){
15546             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15547         }
15548         
15549         this.touchViewBodyEl.setHeight(bodyHeight);
15550
15551         if(this.animate){
15552             var _this = this;
15553             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15554         }else{
15555             this.touchViewEl.addClass('in');
15556         }
15557         
15558         if(this._touchViewMask){
15559             Roo.get(document.body).addClass("x-body-masked");
15560             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15561             this._touchViewMask.setStyle('z-index', 10000);
15562             this._touchViewMask.addClass('show');
15563         }
15564         
15565         this.doTouchViewQuery();
15566         
15567     },
15568     
15569     hideTouchView : function()
15570     {
15571         this.touchViewEl.removeClass('in');
15572
15573         if(this.animate){
15574             var _this = this;
15575             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15576         }else{
15577             this.touchViewEl.setStyle('display', 'none');
15578         }
15579         
15580         if(this._touchViewMask){
15581             this._touchViewMask.removeClass('show');
15582             Roo.get(document.body).removeClass("x-body-masked");
15583         }
15584     },
15585     
15586     setTouchViewValue : function()
15587     {
15588         if(this.multiple){
15589             this.clearItem();
15590         
15591             var _this = this;
15592
15593             Roo.each(this.tickItems, function(o){
15594                 this.addItem(o);
15595             }, this);
15596         }
15597         
15598         this.hideTouchView();
15599     },
15600     
15601     doTouchViewQuery : function()
15602     {
15603         var qe = {
15604             query: '',
15605             forceAll: true,
15606             combo: this,
15607             cancel:false
15608         };
15609         
15610         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15611             return false;
15612         }
15613         
15614         if(!this.alwaysQuery || this.mode == 'local'){
15615             this.onTouchViewLoad();
15616             return;
15617         }
15618         
15619         this.store.load();
15620     },
15621     
15622     onTouchViewBeforeLoad : function(combo,opts)
15623     {
15624         return;
15625     },
15626
15627     // private
15628     onTouchViewLoad : function()
15629     {
15630         if(this.store.getCount() < 1){
15631             this.onTouchViewEmptyResults();
15632             return;
15633         }
15634         
15635         this.clearTouchView();
15636         
15637         var rawValue = this.getRawValue();
15638         
15639         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15640         
15641         this.tickItems = [];
15642         
15643         this.store.data.each(function(d, rowIndex){
15644             var row = this.touchViewListGroup.createChild(template);
15645             
15646             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15647                 row.addClass(d.data.cls);
15648             }
15649             
15650             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15651                 var cfg = {
15652                     data : d.data,
15653                     html : d.data[this.displayField]
15654                 };
15655                 
15656                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15657                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15658                 }
15659             }
15660             row.removeClass('selected');
15661             if(!this.multiple && this.valueField &&
15662                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15663             {
15664                 // radio buttons..
15665                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15666                 row.addClass('selected');
15667             }
15668             
15669             if(this.multiple && this.valueField &&
15670                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15671             {
15672                 
15673                 // checkboxes...
15674                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15675                 this.tickItems.push(d.data);
15676             }
15677             
15678             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15679             
15680         }, this);
15681         
15682         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15683         
15684         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15685
15686         if(this.modalTitle.length){
15687             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15688         }
15689
15690         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15691         
15692         if(this.mobile_restrict_height && listHeight < bodyHeight){
15693             this.touchViewBodyEl.setHeight(listHeight);
15694         }
15695         
15696         var _this = this;
15697         
15698         if(firstChecked && listHeight > bodyHeight){
15699             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15700         }
15701         
15702     },
15703     
15704     onTouchViewLoadException : function()
15705     {
15706         this.hideTouchView();
15707     },
15708     
15709     onTouchViewEmptyResults : function()
15710     {
15711         this.clearTouchView();
15712         
15713         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15714         
15715         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15716         
15717     },
15718     
15719     clearTouchView : function()
15720     {
15721         this.touchViewListGroup.dom.innerHTML = '';
15722     },
15723     
15724     onTouchViewClick : function(e, el, o)
15725     {
15726         e.preventDefault();
15727         
15728         var row = o.row;
15729         var rowIndex = o.rowIndex;
15730         
15731         var r = this.store.getAt(rowIndex);
15732         
15733         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15734             
15735             if(!this.multiple){
15736                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15737                     c.dom.removeAttribute('checked');
15738                 }, this);
15739
15740                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15741
15742                 this.setFromData(r.data);
15743
15744                 var close = this.closeTriggerEl();
15745
15746                 if(close){
15747                     close.show();
15748                 }
15749
15750                 this.hideTouchView();
15751
15752                 this.fireEvent('select', this, r, rowIndex);
15753
15754                 return;
15755             }
15756
15757             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15758                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15759                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15760                 return;
15761             }
15762
15763             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15764             this.addItem(r.data);
15765             this.tickItems.push(r.data);
15766         }
15767     },
15768     
15769     getAutoCreateNativeIOS : function()
15770     {
15771         var cfg = {
15772             cls: 'form-group' //input-group,
15773         };
15774         
15775         var combobox =  {
15776             tag: 'select',
15777             cls : 'roo-ios-select'
15778         };
15779         
15780         if (this.name) {
15781             combobox.name = this.name;
15782         }
15783         
15784         if (this.disabled) {
15785             combobox.disabled = true;
15786         }
15787         
15788         var settings = this;
15789         
15790         ['xs','sm','md','lg'].map(function(size){
15791             if (settings[size]) {
15792                 cfg.cls += ' col-' + size + '-' + settings[size];
15793             }
15794         });
15795         
15796         cfg.cn = combobox;
15797         
15798         return cfg;
15799         
15800     },
15801     
15802     initIOSView : function()
15803     {
15804         this.store.on('load', this.onIOSViewLoad, this);
15805         
15806         return;
15807     },
15808     
15809     onIOSViewLoad : function()
15810     {
15811         if(this.store.getCount() < 1){
15812             return;
15813         }
15814         
15815         this.clearIOSView();
15816         
15817         if(this.allowBlank) {
15818             
15819             var default_text = '-- SELECT --';
15820             
15821             if(this.placeholder.length){
15822                 default_text = this.placeholder;
15823             }
15824             
15825             if(this.emptyTitle.length){
15826                 default_text += ' - ' + this.emptyTitle + ' -';
15827             }
15828             
15829             var opt = this.inputEl().createChild({
15830                 tag: 'option',
15831                 value : 0,
15832                 html : default_text
15833             });
15834             
15835             var o = {};
15836             o[this.valueField] = 0;
15837             o[this.displayField] = default_text;
15838             
15839             this.ios_options.push({
15840                 data : o,
15841                 el : opt
15842             });
15843             
15844         }
15845         
15846         this.store.data.each(function(d, rowIndex){
15847             
15848             var html = '';
15849             
15850             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15851                 html = d.data[this.displayField];
15852             }
15853             
15854             var value = '';
15855             
15856             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15857                 value = d.data[this.valueField];
15858             }
15859             
15860             var option = {
15861                 tag: 'option',
15862                 value : value,
15863                 html : html
15864             };
15865             
15866             if(this.value == d.data[this.valueField]){
15867                 option['selected'] = true;
15868             }
15869             
15870             var opt = this.inputEl().createChild(option);
15871             
15872             this.ios_options.push({
15873                 data : d.data,
15874                 el : opt
15875             });
15876             
15877         }, this);
15878         
15879         this.inputEl().on('change', function(){
15880            this.fireEvent('select', this);
15881         }, this);
15882         
15883     },
15884     
15885     clearIOSView: function()
15886     {
15887         this.inputEl().dom.innerHTML = '';
15888         
15889         this.ios_options = [];
15890     },
15891     
15892     setIOSValue: function(v)
15893     {
15894         this.value = v;
15895         
15896         if(!this.ios_options){
15897             return;
15898         }
15899         
15900         Roo.each(this.ios_options, function(opts){
15901            
15902            opts.el.dom.removeAttribute('selected');
15903            
15904            if(opts.data[this.valueField] != v){
15905                return;
15906            }
15907            
15908            opts.el.dom.setAttribute('selected', true);
15909            
15910         }, this);
15911     }
15912
15913     /** 
15914     * @cfg {Boolean} grow 
15915     * @hide 
15916     */
15917     /** 
15918     * @cfg {Number} growMin 
15919     * @hide 
15920     */
15921     /** 
15922     * @cfg {Number} growMax 
15923     * @hide 
15924     */
15925     /**
15926      * @hide
15927      * @method autoSize
15928      */
15929 });
15930
15931 Roo.apply(Roo.bootstrap.ComboBox,  {
15932     
15933     header : {
15934         tag: 'div',
15935         cls: 'modal-header',
15936         cn: [
15937             {
15938                 tag: 'h4',
15939                 cls: 'modal-title'
15940             }
15941         ]
15942     },
15943     
15944     body : {
15945         tag: 'div',
15946         cls: 'modal-body',
15947         cn: [
15948             {
15949                 tag: 'ul',
15950                 cls: 'list-group'
15951             }
15952         ]
15953     },
15954     
15955     listItemRadio : {
15956         tag: 'li',
15957         cls: 'list-group-item',
15958         cn: [
15959             {
15960                 tag: 'span',
15961                 cls: 'roo-combobox-list-group-item-value'
15962             },
15963             {
15964                 tag: 'div',
15965                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15966                 cn: [
15967                     {
15968                         tag: 'input',
15969                         type: 'radio'
15970                     },
15971                     {
15972                         tag: 'label'
15973                     }
15974                 ]
15975             }
15976         ]
15977     },
15978     
15979     listItemCheckbox : {
15980         tag: 'li',
15981         cls: 'list-group-item',
15982         cn: [
15983             {
15984                 tag: 'span',
15985                 cls: 'roo-combobox-list-group-item-value'
15986             },
15987             {
15988                 tag: 'div',
15989                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15990                 cn: [
15991                     {
15992                         tag: 'input',
15993                         type: 'checkbox'
15994                     },
15995                     {
15996                         tag: 'label'
15997                     }
15998                 ]
15999             }
16000         ]
16001     },
16002     
16003     emptyResult : {
16004         tag: 'div',
16005         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16006     },
16007     
16008     footer : {
16009         tag: 'div',
16010         cls: 'modal-footer',
16011         cn: [
16012             {
16013                 tag: 'div',
16014                 cls: 'row',
16015                 cn: [
16016                     {
16017                         tag: 'div',
16018                         cls: 'col-xs-6 text-left',
16019                         cn: {
16020                             tag: 'button',
16021                             cls: 'btn btn-danger roo-touch-view-cancel',
16022                             html: 'Cancel'
16023                         }
16024                     },
16025                     {
16026                         tag: 'div',
16027                         cls: 'col-xs-6 text-right',
16028                         cn: {
16029                             tag: 'button',
16030                             cls: 'btn btn-success roo-touch-view-ok',
16031                             html: 'OK'
16032                         }
16033                     }
16034                 ]
16035             }
16036         ]
16037         
16038     }
16039 });
16040
16041 Roo.apply(Roo.bootstrap.ComboBox,  {
16042     
16043     touchViewTemplate : {
16044         tag: 'div',
16045         cls: 'modal fade roo-combobox-touch-view',
16046         cn: [
16047             {
16048                 tag: 'div',
16049                 cls: 'modal-dialog',
16050                 style : 'position:fixed', // we have to fix position....
16051                 cn: [
16052                     {
16053                         tag: 'div',
16054                         cls: 'modal-content',
16055                         cn: [
16056                             Roo.bootstrap.ComboBox.header,
16057                             Roo.bootstrap.ComboBox.body,
16058                             Roo.bootstrap.ComboBox.footer
16059                         ]
16060                     }
16061                 ]
16062             }
16063         ]
16064     }
16065 });/*
16066  * Based on:
16067  * Ext JS Library 1.1.1
16068  * Copyright(c) 2006-2007, Ext JS, LLC.
16069  *
16070  * Originally Released Under LGPL - original licence link has changed is not relivant.
16071  *
16072  * Fork - LGPL
16073  * <script type="text/javascript">
16074  */
16075
16076 /**
16077  * @class Roo.View
16078  * @extends Roo.util.Observable
16079  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16080  * This class also supports single and multi selection modes. <br>
16081  * Create a data model bound view:
16082  <pre><code>
16083  var store = new Roo.data.Store(...);
16084
16085  var view = new Roo.View({
16086     el : "my-element",
16087     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16088  
16089     singleSelect: true,
16090     selectedClass: "ydataview-selected",
16091     store: store
16092  });
16093
16094  // listen for node click?
16095  view.on("click", function(vw, index, node, e){
16096  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16097  });
16098
16099  // load XML data
16100  dataModel.load("foobar.xml");
16101  </code></pre>
16102  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16103  * <br><br>
16104  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16105  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16106  * 
16107  * Note: old style constructor is still suported (container, template, config)
16108  * 
16109  * @constructor
16110  * Create a new View
16111  * @param {Object} config The config object
16112  * 
16113  */
16114 Roo.View = function(config, depreciated_tpl, depreciated_config){
16115     
16116     this.parent = false;
16117     
16118     if (typeof(depreciated_tpl) == 'undefined') {
16119         // new way.. - universal constructor.
16120         Roo.apply(this, config);
16121         this.el  = Roo.get(this.el);
16122     } else {
16123         // old format..
16124         this.el  = Roo.get(config);
16125         this.tpl = depreciated_tpl;
16126         Roo.apply(this, depreciated_config);
16127     }
16128     this.wrapEl  = this.el.wrap().wrap();
16129     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16130     
16131     
16132     if(typeof(this.tpl) == "string"){
16133         this.tpl = new Roo.Template(this.tpl);
16134     } else {
16135         // support xtype ctors..
16136         this.tpl = new Roo.factory(this.tpl, Roo);
16137     }
16138     
16139     
16140     this.tpl.compile();
16141     
16142     /** @private */
16143     this.addEvents({
16144         /**
16145          * @event beforeclick
16146          * Fires before a click is processed. Returns false to cancel the default action.
16147          * @param {Roo.View} this
16148          * @param {Number} index The index of the target node
16149          * @param {HTMLElement} node The target node
16150          * @param {Roo.EventObject} e The raw event object
16151          */
16152             "beforeclick" : true,
16153         /**
16154          * @event click
16155          * Fires when a template node is clicked.
16156          * @param {Roo.View} this
16157          * @param {Number} index The index of the target node
16158          * @param {HTMLElement} node The target node
16159          * @param {Roo.EventObject} e The raw event object
16160          */
16161             "click" : true,
16162         /**
16163          * @event dblclick
16164          * Fires when a template node is double clicked.
16165          * @param {Roo.View} this
16166          * @param {Number} index The index of the target node
16167          * @param {HTMLElement} node The target node
16168          * @param {Roo.EventObject} e The raw event object
16169          */
16170             "dblclick" : true,
16171         /**
16172          * @event contextmenu
16173          * Fires when a template node is right clicked.
16174          * @param {Roo.View} this
16175          * @param {Number} index The index of the target node
16176          * @param {HTMLElement} node The target node
16177          * @param {Roo.EventObject} e The raw event object
16178          */
16179             "contextmenu" : true,
16180         /**
16181          * @event selectionchange
16182          * Fires when the selected nodes change.
16183          * @param {Roo.View} this
16184          * @param {Array} selections Array of the selected nodes
16185          */
16186             "selectionchange" : true,
16187     
16188         /**
16189          * @event beforeselect
16190          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16191          * @param {Roo.View} this
16192          * @param {HTMLElement} node The node to be selected
16193          * @param {Array} selections Array of currently selected nodes
16194          */
16195             "beforeselect" : true,
16196         /**
16197          * @event preparedata
16198          * Fires on every row to render, to allow you to change the data.
16199          * @param {Roo.View} this
16200          * @param {Object} data to be rendered (change this)
16201          */
16202           "preparedata" : true
16203           
16204           
16205         });
16206
16207
16208
16209     this.el.on({
16210         "click": this.onClick,
16211         "dblclick": this.onDblClick,
16212         "contextmenu": this.onContextMenu,
16213         scope:this
16214     });
16215
16216     this.selections = [];
16217     this.nodes = [];
16218     this.cmp = new Roo.CompositeElementLite([]);
16219     if(this.store){
16220         this.store = Roo.factory(this.store, Roo.data);
16221         this.setStore(this.store, true);
16222     }
16223     
16224     if ( this.footer && this.footer.xtype) {
16225            
16226          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16227         
16228         this.footer.dataSource = this.store;
16229         this.footer.container = fctr;
16230         this.footer = Roo.factory(this.footer, Roo);
16231         fctr.insertFirst(this.el);
16232         
16233         // this is a bit insane - as the paging toolbar seems to detach the el..
16234 //        dom.parentNode.parentNode.parentNode
16235          // they get detached?
16236     }
16237     
16238     
16239     Roo.View.superclass.constructor.call(this);
16240     
16241     
16242 };
16243
16244 Roo.extend(Roo.View, Roo.util.Observable, {
16245     
16246      /**
16247      * @cfg {Roo.data.Store} store Data store to load data from.
16248      */
16249     store : false,
16250     
16251     /**
16252      * @cfg {String|Roo.Element} el The container element.
16253      */
16254     el : '',
16255     
16256     /**
16257      * @cfg {String|Roo.Template} tpl The template used by this View 
16258      */
16259     tpl : false,
16260     /**
16261      * @cfg {String} dataName the named area of the template to use as the data area
16262      *                          Works with domtemplates roo-name="name"
16263      */
16264     dataName: false,
16265     /**
16266      * @cfg {String} selectedClass The css class to add to selected nodes
16267      */
16268     selectedClass : "x-view-selected",
16269      /**
16270      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16271      */
16272     emptyText : "",
16273     
16274     /**
16275      * @cfg {String} text to display on mask (default Loading)
16276      */
16277     mask : false,
16278     /**
16279      * @cfg {Boolean} multiSelect Allow multiple selection
16280      */
16281     multiSelect : false,
16282     /**
16283      * @cfg {Boolean} singleSelect Allow single selection
16284      */
16285     singleSelect:  false,
16286     
16287     /**
16288      * @cfg {Boolean} toggleSelect - selecting 
16289      */
16290     toggleSelect : false,
16291     
16292     /**
16293      * @cfg {Boolean} tickable - selecting 
16294      */
16295     tickable : false,
16296     
16297     /**
16298      * Returns the element this view is bound to.
16299      * @return {Roo.Element}
16300      */
16301     getEl : function(){
16302         return this.wrapEl;
16303     },
16304     
16305     
16306
16307     /**
16308      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16309      */
16310     refresh : function(){
16311         //Roo.log('refresh');
16312         var t = this.tpl;
16313         
16314         // if we are using something like 'domtemplate', then
16315         // the what gets used is:
16316         // t.applySubtemplate(NAME, data, wrapping data..)
16317         // the outer template then get' applied with
16318         //     the store 'extra data'
16319         // and the body get's added to the
16320         //      roo-name="data" node?
16321         //      <span class='roo-tpl-{name}'></span> ?????
16322         
16323         
16324         
16325         this.clearSelections();
16326         this.el.update("");
16327         var html = [];
16328         var records = this.store.getRange();
16329         if(records.length < 1) {
16330             
16331             // is this valid??  = should it render a template??
16332             
16333             this.el.update(this.emptyText);
16334             return;
16335         }
16336         var el = this.el;
16337         if (this.dataName) {
16338             this.el.update(t.apply(this.store.meta)); //????
16339             el = this.el.child('.roo-tpl-' + this.dataName);
16340         }
16341         
16342         for(var i = 0, len = records.length; i < len; i++){
16343             var data = this.prepareData(records[i].data, i, records[i]);
16344             this.fireEvent("preparedata", this, data, i, records[i]);
16345             
16346             var d = Roo.apply({}, data);
16347             
16348             if(this.tickable){
16349                 Roo.apply(d, {'roo-id' : Roo.id()});
16350                 
16351                 var _this = this;
16352             
16353                 Roo.each(this.parent.item, function(item){
16354                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16355                         return;
16356                     }
16357                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16358                 });
16359             }
16360             
16361             html[html.length] = Roo.util.Format.trim(
16362                 this.dataName ?
16363                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16364                     t.apply(d)
16365             );
16366         }
16367         
16368         
16369         
16370         el.update(html.join(""));
16371         this.nodes = el.dom.childNodes;
16372         this.updateIndexes(0);
16373     },
16374     
16375
16376     /**
16377      * Function to override to reformat the data that is sent to
16378      * the template for each node.
16379      * DEPRICATED - use the preparedata event handler.
16380      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16381      * a JSON object for an UpdateManager bound view).
16382      */
16383     prepareData : function(data, index, record)
16384     {
16385         this.fireEvent("preparedata", this, data, index, record);
16386         return data;
16387     },
16388
16389     onUpdate : function(ds, record){
16390         // Roo.log('on update');   
16391         this.clearSelections();
16392         var index = this.store.indexOf(record);
16393         var n = this.nodes[index];
16394         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16395         n.parentNode.removeChild(n);
16396         this.updateIndexes(index, index);
16397     },
16398
16399     
16400     
16401 // --------- FIXME     
16402     onAdd : function(ds, records, index)
16403     {
16404         //Roo.log(['on Add', ds, records, index] );        
16405         this.clearSelections();
16406         if(this.nodes.length == 0){
16407             this.refresh();
16408             return;
16409         }
16410         var n = this.nodes[index];
16411         for(var i = 0, len = records.length; i < len; i++){
16412             var d = this.prepareData(records[i].data, i, records[i]);
16413             if(n){
16414                 this.tpl.insertBefore(n, d);
16415             }else{
16416                 
16417                 this.tpl.append(this.el, d);
16418             }
16419         }
16420         this.updateIndexes(index);
16421     },
16422
16423     onRemove : function(ds, record, index){
16424        // Roo.log('onRemove');
16425         this.clearSelections();
16426         var el = this.dataName  ?
16427             this.el.child('.roo-tpl-' + this.dataName) :
16428             this.el; 
16429         
16430         el.dom.removeChild(this.nodes[index]);
16431         this.updateIndexes(index);
16432     },
16433
16434     /**
16435      * Refresh an individual node.
16436      * @param {Number} index
16437      */
16438     refreshNode : function(index){
16439         this.onUpdate(this.store, this.store.getAt(index));
16440     },
16441
16442     updateIndexes : function(startIndex, endIndex){
16443         var ns = this.nodes;
16444         startIndex = startIndex || 0;
16445         endIndex = endIndex || ns.length - 1;
16446         for(var i = startIndex; i <= endIndex; i++){
16447             ns[i].nodeIndex = i;
16448         }
16449     },
16450
16451     /**
16452      * Changes the data store this view uses and refresh the view.
16453      * @param {Store} store
16454      */
16455     setStore : function(store, initial){
16456         if(!initial && this.store){
16457             this.store.un("datachanged", this.refresh);
16458             this.store.un("add", this.onAdd);
16459             this.store.un("remove", this.onRemove);
16460             this.store.un("update", this.onUpdate);
16461             this.store.un("clear", this.refresh);
16462             this.store.un("beforeload", this.onBeforeLoad);
16463             this.store.un("load", this.onLoad);
16464             this.store.un("loadexception", this.onLoad);
16465         }
16466         if(store){
16467           
16468             store.on("datachanged", this.refresh, this);
16469             store.on("add", this.onAdd, this);
16470             store.on("remove", this.onRemove, this);
16471             store.on("update", this.onUpdate, this);
16472             store.on("clear", this.refresh, this);
16473             store.on("beforeload", this.onBeforeLoad, this);
16474             store.on("load", this.onLoad, this);
16475             store.on("loadexception", this.onLoad, this);
16476         }
16477         
16478         if(store){
16479             this.refresh();
16480         }
16481     },
16482     /**
16483      * onbeforeLoad - masks the loading area.
16484      *
16485      */
16486     onBeforeLoad : function(store,opts)
16487     {
16488          //Roo.log('onBeforeLoad');   
16489         if (!opts.add) {
16490             this.el.update("");
16491         }
16492         this.el.mask(this.mask ? this.mask : "Loading" ); 
16493     },
16494     onLoad : function ()
16495     {
16496         this.el.unmask();
16497     },
16498     
16499
16500     /**
16501      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16502      * @param {HTMLElement} node
16503      * @return {HTMLElement} The template node
16504      */
16505     findItemFromChild : function(node){
16506         var el = this.dataName  ?
16507             this.el.child('.roo-tpl-' + this.dataName,true) :
16508             this.el.dom; 
16509         
16510         if(!node || node.parentNode == el){
16511                     return node;
16512             }
16513             var p = node.parentNode;
16514             while(p && p != el){
16515             if(p.parentNode == el){
16516                 return p;
16517             }
16518             p = p.parentNode;
16519         }
16520             return null;
16521     },
16522
16523     /** @ignore */
16524     onClick : function(e){
16525         var item = this.findItemFromChild(e.getTarget());
16526         if(item){
16527             var index = this.indexOf(item);
16528             if(this.onItemClick(item, index, e) !== false){
16529                 this.fireEvent("click", this, index, item, e);
16530             }
16531         }else{
16532             this.clearSelections();
16533         }
16534     },
16535
16536     /** @ignore */
16537     onContextMenu : function(e){
16538         var item = this.findItemFromChild(e.getTarget());
16539         if(item){
16540             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16541         }
16542     },
16543
16544     /** @ignore */
16545     onDblClick : function(e){
16546         var item = this.findItemFromChild(e.getTarget());
16547         if(item){
16548             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16549         }
16550     },
16551
16552     onItemClick : function(item, index, e)
16553     {
16554         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16555             return false;
16556         }
16557         if (this.toggleSelect) {
16558             var m = this.isSelected(item) ? 'unselect' : 'select';
16559             //Roo.log(m);
16560             var _t = this;
16561             _t[m](item, true, false);
16562             return true;
16563         }
16564         if(this.multiSelect || this.singleSelect){
16565             if(this.multiSelect && e.shiftKey && this.lastSelection){
16566                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16567             }else{
16568                 this.select(item, this.multiSelect && e.ctrlKey);
16569                 this.lastSelection = item;
16570             }
16571             
16572             if(!this.tickable){
16573                 e.preventDefault();
16574             }
16575             
16576         }
16577         return true;
16578     },
16579
16580     /**
16581      * Get the number of selected nodes.
16582      * @return {Number}
16583      */
16584     getSelectionCount : function(){
16585         return this.selections.length;
16586     },
16587
16588     /**
16589      * Get the currently selected nodes.
16590      * @return {Array} An array of HTMLElements
16591      */
16592     getSelectedNodes : function(){
16593         return this.selections;
16594     },
16595
16596     /**
16597      * Get the indexes of the selected nodes.
16598      * @return {Array}
16599      */
16600     getSelectedIndexes : function(){
16601         var indexes = [], s = this.selections;
16602         for(var i = 0, len = s.length; i < len; i++){
16603             indexes.push(s[i].nodeIndex);
16604         }
16605         return indexes;
16606     },
16607
16608     /**
16609      * Clear all selections
16610      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16611      */
16612     clearSelections : function(suppressEvent){
16613         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16614             this.cmp.elements = this.selections;
16615             this.cmp.removeClass(this.selectedClass);
16616             this.selections = [];
16617             if(!suppressEvent){
16618                 this.fireEvent("selectionchange", this, this.selections);
16619             }
16620         }
16621     },
16622
16623     /**
16624      * Returns true if the passed node is selected
16625      * @param {HTMLElement/Number} node The node or node index
16626      * @return {Boolean}
16627      */
16628     isSelected : function(node){
16629         var s = this.selections;
16630         if(s.length < 1){
16631             return false;
16632         }
16633         node = this.getNode(node);
16634         return s.indexOf(node) !== -1;
16635     },
16636
16637     /**
16638      * Selects nodes.
16639      * @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
16640      * @param {Boolean} keepExisting (optional) true to keep existing selections
16641      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16642      */
16643     select : function(nodeInfo, keepExisting, suppressEvent){
16644         if(nodeInfo instanceof Array){
16645             if(!keepExisting){
16646                 this.clearSelections(true);
16647             }
16648             for(var i = 0, len = nodeInfo.length; i < len; i++){
16649                 this.select(nodeInfo[i], true, true);
16650             }
16651             return;
16652         } 
16653         var node = this.getNode(nodeInfo);
16654         if(!node || this.isSelected(node)){
16655             return; // already selected.
16656         }
16657         if(!keepExisting){
16658             this.clearSelections(true);
16659         }
16660         
16661         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16662             Roo.fly(node).addClass(this.selectedClass);
16663             this.selections.push(node);
16664             if(!suppressEvent){
16665                 this.fireEvent("selectionchange", this, this.selections);
16666             }
16667         }
16668         
16669         
16670     },
16671       /**
16672      * Unselects nodes.
16673      * @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
16674      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16675      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16676      */
16677     unselect : function(nodeInfo, keepExisting, suppressEvent)
16678     {
16679         if(nodeInfo instanceof Array){
16680             Roo.each(this.selections, function(s) {
16681                 this.unselect(s, nodeInfo);
16682             }, this);
16683             return;
16684         }
16685         var node = this.getNode(nodeInfo);
16686         if(!node || !this.isSelected(node)){
16687             //Roo.log("not selected");
16688             return; // not selected.
16689         }
16690         // fireevent???
16691         var ns = [];
16692         Roo.each(this.selections, function(s) {
16693             if (s == node ) {
16694                 Roo.fly(node).removeClass(this.selectedClass);
16695
16696                 return;
16697             }
16698             ns.push(s);
16699         },this);
16700         
16701         this.selections= ns;
16702         this.fireEvent("selectionchange", this, this.selections);
16703     },
16704
16705     /**
16706      * Gets a template node.
16707      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16708      * @return {HTMLElement} The node or null if it wasn't found
16709      */
16710     getNode : function(nodeInfo){
16711         if(typeof nodeInfo == "string"){
16712             return document.getElementById(nodeInfo);
16713         }else if(typeof nodeInfo == "number"){
16714             return this.nodes[nodeInfo];
16715         }
16716         return nodeInfo;
16717     },
16718
16719     /**
16720      * Gets a range template nodes.
16721      * @param {Number} startIndex
16722      * @param {Number} endIndex
16723      * @return {Array} An array of nodes
16724      */
16725     getNodes : function(start, end){
16726         var ns = this.nodes;
16727         start = start || 0;
16728         end = typeof end == "undefined" ? ns.length - 1 : end;
16729         var nodes = [];
16730         if(start <= end){
16731             for(var i = start; i <= end; i++){
16732                 nodes.push(ns[i]);
16733             }
16734         } else{
16735             for(var i = start; i >= end; i--){
16736                 nodes.push(ns[i]);
16737             }
16738         }
16739         return nodes;
16740     },
16741
16742     /**
16743      * Finds the index of the passed node
16744      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16745      * @return {Number} The index of the node or -1
16746      */
16747     indexOf : function(node){
16748         node = this.getNode(node);
16749         if(typeof node.nodeIndex == "number"){
16750             return node.nodeIndex;
16751         }
16752         var ns = this.nodes;
16753         for(var i = 0, len = ns.length; i < len; i++){
16754             if(ns[i] == node){
16755                 return i;
16756             }
16757         }
16758         return -1;
16759     }
16760 });
16761 /*
16762  * - LGPL
16763  *
16764  * based on jquery fullcalendar
16765  * 
16766  */
16767
16768 Roo.bootstrap = Roo.bootstrap || {};
16769 /**
16770  * @class Roo.bootstrap.Calendar
16771  * @extends Roo.bootstrap.Component
16772  * Bootstrap Calendar class
16773  * @cfg {Boolean} loadMask (true|false) default false
16774  * @cfg {Object} header generate the user specific header of the calendar, default false
16775
16776  * @constructor
16777  * Create a new Container
16778  * @param {Object} config The config object
16779  */
16780
16781
16782
16783 Roo.bootstrap.Calendar = function(config){
16784     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16785      this.addEvents({
16786         /**
16787              * @event select
16788              * Fires when a date is selected
16789              * @param {DatePicker} this
16790              * @param {Date} date The selected date
16791              */
16792         'select': true,
16793         /**
16794              * @event monthchange
16795              * Fires when the displayed month changes 
16796              * @param {DatePicker} this
16797              * @param {Date} date The selected month
16798              */
16799         'monthchange': true,
16800         /**
16801              * @event evententer
16802              * Fires when mouse over an event
16803              * @param {Calendar} this
16804              * @param {event} Event
16805              */
16806         'evententer': true,
16807         /**
16808              * @event eventleave
16809              * Fires when the mouse leaves an
16810              * @param {Calendar} this
16811              * @param {event}
16812              */
16813         'eventleave': true,
16814         /**
16815              * @event eventclick
16816              * Fires when the mouse click an
16817              * @param {Calendar} this
16818              * @param {event}
16819              */
16820         'eventclick': true
16821         
16822     });
16823
16824 };
16825
16826 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16827     
16828      /**
16829      * @cfg {Number} startDay
16830      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16831      */
16832     startDay : 0,
16833     
16834     loadMask : false,
16835     
16836     header : false,
16837       
16838     getAutoCreate : function(){
16839         
16840         
16841         var fc_button = function(name, corner, style, content ) {
16842             return Roo.apply({},{
16843                 tag : 'span',
16844                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16845                          (corner.length ?
16846                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16847                             ''
16848                         ),
16849                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16850                 unselectable: 'on'
16851             });
16852         };
16853         
16854         var header = {};
16855         
16856         if(!this.header){
16857             header = {
16858                 tag : 'table',
16859                 cls : 'fc-header',
16860                 style : 'width:100%',
16861                 cn : [
16862                     {
16863                         tag: 'tr',
16864                         cn : [
16865                             {
16866                                 tag : 'td',
16867                                 cls : 'fc-header-left',
16868                                 cn : [
16869                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16870                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16871                                     { tag: 'span', cls: 'fc-header-space' },
16872                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16873
16874
16875                                 ]
16876                             },
16877
16878                             {
16879                                 tag : 'td',
16880                                 cls : 'fc-header-center',
16881                                 cn : [
16882                                     {
16883                                         tag: 'span',
16884                                         cls: 'fc-header-title',
16885                                         cn : {
16886                                             tag: 'H2',
16887                                             html : 'month / year'
16888                                         }
16889                                     }
16890
16891                                 ]
16892                             },
16893                             {
16894                                 tag : 'td',
16895                                 cls : 'fc-header-right',
16896                                 cn : [
16897                               /*      fc_button('month', 'left', '', 'month' ),
16898                                     fc_button('week', '', '', 'week' ),
16899                                     fc_button('day', 'right', '', 'day' )
16900                                 */    
16901
16902                                 ]
16903                             }
16904
16905                         ]
16906                     }
16907                 ]
16908             };
16909         }
16910         
16911         header = this.header;
16912         
16913        
16914         var cal_heads = function() {
16915             var ret = [];
16916             // fixme - handle this.
16917             
16918             for (var i =0; i < Date.dayNames.length; i++) {
16919                 var d = Date.dayNames[i];
16920                 ret.push({
16921                     tag: 'th',
16922                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16923                     html : d.substring(0,3)
16924                 });
16925                 
16926             }
16927             ret[0].cls += ' fc-first';
16928             ret[6].cls += ' fc-last';
16929             return ret;
16930         };
16931         var cal_cell = function(n) {
16932             return  {
16933                 tag: 'td',
16934                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16935                 cn : [
16936                     {
16937                         cn : [
16938                             {
16939                                 cls: 'fc-day-number',
16940                                 html: 'D'
16941                             },
16942                             {
16943                                 cls: 'fc-day-content',
16944                              
16945                                 cn : [
16946                                      {
16947                                         style: 'position: relative;' // height: 17px;
16948                                     }
16949                                 ]
16950                             }
16951                             
16952                             
16953                         ]
16954                     }
16955                 ]
16956                 
16957             }
16958         };
16959         var cal_rows = function() {
16960             
16961             var ret = [];
16962             for (var r = 0; r < 6; r++) {
16963                 var row= {
16964                     tag : 'tr',
16965                     cls : 'fc-week',
16966                     cn : []
16967                 };
16968                 
16969                 for (var i =0; i < Date.dayNames.length; i++) {
16970                     var d = Date.dayNames[i];
16971                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16972
16973                 }
16974                 row.cn[0].cls+=' fc-first';
16975                 row.cn[0].cn[0].style = 'min-height:90px';
16976                 row.cn[6].cls+=' fc-last';
16977                 ret.push(row);
16978                 
16979             }
16980             ret[0].cls += ' fc-first';
16981             ret[4].cls += ' fc-prev-last';
16982             ret[5].cls += ' fc-last';
16983             return ret;
16984             
16985         };
16986         
16987         var cal_table = {
16988             tag: 'table',
16989             cls: 'fc-border-separate',
16990             style : 'width:100%',
16991             cellspacing  : 0,
16992             cn : [
16993                 { 
16994                     tag: 'thead',
16995                     cn : [
16996                         { 
16997                             tag: 'tr',
16998                             cls : 'fc-first fc-last',
16999                             cn : cal_heads()
17000                         }
17001                     ]
17002                 },
17003                 { 
17004                     tag: 'tbody',
17005                     cn : cal_rows()
17006                 }
17007                   
17008             ]
17009         };
17010          
17011          var cfg = {
17012             cls : 'fc fc-ltr',
17013             cn : [
17014                 header,
17015                 {
17016                     cls : 'fc-content',
17017                     style : "position: relative;",
17018                     cn : [
17019                         {
17020                             cls : 'fc-view fc-view-month fc-grid',
17021                             style : 'position: relative',
17022                             unselectable : 'on',
17023                             cn : [
17024                                 {
17025                                     cls : 'fc-event-container',
17026                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17027                                 },
17028                                 cal_table
17029                             ]
17030                         }
17031                     ]
17032     
17033                 }
17034            ] 
17035             
17036         };
17037         
17038          
17039         
17040         return cfg;
17041     },
17042     
17043     
17044     initEvents : function()
17045     {
17046         if(!this.store){
17047             throw "can not find store for calendar";
17048         }
17049         
17050         var mark = {
17051             tag: "div",
17052             cls:"x-dlg-mask",
17053             style: "text-align:center",
17054             cn: [
17055                 {
17056                     tag: "div",
17057                     style: "background-color:white;width:50%;margin:250 auto",
17058                     cn: [
17059                         {
17060                             tag: "img",
17061                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17062                         },
17063                         {
17064                             tag: "span",
17065                             html: "Loading"
17066                         }
17067                         
17068                     ]
17069                 }
17070             ]
17071         };
17072         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17073         
17074         var size = this.el.select('.fc-content', true).first().getSize();
17075         this.maskEl.setSize(size.width, size.height);
17076         this.maskEl.enableDisplayMode("block");
17077         if(!this.loadMask){
17078             this.maskEl.hide();
17079         }
17080         
17081         this.store = Roo.factory(this.store, Roo.data);
17082         this.store.on('load', this.onLoad, this);
17083         this.store.on('beforeload', this.onBeforeLoad, this);
17084         
17085         this.resize();
17086         
17087         this.cells = this.el.select('.fc-day',true);
17088         //Roo.log(this.cells);
17089         this.textNodes = this.el.query('.fc-day-number');
17090         this.cells.addClassOnOver('fc-state-hover');
17091         
17092         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17093         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17094         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17095         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17096         
17097         this.on('monthchange', this.onMonthChange, this);
17098         
17099         this.update(new Date().clearTime());
17100     },
17101     
17102     resize : function() {
17103         var sz  = this.el.getSize();
17104         
17105         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17106         this.el.select('.fc-day-content div',true).setHeight(34);
17107     },
17108     
17109     
17110     // private
17111     showPrevMonth : function(e){
17112         this.update(this.activeDate.add("mo", -1));
17113     },
17114     showToday : function(e){
17115         this.update(new Date().clearTime());
17116     },
17117     // private
17118     showNextMonth : function(e){
17119         this.update(this.activeDate.add("mo", 1));
17120     },
17121
17122     // private
17123     showPrevYear : function(){
17124         this.update(this.activeDate.add("y", -1));
17125     },
17126
17127     // private
17128     showNextYear : function(){
17129         this.update(this.activeDate.add("y", 1));
17130     },
17131
17132     
17133    // private
17134     update : function(date)
17135     {
17136         var vd = this.activeDate;
17137         this.activeDate = date;
17138 //        if(vd && this.el){
17139 //            var t = date.getTime();
17140 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17141 //                Roo.log('using add remove');
17142 //                
17143 //                this.fireEvent('monthchange', this, date);
17144 //                
17145 //                this.cells.removeClass("fc-state-highlight");
17146 //                this.cells.each(function(c){
17147 //                   if(c.dateValue == t){
17148 //                       c.addClass("fc-state-highlight");
17149 //                       setTimeout(function(){
17150 //                            try{c.dom.firstChild.focus();}catch(e){}
17151 //                       }, 50);
17152 //                       return false;
17153 //                   }
17154 //                   return true;
17155 //                });
17156 //                return;
17157 //            }
17158 //        }
17159         
17160         var days = date.getDaysInMonth();
17161         
17162         var firstOfMonth = date.getFirstDateOfMonth();
17163         var startingPos = firstOfMonth.getDay()-this.startDay;
17164         
17165         if(startingPos < this.startDay){
17166             startingPos += 7;
17167         }
17168         
17169         var pm = date.add(Date.MONTH, -1);
17170         var prevStart = pm.getDaysInMonth()-startingPos;
17171 //        
17172         this.cells = this.el.select('.fc-day',true);
17173         this.textNodes = this.el.query('.fc-day-number');
17174         this.cells.addClassOnOver('fc-state-hover');
17175         
17176         var cells = this.cells.elements;
17177         var textEls = this.textNodes;
17178         
17179         Roo.each(cells, function(cell){
17180             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17181         });
17182         
17183         days += startingPos;
17184
17185         // convert everything to numbers so it's fast
17186         var day = 86400000;
17187         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17188         //Roo.log(d);
17189         //Roo.log(pm);
17190         //Roo.log(prevStart);
17191         
17192         var today = new Date().clearTime().getTime();
17193         var sel = date.clearTime().getTime();
17194         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17195         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17196         var ddMatch = this.disabledDatesRE;
17197         var ddText = this.disabledDatesText;
17198         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17199         var ddaysText = this.disabledDaysText;
17200         var format = this.format;
17201         
17202         var setCellClass = function(cal, cell){
17203             cell.row = 0;
17204             cell.events = [];
17205             cell.more = [];
17206             //Roo.log('set Cell Class');
17207             cell.title = "";
17208             var t = d.getTime();
17209             
17210             //Roo.log(d);
17211             
17212             cell.dateValue = t;
17213             if(t == today){
17214                 cell.className += " fc-today";
17215                 cell.className += " fc-state-highlight";
17216                 cell.title = cal.todayText;
17217             }
17218             if(t == sel){
17219                 // disable highlight in other month..
17220                 //cell.className += " fc-state-highlight";
17221                 
17222             }
17223             // disabling
17224             if(t < min) {
17225                 cell.className = " fc-state-disabled";
17226                 cell.title = cal.minText;
17227                 return;
17228             }
17229             if(t > max) {
17230                 cell.className = " fc-state-disabled";
17231                 cell.title = cal.maxText;
17232                 return;
17233             }
17234             if(ddays){
17235                 if(ddays.indexOf(d.getDay()) != -1){
17236                     cell.title = ddaysText;
17237                     cell.className = " fc-state-disabled";
17238                 }
17239             }
17240             if(ddMatch && format){
17241                 var fvalue = d.dateFormat(format);
17242                 if(ddMatch.test(fvalue)){
17243                     cell.title = ddText.replace("%0", fvalue);
17244                     cell.className = " fc-state-disabled";
17245                 }
17246             }
17247             
17248             if (!cell.initialClassName) {
17249                 cell.initialClassName = cell.dom.className;
17250             }
17251             
17252             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17253         };
17254
17255         var i = 0;
17256         
17257         for(; i < startingPos; i++) {
17258             textEls[i].innerHTML = (++prevStart);
17259             d.setDate(d.getDate()+1);
17260             
17261             cells[i].className = "fc-past fc-other-month";
17262             setCellClass(this, cells[i]);
17263         }
17264         
17265         var intDay = 0;
17266         
17267         for(; i < days; i++){
17268             intDay = i - startingPos + 1;
17269             textEls[i].innerHTML = (intDay);
17270             d.setDate(d.getDate()+1);
17271             
17272             cells[i].className = ''; // "x-date-active";
17273             setCellClass(this, cells[i]);
17274         }
17275         var extraDays = 0;
17276         
17277         for(; i < 42; i++) {
17278             textEls[i].innerHTML = (++extraDays);
17279             d.setDate(d.getDate()+1);
17280             
17281             cells[i].className = "fc-future fc-other-month";
17282             setCellClass(this, cells[i]);
17283         }
17284         
17285         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17286         
17287         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17288         
17289         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17290         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17291         
17292         if(totalRows != 6){
17293             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17294             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17295         }
17296         
17297         this.fireEvent('monthchange', this, date);
17298         
17299         
17300         /*
17301         if(!this.internalRender){
17302             var main = this.el.dom.firstChild;
17303             var w = main.offsetWidth;
17304             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17305             Roo.fly(main).setWidth(w);
17306             this.internalRender = true;
17307             // opera does not respect the auto grow header center column
17308             // then, after it gets a width opera refuses to recalculate
17309             // without a second pass
17310             if(Roo.isOpera && !this.secondPass){
17311                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17312                 this.secondPass = true;
17313                 this.update.defer(10, this, [date]);
17314             }
17315         }
17316         */
17317         
17318     },
17319     
17320     findCell : function(dt) {
17321         dt = dt.clearTime().getTime();
17322         var ret = false;
17323         this.cells.each(function(c){
17324             //Roo.log("check " +c.dateValue + '?=' + dt);
17325             if(c.dateValue == dt){
17326                 ret = c;
17327                 return false;
17328             }
17329             return true;
17330         });
17331         
17332         return ret;
17333     },
17334     
17335     findCells : function(ev) {
17336         var s = ev.start.clone().clearTime().getTime();
17337        // Roo.log(s);
17338         var e= ev.end.clone().clearTime().getTime();
17339        // Roo.log(e);
17340         var ret = [];
17341         this.cells.each(function(c){
17342              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17343             
17344             if(c.dateValue > e){
17345                 return ;
17346             }
17347             if(c.dateValue < s){
17348                 return ;
17349             }
17350             ret.push(c);
17351         });
17352         
17353         return ret;    
17354     },
17355     
17356 //    findBestRow: function(cells)
17357 //    {
17358 //        var ret = 0;
17359 //        
17360 //        for (var i =0 ; i < cells.length;i++) {
17361 //            ret  = Math.max(cells[i].rows || 0,ret);
17362 //        }
17363 //        return ret;
17364 //        
17365 //    },
17366     
17367     
17368     addItem : function(ev)
17369     {
17370         // look for vertical location slot in
17371         var cells = this.findCells(ev);
17372         
17373 //        ev.row = this.findBestRow(cells);
17374         
17375         // work out the location.
17376         
17377         var crow = false;
17378         var rows = [];
17379         for(var i =0; i < cells.length; i++) {
17380             
17381             cells[i].row = cells[0].row;
17382             
17383             if(i == 0){
17384                 cells[i].row = cells[i].row + 1;
17385             }
17386             
17387             if (!crow) {
17388                 crow = {
17389                     start : cells[i],
17390                     end :  cells[i]
17391                 };
17392                 continue;
17393             }
17394             if (crow.start.getY() == cells[i].getY()) {
17395                 // on same row.
17396                 crow.end = cells[i];
17397                 continue;
17398             }
17399             // different row.
17400             rows.push(crow);
17401             crow = {
17402                 start: cells[i],
17403                 end : cells[i]
17404             };
17405             
17406         }
17407         
17408         rows.push(crow);
17409         ev.els = [];
17410         ev.rows = rows;
17411         ev.cells = cells;
17412         
17413         cells[0].events.push(ev);
17414         
17415         this.calevents.push(ev);
17416     },
17417     
17418     clearEvents: function() {
17419         
17420         if(!this.calevents){
17421             return;
17422         }
17423         
17424         Roo.each(this.cells.elements, function(c){
17425             c.row = 0;
17426             c.events = [];
17427             c.more = [];
17428         });
17429         
17430         Roo.each(this.calevents, function(e) {
17431             Roo.each(e.els, function(el) {
17432                 el.un('mouseenter' ,this.onEventEnter, this);
17433                 el.un('mouseleave' ,this.onEventLeave, this);
17434                 el.remove();
17435             },this);
17436         },this);
17437         
17438         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17439             e.remove();
17440         });
17441         
17442     },
17443     
17444     renderEvents: function()
17445     {   
17446         var _this = this;
17447         
17448         this.cells.each(function(c) {
17449             
17450             if(c.row < 5){
17451                 return;
17452             }
17453             
17454             var ev = c.events;
17455             
17456             var r = 4;
17457             if(c.row != c.events.length){
17458                 r = 4 - (4 - (c.row - c.events.length));
17459             }
17460             
17461             c.events = ev.slice(0, r);
17462             c.more = ev.slice(r);
17463             
17464             if(c.more.length && c.more.length == 1){
17465                 c.events.push(c.more.pop());
17466             }
17467             
17468             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17469             
17470         });
17471             
17472         this.cells.each(function(c) {
17473             
17474             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17475             
17476             
17477             for (var e = 0; e < c.events.length; e++){
17478                 var ev = c.events[e];
17479                 var rows = ev.rows;
17480                 
17481                 for(var i = 0; i < rows.length; i++) {
17482                 
17483                     // how many rows should it span..
17484
17485                     var  cfg = {
17486                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17487                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17488
17489                         unselectable : "on",
17490                         cn : [
17491                             {
17492                                 cls: 'fc-event-inner',
17493                                 cn : [
17494     //                                {
17495     //                                  tag:'span',
17496     //                                  cls: 'fc-event-time',
17497     //                                  html : cells.length > 1 ? '' : ev.time
17498     //                                },
17499                                     {
17500                                       tag:'span',
17501                                       cls: 'fc-event-title',
17502                                       html : String.format('{0}', ev.title)
17503                                     }
17504
17505
17506                                 ]
17507                             },
17508                             {
17509                                 cls: 'ui-resizable-handle ui-resizable-e',
17510                                 html : '&nbsp;&nbsp;&nbsp'
17511                             }
17512
17513                         ]
17514                     };
17515
17516                     if (i == 0) {
17517                         cfg.cls += ' fc-event-start';
17518                     }
17519                     if ((i+1) == rows.length) {
17520                         cfg.cls += ' fc-event-end';
17521                     }
17522
17523                     var ctr = _this.el.select('.fc-event-container',true).first();
17524                     var cg = ctr.createChild(cfg);
17525
17526                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17527                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17528
17529                     var r = (c.more.length) ? 1 : 0;
17530                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17531                     cg.setWidth(ebox.right - sbox.x -2);
17532
17533                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17534                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17535                     cg.on('click', _this.onEventClick, _this, ev);
17536
17537                     ev.els.push(cg);
17538                     
17539                 }
17540                 
17541             }
17542             
17543             
17544             if(c.more.length){
17545                 var  cfg = {
17546                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17547                     style : 'position: absolute',
17548                     unselectable : "on",
17549                     cn : [
17550                         {
17551                             cls: 'fc-event-inner',
17552                             cn : [
17553                                 {
17554                                   tag:'span',
17555                                   cls: 'fc-event-title',
17556                                   html : 'More'
17557                                 }
17558
17559
17560                             ]
17561                         },
17562                         {
17563                             cls: 'ui-resizable-handle ui-resizable-e',
17564                             html : '&nbsp;&nbsp;&nbsp'
17565                         }
17566
17567                     ]
17568                 };
17569
17570                 var ctr = _this.el.select('.fc-event-container',true).first();
17571                 var cg = ctr.createChild(cfg);
17572
17573                 var sbox = c.select('.fc-day-content',true).first().getBox();
17574                 var ebox = c.select('.fc-day-content',true).first().getBox();
17575                 //Roo.log(cg);
17576                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17577                 cg.setWidth(ebox.right - sbox.x -2);
17578
17579                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17580                 
17581             }
17582             
17583         });
17584         
17585         
17586         
17587     },
17588     
17589     onEventEnter: function (e, el,event,d) {
17590         this.fireEvent('evententer', this, el, event);
17591     },
17592     
17593     onEventLeave: function (e, el,event,d) {
17594         this.fireEvent('eventleave', this, el, event);
17595     },
17596     
17597     onEventClick: function (e, el,event,d) {
17598         this.fireEvent('eventclick', this, el, event);
17599     },
17600     
17601     onMonthChange: function () {
17602         this.store.load();
17603     },
17604     
17605     onMoreEventClick: function(e, el, more)
17606     {
17607         var _this = this;
17608         
17609         this.calpopover.placement = 'right';
17610         this.calpopover.setTitle('More');
17611         
17612         this.calpopover.setContent('');
17613         
17614         var ctr = this.calpopover.el.select('.popover-content', true).first();
17615         
17616         Roo.each(more, function(m){
17617             var cfg = {
17618                 cls : 'fc-event-hori fc-event-draggable',
17619                 html : m.title
17620             };
17621             var cg = ctr.createChild(cfg);
17622             
17623             cg.on('click', _this.onEventClick, _this, m);
17624         });
17625         
17626         this.calpopover.show(el);
17627         
17628         
17629     },
17630     
17631     onLoad: function () 
17632     {   
17633         this.calevents = [];
17634         var cal = this;
17635         
17636         if(this.store.getCount() > 0){
17637             this.store.data.each(function(d){
17638                cal.addItem({
17639                     id : d.data.id,
17640                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17641                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17642                     time : d.data.start_time,
17643                     title : d.data.title,
17644                     description : d.data.description,
17645                     venue : d.data.venue
17646                 });
17647             });
17648         }
17649         
17650         this.renderEvents();
17651         
17652         if(this.calevents.length && this.loadMask){
17653             this.maskEl.hide();
17654         }
17655     },
17656     
17657     onBeforeLoad: function()
17658     {
17659         this.clearEvents();
17660         if(this.loadMask){
17661             this.maskEl.show();
17662         }
17663     }
17664 });
17665
17666  
17667  /*
17668  * - LGPL
17669  *
17670  * element
17671  * 
17672  */
17673
17674 /**
17675  * @class Roo.bootstrap.Popover
17676  * @extends Roo.bootstrap.Component
17677  * Bootstrap Popover class
17678  * @cfg {String} html contents of the popover   (or false to use children..)
17679  * @cfg {String} title of popover (or false to hide)
17680  * @cfg {String} placement how it is placed
17681  * @cfg {String} trigger click || hover (or false to trigger manually)
17682  * @cfg {String} over what (parent or false to trigger manually.)
17683  * @cfg {Number} delay - delay before showing
17684  
17685  * @constructor
17686  * Create a new Popover
17687  * @param {Object} config The config object
17688  */
17689
17690 Roo.bootstrap.Popover = function(config){
17691     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17692     
17693     this.addEvents({
17694         // raw events
17695          /**
17696          * @event show
17697          * After the popover show
17698          * 
17699          * @param {Roo.bootstrap.Popover} this
17700          */
17701         "show" : true,
17702         /**
17703          * @event hide
17704          * After the popover hide
17705          * 
17706          * @param {Roo.bootstrap.Popover} this
17707          */
17708         "hide" : true
17709     });
17710 };
17711
17712 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17713     
17714     title: 'Fill in a title',
17715     html: false,
17716     
17717     placement : 'right',
17718     trigger : 'hover', // hover
17719     
17720     delay : 0,
17721     
17722     over: 'parent',
17723     
17724     can_build_overlaid : false,
17725     
17726     getChildContainer : function()
17727     {
17728         return this.el.select('.popover-content',true).first();
17729     },
17730     
17731     getAutoCreate : function(){
17732          
17733         var cfg = {
17734            cls : 'popover roo-dynamic',
17735            style: 'display:block',
17736            cn : [
17737                 {
17738                     cls : 'arrow'
17739                 },
17740                 {
17741                     cls : 'popover-inner',
17742                     cn : [
17743                         {
17744                             tag: 'h3',
17745                             cls: 'popover-title popover-header',
17746                             html : this.title
17747                         },
17748                         {
17749                             cls : 'popover-content popover-body',
17750                             html : this.html
17751                         }
17752                     ]
17753                     
17754                 }
17755            ]
17756         };
17757         
17758         return cfg;
17759     },
17760     setTitle: function(str)
17761     {
17762         this.title = str;
17763         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17764     },
17765     setContent: function(str)
17766     {
17767         this.html = str;
17768         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17769     },
17770     // as it get's added to the bottom of the page.
17771     onRender : function(ct, position)
17772     {
17773         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17774         if(!this.el){
17775             var cfg = Roo.apply({},  this.getAutoCreate());
17776             cfg.id = Roo.id();
17777             
17778             if (this.cls) {
17779                 cfg.cls += ' ' + this.cls;
17780             }
17781             if (this.style) {
17782                 cfg.style = this.style;
17783             }
17784             //Roo.log("adding to ");
17785             this.el = Roo.get(document.body).createChild(cfg, position);
17786 //            Roo.log(this.el);
17787         }
17788         this.initEvents();
17789     },
17790     
17791     initEvents : function()
17792     {
17793         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17794         this.el.enableDisplayMode('block');
17795         this.el.hide();
17796         if (this.over === false) {
17797             return; 
17798         }
17799         if (this.triggers === false) {
17800             return;
17801         }
17802         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17803         var triggers = this.trigger ? this.trigger.split(' ') : [];
17804         Roo.each(triggers, function(trigger) {
17805         
17806             if (trigger == 'click') {
17807                 on_el.on('click', this.toggle, this);
17808             } else if (trigger != 'manual') {
17809                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17810                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17811       
17812                 on_el.on(eventIn  ,this.enter, this);
17813                 on_el.on(eventOut, this.leave, this);
17814             }
17815         }, this);
17816         
17817     },
17818     
17819     
17820     // private
17821     timeout : null,
17822     hoverState : null,
17823     
17824     toggle : function () {
17825         this.hoverState == 'in' ? this.leave() : this.enter();
17826     },
17827     
17828     enter : function () {
17829         
17830         clearTimeout(this.timeout);
17831     
17832         this.hoverState = 'in';
17833     
17834         if (!this.delay || !this.delay.show) {
17835             this.show();
17836             return;
17837         }
17838         var _t = this;
17839         this.timeout = setTimeout(function () {
17840             if (_t.hoverState == 'in') {
17841                 _t.show();
17842             }
17843         }, this.delay.show)
17844     },
17845     
17846     leave : function() {
17847         clearTimeout(this.timeout);
17848     
17849         this.hoverState = 'out';
17850     
17851         if (!this.delay || !this.delay.hide) {
17852             this.hide();
17853             return;
17854         }
17855         var _t = this;
17856         this.timeout = setTimeout(function () {
17857             if (_t.hoverState == 'out') {
17858                 _t.hide();
17859             }
17860         }, this.delay.hide)
17861     },
17862     
17863     show : function (on_el)
17864     {
17865         if (!on_el) {
17866             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17867         }
17868         
17869         // set content.
17870         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17871         if (this.html !== false) {
17872             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17873         }
17874         this.el.removeClass([
17875             'fade','top','bottom', 'left', 'right','in',
17876             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17877         ]);
17878         if (!this.title.length) {
17879             this.el.select('.popover-title',true).hide();
17880         }
17881         
17882         var placement = typeof this.placement == 'function' ?
17883             this.placement.call(this, this.el, on_el) :
17884             this.placement;
17885             
17886         var autoToken = /\s?auto?\s?/i;
17887         var autoPlace = autoToken.test(placement);
17888         if (autoPlace) {
17889             placement = placement.replace(autoToken, '') || 'top';
17890         }
17891         
17892         //this.el.detach()
17893         //this.el.setXY([0,0]);
17894         this.el.show();
17895         this.el.dom.style.display='block';
17896         this.el.addClass(placement);
17897         
17898         //this.el.appendTo(on_el);
17899         
17900         var p = this.getPosition();
17901         var box = this.el.getBox();
17902         
17903         if (autoPlace) {
17904             // fixme..
17905         }
17906         var align = Roo.bootstrap.Popover.alignment[placement];
17907         
17908 //        Roo.log(align);
17909         this.el.alignTo(on_el, align[0],align[1]);
17910         //var arrow = this.el.select('.arrow',true).first();
17911         //arrow.set(align[2], 
17912         
17913         this.el.addClass('in');
17914         
17915         
17916         if (this.el.hasClass('fade')) {
17917             // fade it?
17918         }
17919         
17920         this.hoverState = 'in';
17921         
17922         this.fireEvent('show', this);
17923         
17924     },
17925     hide : function()
17926     {
17927         this.el.setXY([0,0]);
17928         this.el.removeClass('in');
17929         this.el.hide();
17930         this.hoverState = null;
17931         
17932         this.fireEvent('hide', this);
17933     }
17934     
17935 });
17936
17937 Roo.bootstrap.Popover.alignment = {
17938     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17939     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17940     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17941     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17942 };
17943
17944  /*
17945  * - LGPL
17946  *
17947  * Progress
17948  * 
17949  */
17950
17951 /**
17952  * @class Roo.bootstrap.Progress
17953  * @extends Roo.bootstrap.Component
17954  * Bootstrap Progress class
17955  * @cfg {Boolean} striped striped of the progress bar
17956  * @cfg {Boolean} active animated of the progress bar
17957  * 
17958  * 
17959  * @constructor
17960  * Create a new Progress
17961  * @param {Object} config The config object
17962  */
17963
17964 Roo.bootstrap.Progress = function(config){
17965     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17966 };
17967
17968 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17969     
17970     striped : false,
17971     active: false,
17972     
17973     getAutoCreate : function(){
17974         var cfg = {
17975             tag: 'div',
17976             cls: 'progress'
17977         };
17978         
17979         
17980         if(this.striped){
17981             cfg.cls += ' progress-striped';
17982         }
17983       
17984         if(this.active){
17985             cfg.cls += ' active';
17986         }
17987         
17988         
17989         return cfg;
17990     }
17991    
17992 });
17993
17994  
17995
17996  /*
17997  * - LGPL
17998  *
17999  * ProgressBar
18000  * 
18001  */
18002
18003 /**
18004  * @class Roo.bootstrap.ProgressBar
18005  * @extends Roo.bootstrap.Component
18006  * Bootstrap ProgressBar class
18007  * @cfg {Number} aria_valuenow aria-value now
18008  * @cfg {Number} aria_valuemin aria-value min
18009  * @cfg {Number} aria_valuemax aria-value max
18010  * @cfg {String} label label for the progress bar
18011  * @cfg {String} panel (success | info | warning | danger )
18012  * @cfg {String} role role of the progress bar
18013  * @cfg {String} sr_only text
18014  * 
18015  * 
18016  * @constructor
18017  * Create a new ProgressBar
18018  * @param {Object} config The config object
18019  */
18020
18021 Roo.bootstrap.ProgressBar = function(config){
18022     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18023 };
18024
18025 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18026     
18027     aria_valuenow : 0,
18028     aria_valuemin : 0,
18029     aria_valuemax : 100,
18030     label : false,
18031     panel : false,
18032     role : false,
18033     sr_only: false,
18034     
18035     getAutoCreate : function()
18036     {
18037         
18038         var cfg = {
18039             tag: 'div',
18040             cls: 'progress-bar',
18041             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18042         };
18043         
18044         if(this.sr_only){
18045             cfg.cn = {
18046                 tag: 'span',
18047                 cls: 'sr-only',
18048                 html: this.sr_only
18049             }
18050         }
18051         
18052         if(this.role){
18053             cfg.role = this.role;
18054         }
18055         
18056         if(this.aria_valuenow){
18057             cfg['aria-valuenow'] = this.aria_valuenow;
18058         }
18059         
18060         if(this.aria_valuemin){
18061             cfg['aria-valuemin'] = this.aria_valuemin;
18062         }
18063         
18064         if(this.aria_valuemax){
18065             cfg['aria-valuemax'] = this.aria_valuemax;
18066         }
18067         
18068         if(this.label && !this.sr_only){
18069             cfg.html = this.label;
18070         }
18071         
18072         if(this.panel){
18073             cfg.cls += ' progress-bar-' + this.panel;
18074         }
18075         
18076         return cfg;
18077     },
18078     
18079     update : function(aria_valuenow)
18080     {
18081         this.aria_valuenow = aria_valuenow;
18082         
18083         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18084     }
18085    
18086 });
18087
18088  
18089
18090  /*
18091  * - LGPL
18092  *
18093  * column
18094  * 
18095  */
18096
18097 /**
18098  * @class Roo.bootstrap.TabGroup
18099  * @extends Roo.bootstrap.Column
18100  * Bootstrap Column class
18101  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18102  * @cfg {Boolean} carousel true to make the group behave like a carousel
18103  * @cfg {Boolean} bullets show bullets for the panels
18104  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18105  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18106  * @cfg {Boolean} showarrow (true|false) show arrow default true
18107  * 
18108  * @constructor
18109  * Create a new TabGroup
18110  * @param {Object} config The config object
18111  */
18112
18113 Roo.bootstrap.TabGroup = function(config){
18114     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18115     if (!this.navId) {
18116         this.navId = Roo.id();
18117     }
18118     this.tabs = [];
18119     Roo.bootstrap.TabGroup.register(this);
18120     
18121 };
18122
18123 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18124     
18125     carousel : false,
18126     transition : false,
18127     bullets : 0,
18128     timer : 0,
18129     autoslide : false,
18130     slideFn : false,
18131     slideOnTouch : false,
18132     showarrow : true,
18133     
18134     getAutoCreate : function()
18135     {
18136         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18137         
18138         cfg.cls += ' tab-content';
18139         
18140         if (this.carousel) {
18141             cfg.cls += ' carousel slide';
18142             
18143             cfg.cn = [{
18144                cls : 'carousel-inner',
18145                cn : []
18146             }];
18147         
18148             if(this.bullets  && !Roo.isTouch){
18149                 
18150                 var bullets = {
18151                     cls : 'carousel-bullets',
18152                     cn : []
18153                 };
18154                
18155                 if(this.bullets_cls){
18156                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18157                 }
18158                 
18159                 bullets.cn.push({
18160                     cls : 'clear'
18161                 });
18162                 
18163                 cfg.cn[0].cn.push(bullets);
18164             }
18165             
18166             if(this.showarrow){
18167                 cfg.cn[0].cn.push({
18168                     tag : 'div',
18169                     class : 'carousel-arrow',
18170                     cn : [
18171                         {
18172                             tag : 'div',
18173                             class : 'carousel-prev',
18174                             cn : [
18175                                 {
18176                                     tag : 'i',
18177                                     class : 'fa fa-chevron-left'
18178                                 }
18179                             ]
18180                         },
18181                         {
18182                             tag : 'div',
18183                             class : 'carousel-next',
18184                             cn : [
18185                                 {
18186                                     tag : 'i',
18187                                     class : 'fa fa-chevron-right'
18188                                 }
18189                             ]
18190                         }
18191                     ]
18192                 });
18193             }
18194             
18195         }
18196         
18197         return cfg;
18198     },
18199     
18200     initEvents:  function()
18201     {
18202 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18203 //            this.el.on("touchstart", this.onTouchStart, this);
18204 //        }
18205         
18206         if(this.autoslide){
18207             var _this = this;
18208             
18209             this.slideFn = window.setInterval(function() {
18210                 _this.showPanelNext();
18211             }, this.timer);
18212         }
18213         
18214         if(this.showarrow){
18215             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18216             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18217         }
18218         
18219         
18220     },
18221     
18222 //    onTouchStart : function(e, el, o)
18223 //    {
18224 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18225 //            return;
18226 //        }
18227 //        
18228 //        this.showPanelNext();
18229 //    },
18230     
18231     
18232     getChildContainer : function()
18233     {
18234         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18235     },
18236     
18237     /**
18238     * register a Navigation item
18239     * @param {Roo.bootstrap.NavItem} the navitem to add
18240     */
18241     register : function(item)
18242     {
18243         this.tabs.push( item);
18244         item.navId = this.navId; // not really needed..
18245         this.addBullet();
18246     
18247     },
18248     
18249     getActivePanel : function()
18250     {
18251         var r = false;
18252         Roo.each(this.tabs, function(t) {
18253             if (t.active) {
18254                 r = t;
18255                 return false;
18256             }
18257             return null;
18258         });
18259         return r;
18260         
18261     },
18262     getPanelByName : function(n)
18263     {
18264         var r = false;
18265         Roo.each(this.tabs, function(t) {
18266             if (t.tabId == n) {
18267                 r = t;
18268                 return false;
18269             }
18270             return null;
18271         });
18272         return r;
18273     },
18274     indexOfPanel : function(p)
18275     {
18276         var r = false;
18277         Roo.each(this.tabs, function(t,i) {
18278             if (t.tabId == p.tabId) {
18279                 r = i;
18280                 return false;
18281             }
18282             return null;
18283         });
18284         return r;
18285     },
18286     /**
18287      * show a specific panel
18288      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18289      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18290      */
18291     showPanel : function (pan)
18292     {
18293         if(this.transition || typeof(pan) == 'undefined'){
18294             Roo.log("waiting for the transitionend");
18295             return;
18296         }
18297         
18298         if (typeof(pan) == 'number') {
18299             pan = this.tabs[pan];
18300         }
18301         
18302         if (typeof(pan) == 'string') {
18303             pan = this.getPanelByName(pan);
18304         }
18305         
18306         var cur = this.getActivePanel();
18307         
18308         if(!pan || !cur){
18309             Roo.log('pan or acitve pan is undefined');
18310             return false;
18311         }
18312         
18313         if (pan.tabId == this.getActivePanel().tabId) {
18314             return true;
18315         }
18316         
18317         if (false === cur.fireEvent('beforedeactivate')) {
18318             return false;
18319         }
18320         
18321         if(this.bullets > 0 && !Roo.isTouch){
18322             this.setActiveBullet(this.indexOfPanel(pan));
18323         }
18324         
18325         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18326             
18327             this.transition = true;
18328             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18329             var lr = dir == 'next' ? 'left' : 'right';
18330             pan.el.addClass(dir); // or prev
18331             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18332             cur.el.addClass(lr); // or right
18333             pan.el.addClass(lr);
18334             
18335             var _this = this;
18336             cur.el.on('transitionend', function() {
18337                 Roo.log("trans end?");
18338                 
18339                 pan.el.removeClass([lr,dir]);
18340                 pan.setActive(true);
18341                 
18342                 cur.el.removeClass([lr]);
18343                 cur.setActive(false);
18344                 
18345                 _this.transition = false;
18346                 
18347             }, this, { single:  true } );
18348             
18349             return true;
18350         }
18351         
18352         cur.setActive(false);
18353         pan.setActive(true);
18354         
18355         return true;
18356         
18357     },
18358     showPanelNext : function()
18359     {
18360         var i = this.indexOfPanel(this.getActivePanel());
18361         
18362         if (i >= this.tabs.length - 1 && !this.autoslide) {
18363             return;
18364         }
18365         
18366         if (i >= this.tabs.length - 1 && this.autoslide) {
18367             i = -1;
18368         }
18369         
18370         this.showPanel(this.tabs[i+1]);
18371     },
18372     
18373     showPanelPrev : function()
18374     {
18375         var i = this.indexOfPanel(this.getActivePanel());
18376         
18377         if (i  < 1 && !this.autoslide) {
18378             return;
18379         }
18380         
18381         if (i < 1 && this.autoslide) {
18382             i = this.tabs.length;
18383         }
18384         
18385         this.showPanel(this.tabs[i-1]);
18386     },
18387     
18388     
18389     addBullet: function()
18390     {
18391         if(!this.bullets || Roo.isTouch){
18392             return;
18393         }
18394         var ctr = this.el.select('.carousel-bullets',true).first();
18395         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18396         var bullet = ctr.createChild({
18397             cls : 'bullet bullet-' + i
18398         },ctr.dom.lastChild);
18399         
18400         
18401         var _this = this;
18402         
18403         bullet.on('click', (function(e, el, o, ii, t){
18404
18405             e.preventDefault();
18406
18407             this.showPanel(ii);
18408
18409             if(this.autoslide && this.slideFn){
18410                 clearInterval(this.slideFn);
18411                 this.slideFn = window.setInterval(function() {
18412                     _this.showPanelNext();
18413                 }, this.timer);
18414             }
18415
18416         }).createDelegate(this, [i, bullet], true));
18417                 
18418         
18419     },
18420      
18421     setActiveBullet : function(i)
18422     {
18423         if(Roo.isTouch){
18424             return;
18425         }
18426         
18427         Roo.each(this.el.select('.bullet', true).elements, function(el){
18428             el.removeClass('selected');
18429         });
18430
18431         var bullet = this.el.select('.bullet-' + i, true).first();
18432         
18433         if(!bullet){
18434             return;
18435         }
18436         
18437         bullet.addClass('selected');
18438     }
18439     
18440     
18441   
18442 });
18443
18444  
18445
18446  
18447  
18448 Roo.apply(Roo.bootstrap.TabGroup, {
18449     
18450     groups: {},
18451      /**
18452     * register a Navigation Group
18453     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18454     */
18455     register : function(navgrp)
18456     {
18457         this.groups[navgrp.navId] = navgrp;
18458         
18459     },
18460     /**
18461     * fetch a Navigation Group based on the navigation ID
18462     * if one does not exist , it will get created.
18463     * @param {string} the navgroup to add
18464     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18465     */
18466     get: function(navId) {
18467         if (typeof(this.groups[navId]) == 'undefined') {
18468             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18469         }
18470         return this.groups[navId] ;
18471     }
18472     
18473     
18474     
18475 });
18476
18477  /*
18478  * - LGPL
18479  *
18480  * TabPanel
18481  * 
18482  */
18483
18484 /**
18485  * @class Roo.bootstrap.TabPanel
18486  * @extends Roo.bootstrap.Component
18487  * Bootstrap TabPanel class
18488  * @cfg {Boolean} active panel active
18489  * @cfg {String} html panel content
18490  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18491  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18492  * @cfg {String} href click to link..
18493  * 
18494  * 
18495  * @constructor
18496  * Create a new TabPanel
18497  * @param {Object} config The config object
18498  */
18499
18500 Roo.bootstrap.TabPanel = function(config){
18501     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18502     this.addEvents({
18503         /**
18504              * @event changed
18505              * Fires when the active status changes
18506              * @param {Roo.bootstrap.TabPanel} this
18507              * @param {Boolean} state the new state
18508             
18509          */
18510         'changed': true,
18511         /**
18512              * @event beforedeactivate
18513              * Fires before a tab is de-activated - can be used to do validation on a form.
18514              * @param {Roo.bootstrap.TabPanel} this
18515              * @return {Boolean} false if there is an error
18516             
18517          */
18518         'beforedeactivate': true
18519      });
18520     
18521     this.tabId = this.tabId || Roo.id();
18522   
18523 };
18524
18525 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18526     
18527     active: false,
18528     html: false,
18529     tabId: false,
18530     navId : false,
18531     href : '',
18532     
18533     getAutoCreate : function(){
18534         var cfg = {
18535             tag: 'div',
18536             // item is needed for carousel - not sure if it has any effect otherwise
18537             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18538             html: this.html || ''
18539         };
18540         
18541         if(this.active){
18542             cfg.cls += ' active';
18543         }
18544         
18545         if(this.tabId){
18546             cfg.tabId = this.tabId;
18547         }
18548         
18549         
18550         return cfg;
18551     },
18552     
18553     initEvents:  function()
18554     {
18555         var p = this.parent();
18556         
18557         this.navId = this.navId || p.navId;
18558         
18559         if (typeof(this.navId) != 'undefined') {
18560             // not really needed.. but just in case.. parent should be a NavGroup.
18561             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18562             
18563             tg.register(this);
18564             
18565             var i = tg.tabs.length - 1;
18566             
18567             if(this.active && tg.bullets > 0 && i < tg.bullets){
18568                 tg.setActiveBullet(i);
18569             }
18570         }
18571         
18572         this.el.on('click', this.onClick, this);
18573         
18574         if(Roo.isTouch){
18575             this.el.on("touchstart", this.onTouchStart, this);
18576             this.el.on("touchmove", this.onTouchMove, this);
18577             this.el.on("touchend", this.onTouchEnd, this);
18578         }
18579         
18580     },
18581     
18582     onRender : function(ct, position)
18583     {
18584         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18585     },
18586     
18587     setActive : function(state)
18588     {
18589         Roo.log("panel - set active " + this.tabId + "=" + state);
18590         
18591         this.active = state;
18592         if (!state) {
18593             this.el.removeClass('active');
18594             
18595         } else  if (!this.el.hasClass('active')) {
18596             this.el.addClass('active');
18597         }
18598         
18599         this.fireEvent('changed', this, state);
18600     },
18601     
18602     onClick : function(e)
18603     {
18604         e.preventDefault();
18605         
18606         if(!this.href.length){
18607             return;
18608         }
18609         
18610         window.location.href = this.href;
18611     },
18612     
18613     startX : 0,
18614     startY : 0,
18615     endX : 0,
18616     endY : 0,
18617     swiping : false,
18618     
18619     onTouchStart : function(e)
18620     {
18621         this.swiping = false;
18622         
18623         this.startX = e.browserEvent.touches[0].clientX;
18624         this.startY = e.browserEvent.touches[0].clientY;
18625     },
18626     
18627     onTouchMove : function(e)
18628     {
18629         this.swiping = true;
18630         
18631         this.endX = e.browserEvent.touches[0].clientX;
18632         this.endY = e.browserEvent.touches[0].clientY;
18633     },
18634     
18635     onTouchEnd : function(e)
18636     {
18637         if(!this.swiping){
18638             this.onClick(e);
18639             return;
18640         }
18641         
18642         var tabGroup = this.parent();
18643         
18644         if(this.endX > this.startX){ // swiping right
18645             tabGroup.showPanelPrev();
18646             return;
18647         }
18648         
18649         if(this.startX > this.endX){ // swiping left
18650             tabGroup.showPanelNext();
18651             return;
18652         }
18653     }
18654     
18655     
18656 });
18657  
18658
18659  
18660
18661  /*
18662  * - LGPL
18663  *
18664  * DateField
18665  * 
18666  */
18667
18668 /**
18669  * @class Roo.bootstrap.DateField
18670  * @extends Roo.bootstrap.Input
18671  * Bootstrap DateField class
18672  * @cfg {Number} weekStart default 0
18673  * @cfg {String} viewMode default empty, (months|years)
18674  * @cfg {String} minViewMode default empty, (months|years)
18675  * @cfg {Number} startDate default -Infinity
18676  * @cfg {Number} endDate default Infinity
18677  * @cfg {Boolean} todayHighlight default false
18678  * @cfg {Boolean} todayBtn default false
18679  * @cfg {Boolean} calendarWeeks default false
18680  * @cfg {Object} daysOfWeekDisabled default empty
18681  * @cfg {Boolean} singleMode default false (true | false)
18682  * 
18683  * @cfg {Boolean} keyboardNavigation default true
18684  * @cfg {String} language default en
18685  * 
18686  * @constructor
18687  * Create a new DateField
18688  * @param {Object} config The config object
18689  */
18690
18691 Roo.bootstrap.DateField = function(config){
18692     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18693      this.addEvents({
18694             /**
18695              * @event show
18696              * Fires when this field show.
18697              * @param {Roo.bootstrap.DateField} this
18698              * @param {Mixed} date The date value
18699              */
18700             show : true,
18701             /**
18702              * @event show
18703              * Fires when this field hide.
18704              * @param {Roo.bootstrap.DateField} this
18705              * @param {Mixed} date The date value
18706              */
18707             hide : true,
18708             /**
18709              * @event select
18710              * Fires when select a date.
18711              * @param {Roo.bootstrap.DateField} this
18712              * @param {Mixed} date The date value
18713              */
18714             select : true,
18715             /**
18716              * @event beforeselect
18717              * Fires when before select a date.
18718              * @param {Roo.bootstrap.DateField} this
18719              * @param {Mixed} date The date value
18720              */
18721             beforeselect : true
18722         });
18723 };
18724
18725 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18726     
18727     /**
18728      * @cfg {String} format
18729      * The default date format string which can be overriden for localization support.  The format must be
18730      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18731      */
18732     format : "m/d/y",
18733     /**
18734      * @cfg {String} altFormats
18735      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18736      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18737      */
18738     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18739     
18740     weekStart : 0,
18741     
18742     viewMode : '',
18743     
18744     minViewMode : '',
18745     
18746     todayHighlight : false,
18747     
18748     todayBtn: false,
18749     
18750     language: 'en',
18751     
18752     keyboardNavigation: true,
18753     
18754     calendarWeeks: false,
18755     
18756     startDate: -Infinity,
18757     
18758     endDate: Infinity,
18759     
18760     daysOfWeekDisabled: [],
18761     
18762     _events: [],
18763     
18764     singleMode : false,
18765     
18766     UTCDate: function()
18767     {
18768         return new Date(Date.UTC.apply(Date, arguments));
18769     },
18770     
18771     UTCToday: function()
18772     {
18773         var today = new Date();
18774         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18775     },
18776     
18777     getDate: function() {
18778             var d = this.getUTCDate();
18779             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18780     },
18781     
18782     getUTCDate: function() {
18783             return this.date;
18784     },
18785     
18786     setDate: function(d) {
18787             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18788     },
18789     
18790     setUTCDate: function(d) {
18791             this.date = d;
18792             this.setValue(this.formatDate(this.date));
18793     },
18794         
18795     onRender: function(ct, position)
18796     {
18797         
18798         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18799         
18800         this.language = this.language || 'en';
18801         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18802         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18803         
18804         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18805         this.format = this.format || 'm/d/y';
18806         this.isInline = false;
18807         this.isInput = true;
18808         this.component = this.el.select('.add-on', true).first() || false;
18809         this.component = (this.component && this.component.length === 0) ? false : this.component;
18810         this.hasInput = this.component && this.inputEl().length;
18811         
18812         if (typeof(this.minViewMode === 'string')) {
18813             switch (this.minViewMode) {
18814                 case 'months':
18815                     this.minViewMode = 1;
18816                     break;
18817                 case 'years':
18818                     this.minViewMode = 2;
18819                     break;
18820                 default:
18821                     this.minViewMode = 0;
18822                     break;
18823             }
18824         }
18825         
18826         if (typeof(this.viewMode === 'string')) {
18827             switch (this.viewMode) {
18828                 case 'months':
18829                     this.viewMode = 1;
18830                     break;
18831                 case 'years':
18832                     this.viewMode = 2;
18833                     break;
18834                 default:
18835                     this.viewMode = 0;
18836                     break;
18837             }
18838         }
18839                 
18840         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18841         
18842 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18843         
18844         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18845         
18846         this.picker().on('mousedown', this.onMousedown, this);
18847         this.picker().on('click', this.onClick, this);
18848         
18849         this.picker().addClass('datepicker-dropdown');
18850         
18851         this.startViewMode = this.viewMode;
18852         
18853         if(this.singleMode){
18854             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18855                 v.setVisibilityMode(Roo.Element.DISPLAY);
18856                 v.hide();
18857             });
18858             
18859             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18860                 v.setStyle('width', '189px');
18861             });
18862         }
18863         
18864         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18865             if(!this.calendarWeeks){
18866                 v.remove();
18867                 return;
18868             }
18869             
18870             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18871             v.attr('colspan', function(i, val){
18872                 return parseInt(val) + 1;
18873             });
18874         });
18875                         
18876         
18877         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18878         
18879         this.setStartDate(this.startDate);
18880         this.setEndDate(this.endDate);
18881         
18882         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18883         
18884         this.fillDow();
18885         this.fillMonths();
18886         this.update();
18887         this.showMode();
18888         
18889         if(this.isInline) {
18890             this.showPopup();
18891         }
18892     },
18893     
18894     picker : function()
18895     {
18896         return this.pickerEl;
18897 //        return this.el.select('.datepicker', true).first();
18898     },
18899     
18900     fillDow: function()
18901     {
18902         var dowCnt = this.weekStart;
18903         
18904         var dow = {
18905             tag: 'tr',
18906             cn: [
18907                 
18908             ]
18909         };
18910         
18911         if(this.calendarWeeks){
18912             dow.cn.push({
18913                 tag: 'th',
18914                 cls: 'cw',
18915                 html: '&nbsp;'
18916             })
18917         }
18918         
18919         while (dowCnt < this.weekStart + 7) {
18920             dow.cn.push({
18921                 tag: 'th',
18922                 cls: 'dow',
18923                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18924             });
18925         }
18926         
18927         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18928     },
18929     
18930     fillMonths: function()
18931     {    
18932         var i = 0;
18933         var months = this.picker().select('>.datepicker-months td', true).first();
18934         
18935         months.dom.innerHTML = '';
18936         
18937         while (i < 12) {
18938             var month = {
18939                 tag: 'span',
18940                 cls: 'month',
18941                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18942             };
18943             
18944             months.createChild(month);
18945         }
18946         
18947     },
18948     
18949     update: function()
18950     {
18951         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;
18952         
18953         if (this.date < this.startDate) {
18954             this.viewDate = new Date(this.startDate);
18955         } else if (this.date > this.endDate) {
18956             this.viewDate = new Date(this.endDate);
18957         } else {
18958             this.viewDate = new Date(this.date);
18959         }
18960         
18961         this.fill();
18962     },
18963     
18964     fill: function() 
18965     {
18966         var d = new Date(this.viewDate),
18967                 year = d.getUTCFullYear(),
18968                 month = d.getUTCMonth(),
18969                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18970                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18971                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18972                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18973                 currentDate = this.date && this.date.valueOf(),
18974                 today = this.UTCToday();
18975         
18976         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18977         
18978 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18979         
18980 //        this.picker.select('>tfoot th.today').
18981 //                                              .text(dates[this.language].today)
18982 //                                              .toggle(this.todayBtn !== false);
18983     
18984         this.updateNavArrows();
18985         this.fillMonths();
18986                                                 
18987         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18988         
18989         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18990          
18991         prevMonth.setUTCDate(day);
18992         
18993         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18994         
18995         var nextMonth = new Date(prevMonth);
18996         
18997         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18998         
18999         nextMonth = nextMonth.valueOf();
19000         
19001         var fillMonths = false;
19002         
19003         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19004         
19005         while(prevMonth.valueOf() <= nextMonth) {
19006             var clsName = '';
19007             
19008             if (prevMonth.getUTCDay() === this.weekStart) {
19009                 if(fillMonths){
19010                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19011                 }
19012                     
19013                 fillMonths = {
19014                     tag: 'tr',
19015                     cn: []
19016                 };
19017                 
19018                 if(this.calendarWeeks){
19019                     // ISO 8601: First week contains first thursday.
19020                     // ISO also states week starts on Monday, but we can be more abstract here.
19021                     var
19022                     // Start of current week: based on weekstart/current date
19023                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19024                     // Thursday of this week
19025                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19026                     // First Thursday of year, year from thursday
19027                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19028                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19029                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19030                     
19031                     fillMonths.cn.push({
19032                         tag: 'td',
19033                         cls: 'cw',
19034                         html: calWeek
19035                     });
19036                 }
19037             }
19038             
19039             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19040                 clsName += ' old';
19041             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19042                 clsName += ' new';
19043             }
19044             if (this.todayHighlight &&
19045                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19046                 prevMonth.getUTCMonth() == today.getMonth() &&
19047                 prevMonth.getUTCDate() == today.getDate()) {
19048                 clsName += ' today';
19049             }
19050             
19051             if (currentDate && prevMonth.valueOf() === currentDate) {
19052                 clsName += ' active';
19053             }
19054             
19055             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19056                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19057                     clsName += ' disabled';
19058             }
19059             
19060             fillMonths.cn.push({
19061                 tag: 'td',
19062                 cls: 'day ' + clsName,
19063                 html: prevMonth.getDate()
19064             });
19065             
19066             prevMonth.setDate(prevMonth.getDate()+1);
19067         }
19068           
19069         var currentYear = this.date && this.date.getUTCFullYear();
19070         var currentMonth = this.date && this.date.getUTCMonth();
19071         
19072         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19073         
19074         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19075             v.removeClass('active');
19076             
19077             if(currentYear === year && k === currentMonth){
19078                 v.addClass('active');
19079             }
19080             
19081             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19082                 v.addClass('disabled');
19083             }
19084             
19085         });
19086         
19087         
19088         year = parseInt(year/10, 10) * 10;
19089         
19090         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19091         
19092         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19093         
19094         year -= 1;
19095         for (var i = -1; i < 11; i++) {
19096             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19097                 tag: 'span',
19098                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19099                 html: year
19100             });
19101             
19102             year += 1;
19103         }
19104     },
19105     
19106     showMode: function(dir) 
19107     {
19108         if (dir) {
19109             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19110         }
19111         
19112         Roo.each(this.picker().select('>div',true).elements, function(v){
19113             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19114             v.hide();
19115         });
19116         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19117     },
19118     
19119     place: function()
19120     {
19121         if(this.isInline) {
19122             return;
19123         }
19124         
19125         this.picker().removeClass(['bottom', 'top']);
19126         
19127         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19128             /*
19129              * place to the top of element!
19130              *
19131              */
19132             
19133             this.picker().addClass('top');
19134             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19135             
19136             return;
19137         }
19138         
19139         this.picker().addClass('bottom');
19140         
19141         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19142     },
19143     
19144     parseDate : function(value)
19145     {
19146         if(!value || value instanceof Date){
19147             return value;
19148         }
19149         var v = Date.parseDate(value, this.format);
19150         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19151             v = Date.parseDate(value, 'Y-m-d');
19152         }
19153         if(!v && this.altFormats){
19154             if(!this.altFormatsArray){
19155                 this.altFormatsArray = this.altFormats.split("|");
19156             }
19157             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19158                 v = Date.parseDate(value, this.altFormatsArray[i]);
19159             }
19160         }
19161         return v;
19162     },
19163     
19164     formatDate : function(date, fmt)
19165     {   
19166         return (!date || !(date instanceof Date)) ?
19167         date : date.dateFormat(fmt || this.format);
19168     },
19169     
19170     onFocus : function()
19171     {
19172         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19173         this.showPopup();
19174     },
19175     
19176     onBlur : function()
19177     {
19178         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19179         
19180         var d = this.inputEl().getValue();
19181         
19182         this.setValue(d);
19183                 
19184         this.hidePopup();
19185     },
19186     
19187     showPopup : function()
19188     {
19189         this.picker().show();
19190         this.update();
19191         this.place();
19192         
19193         this.fireEvent('showpopup', this, this.date);
19194     },
19195     
19196     hidePopup : function()
19197     {
19198         if(this.isInline) {
19199             return;
19200         }
19201         this.picker().hide();
19202         this.viewMode = this.startViewMode;
19203         this.showMode();
19204         
19205         this.fireEvent('hidepopup', this, this.date);
19206         
19207     },
19208     
19209     onMousedown: function(e)
19210     {
19211         e.stopPropagation();
19212         e.preventDefault();
19213     },
19214     
19215     keyup: function(e)
19216     {
19217         Roo.bootstrap.DateField.superclass.keyup.call(this);
19218         this.update();
19219     },
19220
19221     setValue: function(v)
19222     {
19223         if(this.fireEvent('beforeselect', this, v) !== false){
19224             var d = new Date(this.parseDate(v) ).clearTime();
19225         
19226             if(isNaN(d.getTime())){
19227                 this.date = this.viewDate = '';
19228                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19229                 return;
19230             }
19231
19232             v = this.formatDate(d);
19233
19234             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19235
19236             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19237
19238             this.update();
19239
19240             this.fireEvent('select', this, this.date);
19241         }
19242     },
19243     
19244     getValue: function()
19245     {
19246         return this.formatDate(this.date);
19247     },
19248     
19249     fireKey: function(e)
19250     {
19251         if (!this.picker().isVisible()){
19252             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19253                 this.showPopup();
19254             }
19255             return;
19256         }
19257         
19258         var dateChanged = false,
19259         dir, day, month,
19260         newDate, newViewDate;
19261         
19262         switch(e.keyCode){
19263             case 27: // escape
19264                 this.hidePopup();
19265                 e.preventDefault();
19266                 break;
19267             case 37: // left
19268             case 39: // right
19269                 if (!this.keyboardNavigation) {
19270                     break;
19271                 }
19272                 dir = e.keyCode == 37 ? -1 : 1;
19273                 
19274                 if (e.ctrlKey){
19275                     newDate = this.moveYear(this.date, dir);
19276                     newViewDate = this.moveYear(this.viewDate, dir);
19277                 } else if (e.shiftKey){
19278                     newDate = this.moveMonth(this.date, dir);
19279                     newViewDate = this.moveMonth(this.viewDate, dir);
19280                 } else {
19281                     newDate = new Date(this.date);
19282                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19283                     newViewDate = new Date(this.viewDate);
19284                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19285                 }
19286                 if (this.dateWithinRange(newDate)){
19287                     this.date = newDate;
19288                     this.viewDate = newViewDate;
19289                     this.setValue(this.formatDate(this.date));
19290 //                    this.update();
19291                     e.preventDefault();
19292                     dateChanged = true;
19293                 }
19294                 break;
19295             case 38: // up
19296             case 40: // down
19297                 if (!this.keyboardNavigation) {
19298                     break;
19299                 }
19300                 dir = e.keyCode == 38 ? -1 : 1;
19301                 if (e.ctrlKey){
19302                     newDate = this.moveYear(this.date, dir);
19303                     newViewDate = this.moveYear(this.viewDate, dir);
19304                 } else if (e.shiftKey){
19305                     newDate = this.moveMonth(this.date, dir);
19306                     newViewDate = this.moveMonth(this.viewDate, dir);
19307                 } else {
19308                     newDate = new Date(this.date);
19309                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19310                     newViewDate = new Date(this.viewDate);
19311                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19312                 }
19313                 if (this.dateWithinRange(newDate)){
19314                     this.date = newDate;
19315                     this.viewDate = newViewDate;
19316                     this.setValue(this.formatDate(this.date));
19317 //                    this.update();
19318                     e.preventDefault();
19319                     dateChanged = true;
19320                 }
19321                 break;
19322             case 13: // enter
19323                 this.setValue(this.formatDate(this.date));
19324                 this.hidePopup();
19325                 e.preventDefault();
19326                 break;
19327             case 9: // tab
19328                 this.setValue(this.formatDate(this.date));
19329                 this.hidePopup();
19330                 break;
19331             case 16: // shift
19332             case 17: // ctrl
19333             case 18: // alt
19334                 break;
19335             default :
19336                 this.hidePopup();
19337                 
19338         }
19339     },
19340     
19341     
19342     onClick: function(e) 
19343     {
19344         e.stopPropagation();
19345         e.preventDefault();
19346         
19347         var target = e.getTarget();
19348         
19349         if(target.nodeName.toLowerCase() === 'i'){
19350             target = Roo.get(target).dom.parentNode;
19351         }
19352         
19353         var nodeName = target.nodeName;
19354         var className = target.className;
19355         var html = target.innerHTML;
19356         //Roo.log(nodeName);
19357         
19358         switch(nodeName.toLowerCase()) {
19359             case 'th':
19360                 switch(className) {
19361                     case 'switch':
19362                         this.showMode(1);
19363                         break;
19364                     case 'prev':
19365                     case 'next':
19366                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19367                         switch(this.viewMode){
19368                                 case 0:
19369                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19370                                         break;
19371                                 case 1:
19372                                 case 2:
19373                                         this.viewDate = this.moveYear(this.viewDate, dir);
19374                                         break;
19375                         }
19376                         this.fill();
19377                         break;
19378                     case 'today':
19379                         var date = new Date();
19380                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19381 //                        this.fill()
19382                         this.setValue(this.formatDate(this.date));
19383                         
19384                         this.hidePopup();
19385                         break;
19386                 }
19387                 break;
19388             case 'span':
19389                 if (className.indexOf('disabled') < 0) {
19390                     this.viewDate.setUTCDate(1);
19391                     if (className.indexOf('month') > -1) {
19392                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19393                     } else {
19394                         var year = parseInt(html, 10) || 0;
19395                         this.viewDate.setUTCFullYear(year);
19396                         
19397                     }
19398                     
19399                     if(this.singleMode){
19400                         this.setValue(this.formatDate(this.viewDate));
19401                         this.hidePopup();
19402                         return;
19403                     }
19404                     
19405                     this.showMode(-1);
19406                     this.fill();
19407                 }
19408                 break;
19409                 
19410             case 'td':
19411                 //Roo.log(className);
19412                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19413                     var day = parseInt(html, 10) || 1;
19414                     var year = this.viewDate.getUTCFullYear(),
19415                         month = this.viewDate.getUTCMonth();
19416
19417                     if (className.indexOf('old') > -1) {
19418                         if(month === 0 ){
19419                             month = 11;
19420                             year -= 1;
19421                         }else{
19422                             month -= 1;
19423                         }
19424                     } else if (className.indexOf('new') > -1) {
19425                         if (month == 11) {
19426                             month = 0;
19427                             year += 1;
19428                         } else {
19429                             month += 1;
19430                         }
19431                     }
19432                     //Roo.log([year,month,day]);
19433                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19434                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19435 //                    this.fill();
19436                     //Roo.log(this.formatDate(this.date));
19437                     this.setValue(this.formatDate(this.date));
19438                     this.hidePopup();
19439                 }
19440                 break;
19441         }
19442     },
19443     
19444     setStartDate: function(startDate)
19445     {
19446         this.startDate = startDate || -Infinity;
19447         if (this.startDate !== -Infinity) {
19448             this.startDate = this.parseDate(this.startDate);
19449         }
19450         this.update();
19451         this.updateNavArrows();
19452     },
19453
19454     setEndDate: function(endDate)
19455     {
19456         this.endDate = endDate || Infinity;
19457         if (this.endDate !== Infinity) {
19458             this.endDate = this.parseDate(this.endDate);
19459         }
19460         this.update();
19461         this.updateNavArrows();
19462     },
19463     
19464     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19465     {
19466         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19467         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19468             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19469         }
19470         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19471             return parseInt(d, 10);
19472         });
19473         this.update();
19474         this.updateNavArrows();
19475     },
19476     
19477     updateNavArrows: function() 
19478     {
19479         if(this.singleMode){
19480             return;
19481         }
19482         
19483         var d = new Date(this.viewDate),
19484         year = d.getUTCFullYear(),
19485         month = d.getUTCMonth();
19486         
19487         Roo.each(this.picker().select('.prev', true).elements, function(v){
19488             v.show();
19489             switch (this.viewMode) {
19490                 case 0:
19491
19492                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19493                         v.hide();
19494                     }
19495                     break;
19496                 case 1:
19497                 case 2:
19498                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19499                         v.hide();
19500                     }
19501                     break;
19502             }
19503         });
19504         
19505         Roo.each(this.picker().select('.next', true).elements, function(v){
19506             v.show();
19507             switch (this.viewMode) {
19508                 case 0:
19509
19510                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19511                         v.hide();
19512                     }
19513                     break;
19514                 case 1:
19515                 case 2:
19516                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19517                         v.hide();
19518                     }
19519                     break;
19520             }
19521         })
19522     },
19523     
19524     moveMonth: function(date, dir)
19525     {
19526         if (!dir) {
19527             return date;
19528         }
19529         var new_date = new Date(date.valueOf()),
19530         day = new_date.getUTCDate(),
19531         month = new_date.getUTCMonth(),
19532         mag = Math.abs(dir),
19533         new_month, test;
19534         dir = dir > 0 ? 1 : -1;
19535         if (mag == 1){
19536             test = dir == -1
19537             // If going back one month, make sure month is not current month
19538             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19539             ? function(){
19540                 return new_date.getUTCMonth() == month;
19541             }
19542             // If going forward one month, make sure month is as expected
19543             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19544             : function(){
19545                 return new_date.getUTCMonth() != new_month;
19546             };
19547             new_month = month + dir;
19548             new_date.setUTCMonth(new_month);
19549             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19550             if (new_month < 0 || new_month > 11) {
19551                 new_month = (new_month + 12) % 12;
19552             }
19553         } else {
19554             // For magnitudes >1, move one month at a time...
19555             for (var i=0; i<mag; i++) {
19556                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19557                 new_date = this.moveMonth(new_date, dir);
19558             }
19559             // ...then reset the day, keeping it in the new month
19560             new_month = new_date.getUTCMonth();
19561             new_date.setUTCDate(day);
19562             test = function(){
19563                 return new_month != new_date.getUTCMonth();
19564             };
19565         }
19566         // Common date-resetting loop -- if date is beyond end of month, make it
19567         // end of month
19568         while (test()){
19569             new_date.setUTCDate(--day);
19570             new_date.setUTCMonth(new_month);
19571         }
19572         return new_date;
19573     },
19574
19575     moveYear: function(date, dir)
19576     {
19577         return this.moveMonth(date, dir*12);
19578     },
19579
19580     dateWithinRange: function(date)
19581     {
19582         return date >= this.startDate && date <= this.endDate;
19583     },
19584
19585     
19586     remove: function() 
19587     {
19588         this.picker().remove();
19589     },
19590     
19591     validateValue : function(value)
19592     {
19593         if(this.getVisibilityEl().hasClass('hidden')){
19594             return true;
19595         }
19596         
19597         if(value.length < 1)  {
19598             if(this.allowBlank){
19599                 return true;
19600             }
19601             return false;
19602         }
19603         
19604         if(value.length < this.minLength){
19605             return false;
19606         }
19607         if(value.length > this.maxLength){
19608             return false;
19609         }
19610         if(this.vtype){
19611             var vt = Roo.form.VTypes;
19612             if(!vt[this.vtype](value, this)){
19613                 return false;
19614             }
19615         }
19616         if(typeof this.validator == "function"){
19617             var msg = this.validator(value);
19618             if(msg !== true){
19619                 return false;
19620             }
19621         }
19622         
19623         if(this.regex && !this.regex.test(value)){
19624             return false;
19625         }
19626         
19627         if(typeof(this.parseDate(value)) == 'undefined'){
19628             return false;
19629         }
19630         
19631         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19632             return false;
19633         }      
19634         
19635         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19636             return false;
19637         } 
19638         
19639         
19640         return true;
19641     },
19642     
19643     reset : function()
19644     {
19645         this.date = this.viewDate = '';
19646         
19647         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19648     }
19649    
19650 });
19651
19652 Roo.apply(Roo.bootstrap.DateField,  {
19653     
19654     head : {
19655         tag: 'thead',
19656         cn: [
19657         {
19658             tag: 'tr',
19659             cn: [
19660             {
19661                 tag: 'th',
19662                 cls: 'prev',
19663                 html: '<i class="fa fa-arrow-left"/>'
19664             },
19665             {
19666                 tag: 'th',
19667                 cls: 'switch',
19668                 colspan: '5'
19669             },
19670             {
19671                 tag: 'th',
19672                 cls: 'next',
19673                 html: '<i class="fa fa-arrow-right"/>'
19674             }
19675
19676             ]
19677         }
19678         ]
19679     },
19680     
19681     content : {
19682         tag: 'tbody',
19683         cn: [
19684         {
19685             tag: 'tr',
19686             cn: [
19687             {
19688                 tag: 'td',
19689                 colspan: '7'
19690             }
19691             ]
19692         }
19693         ]
19694     },
19695     
19696     footer : {
19697         tag: 'tfoot',
19698         cn: [
19699         {
19700             tag: 'tr',
19701             cn: [
19702             {
19703                 tag: 'th',
19704                 colspan: '7',
19705                 cls: 'today'
19706             }
19707                     
19708             ]
19709         }
19710         ]
19711     },
19712     
19713     dates:{
19714         en: {
19715             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19716             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19717             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19718             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19719             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19720             today: "Today"
19721         }
19722     },
19723     
19724     modes: [
19725     {
19726         clsName: 'days',
19727         navFnc: 'Month',
19728         navStep: 1
19729     },
19730     {
19731         clsName: 'months',
19732         navFnc: 'FullYear',
19733         navStep: 1
19734     },
19735     {
19736         clsName: 'years',
19737         navFnc: 'FullYear',
19738         navStep: 10
19739     }]
19740 });
19741
19742 Roo.apply(Roo.bootstrap.DateField,  {
19743   
19744     template : {
19745         tag: 'div',
19746         cls: 'datepicker dropdown-menu roo-dynamic',
19747         cn: [
19748         {
19749             tag: 'div',
19750             cls: 'datepicker-days',
19751             cn: [
19752             {
19753                 tag: 'table',
19754                 cls: 'table-condensed',
19755                 cn:[
19756                 Roo.bootstrap.DateField.head,
19757                 {
19758                     tag: 'tbody'
19759                 },
19760                 Roo.bootstrap.DateField.footer
19761                 ]
19762             }
19763             ]
19764         },
19765         {
19766             tag: 'div',
19767             cls: 'datepicker-months',
19768             cn: [
19769             {
19770                 tag: 'table',
19771                 cls: 'table-condensed',
19772                 cn:[
19773                 Roo.bootstrap.DateField.head,
19774                 Roo.bootstrap.DateField.content,
19775                 Roo.bootstrap.DateField.footer
19776                 ]
19777             }
19778             ]
19779         },
19780         {
19781             tag: 'div',
19782             cls: 'datepicker-years',
19783             cn: [
19784             {
19785                 tag: 'table',
19786                 cls: 'table-condensed',
19787                 cn:[
19788                 Roo.bootstrap.DateField.head,
19789                 Roo.bootstrap.DateField.content,
19790                 Roo.bootstrap.DateField.footer
19791                 ]
19792             }
19793             ]
19794         }
19795         ]
19796     }
19797 });
19798
19799  
19800
19801  /*
19802  * - LGPL
19803  *
19804  * TimeField
19805  * 
19806  */
19807
19808 /**
19809  * @class Roo.bootstrap.TimeField
19810  * @extends Roo.bootstrap.Input
19811  * Bootstrap DateField class
19812  * 
19813  * 
19814  * @constructor
19815  * Create a new TimeField
19816  * @param {Object} config The config object
19817  */
19818
19819 Roo.bootstrap.TimeField = function(config){
19820     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19821     this.addEvents({
19822             /**
19823              * @event show
19824              * Fires when this field show.
19825              * @param {Roo.bootstrap.DateField} thisthis
19826              * @param {Mixed} date The date value
19827              */
19828             show : true,
19829             /**
19830              * @event show
19831              * Fires when this field hide.
19832              * @param {Roo.bootstrap.DateField} this
19833              * @param {Mixed} date The date value
19834              */
19835             hide : true,
19836             /**
19837              * @event select
19838              * Fires when select a date.
19839              * @param {Roo.bootstrap.DateField} this
19840              * @param {Mixed} date The date value
19841              */
19842             select : true
19843         });
19844 };
19845
19846 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19847     
19848     /**
19849      * @cfg {String} format
19850      * The default time format string which can be overriden for localization support.  The format must be
19851      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19852      */
19853     format : "H:i",
19854        
19855     onRender: function(ct, position)
19856     {
19857         
19858         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19859                 
19860         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19861         
19862         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19863         
19864         this.pop = this.picker().select('>.datepicker-time',true).first();
19865         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19866         
19867         this.picker().on('mousedown', this.onMousedown, this);
19868         this.picker().on('click', this.onClick, this);
19869         
19870         this.picker().addClass('datepicker-dropdown');
19871     
19872         this.fillTime();
19873         this.update();
19874             
19875         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19876         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19877         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19878         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19879         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19880         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19881
19882     },
19883     
19884     fireKey: function(e){
19885         if (!this.picker().isVisible()){
19886             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19887                 this.show();
19888             }
19889             return;
19890         }
19891
19892         e.preventDefault();
19893         
19894         switch(e.keyCode){
19895             case 27: // escape
19896                 this.hide();
19897                 break;
19898             case 37: // left
19899             case 39: // right
19900                 this.onTogglePeriod();
19901                 break;
19902             case 38: // up
19903                 this.onIncrementMinutes();
19904                 break;
19905             case 40: // down
19906                 this.onDecrementMinutes();
19907                 break;
19908             case 13: // enter
19909             case 9: // tab
19910                 this.setTime();
19911                 break;
19912         }
19913     },
19914     
19915     onClick: function(e) {
19916         e.stopPropagation();
19917         e.preventDefault();
19918     },
19919     
19920     picker : function()
19921     {
19922         return this.el.select('.datepicker', true).first();
19923     },
19924     
19925     fillTime: function()
19926     {    
19927         var time = this.pop.select('tbody', true).first();
19928         
19929         time.dom.innerHTML = '';
19930         
19931         time.createChild({
19932             tag: 'tr',
19933             cn: [
19934                 {
19935                     tag: 'td',
19936                     cn: [
19937                         {
19938                             tag: 'a',
19939                             href: '#',
19940                             cls: 'btn',
19941                             cn: [
19942                                 {
19943                                     tag: 'span',
19944                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19945                                 }
19946                             ]
19947                         } 
19948                     ]
19949                 },
19950                 {
19951                     tag: 'td',
19952                     cls: 'separator'
19953                 },
19954                 {
19955                     tag: 'td',
19956                     cn: [
19957                         {
19958                             tag: 'a',
19959                             href: '#',
19960                             cls: 'btn',
19961                             cn: [
19962                                 {
19963                                     tag: 'span',
19964                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19965                                 }
19966                             ]
19967                         }
19968                     ]
19969                 },
19970                 {
19971                     tag: 'td',
19972                     cls: 'separator'
19973                 }
19974             ]
19975         });
19976         
19977         time.createChild({
19978             tag: 'tr',
19979             cn: [
19980                 {
19981                     tag: 'td',
19982                     cn: [
19983                         {
19984                             tag: 'span',
19985                             cls: 'timepicker-hour',
19986                             html: '00'
19987                         }  
19988                     ]
19989                 },
19990                 {
19991                     tag: 'td',
19992                     cls: 'separator',
19993                     html: ':'
19994                 },
19995                 {
19996                     tag: 'td',
19997                     cn: [
19998                         {
19999                             tag: 'span',
20000                             cls: 'timepicker-minute',
20001                             html: '00'
20002                         }  
20003                     ]
20004                 },
20005                 {
20006                     tag: 'td',
20007                     cls: 'separator'
20008                 },
20009                 {
20010                     tag: 'td',
20011                     cn: [
20012                         {
20013                             tag: 'button',
20014                             type: 'button',
20015                             cls: 'btn btn-primary period',
20016                             html: 'AM'
20017                             
20018                         }
20019                     ]
20020                 }
20021             ]
20022         });
20023         
20024         time.createChild({
20025             tag: 'tr',
20026             cn: [
20027                 {
20028                     tag: 'td',
20029                     cn: [
20030                         {
20031                             tag: 'a',
20032                             href: '#',
20033                             cls: 'btn',
20034                             cn: [
20035                                 {
20036                                     tag: 'span',
20037                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20038                                 }
20039                             ]
20040                         }
20041                     ]
20042                 },
20043                 {
20044                     tag: 'td',
20045                     cls: 'separator'
20046                 },
20047                 {
20048                     tag: 'td',
20049                     cn: [
20050                         {
20051                             tag: 'a',
20052                             href: '#',
20053                             cls: 'btn',
20054                             cn: [
20055                                 {
20056                                     tag: 'span',
20057                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20058                                 }
20059                             ]
20060                         }
20061                     ]
20062                 },
20063                 {
20064                     tag: 'td',
20065                     cls: 'separator'
20066                 }
20067             ]
20068         });
20069         
20070     },
20071     
20072     update: function()
20073     {
20074         
20075         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20076         
20077         this.fill();
20078     },
20079     
20080     fill: function() 
20081     {
20082         var hours = this.time.getHours();
20083         var minutes = this.time.getMinutes();
20084         var period = 'AM';
20085         
20086         if(hours > 11){
20087             period = 'PM';
20088         }
20089         
20090         if(hours == 0){
20091             hours = 12;
20092         }
20093         
20094         
20095         if(hours > 12){
20096             hours = hours - 12;
20097         }
20098         
20099         if(hours < 10){
20100             hours = '0' + hours;
20101         }
20102         
20103         if(minutes < 10){
20104             minutes = '0' + minutes;
20105         }
20106         
20107         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20108         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20109         this.pop.select('button', true).first().dom.innerHTML = period;
20110         
20111     },
20112     
20113     place: function()
20114     {   
20115         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20116         
20117         var cls = ['bottom'];
20118         
20119         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20120             cls.pop();
20121             cls.push('top');
20122         }
20123         
20124         cls.push('right');
20125         
20126         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20127             cls.pop();
20128             cls.push('left');
20129         }
20130         
20131         this.picker().addClass(cls.join('-'));
20132         
20133         var _this = this;
20134         
20135         Roo.each(cls, function(c){
20136             if(c == 'bottom'){
20137                 _this.picker().setTop(_this.inputEl().getHeight());
20138                 return;
20139             }
20140             if(c == 'top'){
20141                 _this.picker().setTop(0 - _this.picker().getHeight());
20142                 return;
20143             }
20144             
20145             if(c == 'left'){
20146                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20147                 return;
20148             }
20149             if(c == 'right'){
20150                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20151                 return;
20152             }
20153         });
20154         
20155     },
20156   
20157     onFocus : function()
20158     {
20159         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20160         this.show();
20161     },
20162     
20163     onBlur : function()
20164     {
20165         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20166         this.hide();
20167     },
20168     
20169     show : function()
20170     {
20171         this.picker().show();
20172         this.pop.show();
20173         this.update();
20174         this.place();
20175         
20176         this.fireEvent('show', this, this.date);
20177     },
20178     
20179     hide : function()
20180     {
20181         this.picker().hide();
20182         this.pop.hide();
20183         
20184         this.fireEvent('hide', this, this.date);
20185     },
20186     
20187     setTime : function()
20188     {
20189         this.hide();
20190         this.setValue(this.time.format(this.format));
20191         
20192         this.fireEvent('select', this, this.date);
20193         
20194         
20195     },
20196     
20197     onMousedown: function(e){
20198         e.stopPropagation();
20199         e.preventDefault();
20200     },
20201     
20202     onIncrementHours: function()
20203     {
20204         Roo.log('onIncrementHours');
20205         this.time = this.time.add(Date.HOUR, 1);
20206         this.update();
20207         
20208     },
20209     
20210     onDecrementHours: function()
20211     {
20212         Roo.log('onDecrementHours');
20213         this.time = this.time.add(Date.HOUR, -1);
20214         this.update();
20215     },
20216     
20217     onIncrementMinutes: function()
20218     {
20219         Roo.log('onIncrementMinutes');
20220         this.time = this.time.add(Date.MINUTE, 1);
20221         this.update();
20222     },
20223     
20224     onDecrementMinutes: function()
20225     {
20226         Roo.log('onDecrementMinutes');
20227         this.time = this.time.add(Date.MINUTE, -1);
20228         this.update();
20229     },
20230     
20231     onTogglePeriod: function()
20232     {
20233         Roo.log('onTogglePeriod');
20234         this.time = this.time.add(Date.HOUR, 12);
20235         this.update();
20236     }
20237     
20238    
20239 });
20240
20241 Roo.apply(Roo.bootstrap.TimeField,  {
20242     
20243     content : {
20244         tag: 'tbody',
20245         cn: [
20246             {
20247                 tag: 'tr',
20248                 cn: [
20249                 {
20250                     tag: 'td',
20251                     colspan: '7'
20252                 }
20253                 ]
20254             }
20255         ]
20256     },
20257     
20258     footer : {
20259         tag: 'tfoot',
20260         cn: [
20261             {
20262                 tag: 'tr',
20263                 cn: [
20264                 {
20265                     tag: 'th',
20266                     colspan: '7',
20267                     cls: '',
20268                     cn: [
20269                         {
20270                             tag: 'button',
20271                             cls: 'btn btn-info ok',
20272                             html: 'OK'
20273                         }
20274                     ]
20275                 }
20276
20277                 ]
20278             }
20279         ]
20280     }
20281 });
20282
20283 Roo.apply(Roo.bootstrap.TimeField,  {
20284   
20285     template : {
20286         tag: 'div',
20287         cls: 'datepicker dropdown-menu',
20288         cn: [
20289             {
20290                 tag: 'div',
20291                 cls: 'datepicker-time',
20292                 cn: [
20293                 {
20294                     tag: 'table',
20295                     cls: 'table-condensed',
20296                     cn:[
20297                     Roo.bootstrap.TimeField.content,
20298                     Roo.bootstrap.TimeField.footer
20299                     ]
20300                 }
20301                 ]
20302             }
20303         ]
20304     }
20305 });
20306
20307  
20308
20309  /*
20310  * - LGPL
20311  *
20312  * MonthField
20313  * 
20314  */
20315
20316 /**
20317  * @class Roo.bootstrap.MonthField
20318  * @extends Roo.bootstrap.Input
20319  * Bootstrap MonthField class
20320  * 
20321  * @cfg {String} language default en
20322  * 
20323  * @constructor
20324  * Create a new MonthField
20325  * @param {Object} config The config object
20326  */
20327
20328 Roo.bootstrap.MonthField = function(config){
20329     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20330     
20331     this.addEvents({
20332         /**
20333          * @event show
20334          * Fires when this field show.
20335          * @param {Roo.bootstrap.MonthField} this
20336          * @param {Mixed} date The date value
20337          */
20338         show : true,
20339         /**
20340          * @event show
20341          * Fires when this field hide.
20342          * @param {Roo.bootstrap.MonthField} this
20343          * @param {Mixed} date The date value
20344          */
20345         hide : true,
20346         /**
20347          * @event select
20348          * Fires when select a date.
20349          * @param {Roo.bootstrap.MonthField} this
20350          * @param {String} oldvalue The old value
20351          * @param {String} newvalue The new value
20352          */
20353         select : true
20354     });
20355 };
20356
20357 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20358     
20359     onRender: function(ct, position)
20360     {
20361         
20362         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20363         
20364         this.language = this.language || 'en';
20365         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20366         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20367         
20368         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20369         this.isInline = false;
20370         this.isInput = true;
20371         this.component = this.el.select('.add-on', true).first() || false;
20372         this.component = (this.component && this.component.length === 0) ? false : this.component;
20373         this.hasInput = this.component && this.inputEL().length;
20374         
20375         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20376         
20377         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20378         
20379         this.picker().on('mousedown', this.onMousedown, this);
20380         this.picker().on('click', this.onClick, this);
20381         
20382         this.picker().addClass('datepicker-dropdown');
20383         
20384         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20385             v.setStyle('width', '189px');
20386         });
20387         
20388         this.fillMonths();
20389         
20390         this.update();
20391         
20392         if(this.isInline) {
20393             this.show();
20394         }
20395         
20396     },
20397     
20398     setValue: function(v, suppressEvent)
20399     {   
20400         var o = this.getValue();
20401         
20402         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20403         
20404         this.update();
20405
20406         if(suppressEvent !== true){
20407             this.fireEvent('select', this, o, v);
20408         }
20409         
20410     },
20411     
20412     getValue: function()
20413     {
20414         return this.value;
20415     },
20416     
20417     onClick: function(e) 
20418     {
20419         e.stopPropagation();
20420         e.preventDefault();
20421         
20422         var target = e.getTarget();
20423         
20424         if(target.nodeName.toLowerCase() === 'i'){
20425             target = Roo.get(target).dom.parentNode;
20426         }
20427         
20428         var nodeName = target.nodeName;
20429         var className = target.className;
20430         var html = target.innerHTML;
20431         
20432         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20433             return;
20434         }
20435         
20436         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20437         
20438         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20439         
20440         this.hide();
20441                         
20442     },
20443     
20444     picker : function()
20445     {
20446         return this.pickerEl;
20447     },
20448     
20449     fillMonths: function()
20450     {    
20451         var i = 0;
20452         var months = this.picker().select('>.datepicker-months td', true).first();
20453         
20454         months.dom.innerHTML = '';
20455         
20456         while (i < 12) {
20457             var month = {
20458                 tag: 'span',
20459                 cls: 'month',
20460                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20461             };
20462             
20463             months.createChild(month);
20464         }
20465         
20466     },
20467     
20468     update: function()
20469     {
20470         var _this = this;
20471         
20472         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20473             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20474         }
20475         
20476         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20477             e.removeClass('active');
20478             
20479             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20480                 e.addClass('active');
20481             }
20482         })
20483     },
20484     
20485     place: function()
20486     {
20487         if(this.isInline) {
20488             return;
20489         }
20490         
20491         this.picker().removeClass(['bottom', 'top']);
20492         
20493         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20494             /*
20495              * place to the top of element!
20496              *
20497              */
20498             
20499             this.picker().addClass('top');
20500             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20501             
20502             return;
20503         }
20504         
20505         this.picker().addClass('bottom');
20506         
20507         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20508     },
20509     
20510     onFocus : function()
20511     {
20512         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20513         this.show();
20514     },
20515     
20516     onBlur : function()
20517     {
20518         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20519         
20520         var d = this.inputEl().getValue();
20521         
20522         this.setValue(d);
20523                 
20524         this.hide();
20525     },
20526     
20527     show : function()
20528     {
20529         this.picker().show();
20530         this.picker().select('>.datepicker-months', true).first().show();
20531         this.update();
20532         this.place();
20533         
20534         this.fireEvent('show', this, this.date);
20535     },
20536     
20537     hide : function()
20538     {
20539         if(this.isInline) {
20540             return;
20541         }
20542         this.picker().hide();
20543         this.fireEvent('hide', this, this.date);
20544         
20545     },
20546     
20547     onMousedown: function(e)
20548     {
20549         e.stopPropagation();
20550         e.preventDefault();
20551     },
20552     
20553     keyup: function(e)
20554     {
20555         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20556         this.update();
20557     },
20558
20559     fireKey: function(e)
20560     {
20561         if (!this.picker().isVisible()){
20562             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20563                 this.show();
20564             }
20565             return;
20566         }
20567         
20568         var dir;
20569         
20570         switch(e.keyCode){
20571             case 27: // escape
20572                 this.hide();
20573                 e.preventDefault();
20574                 break;
20575             case 37: // left
20576             case 39: // right
20577                 dir = e.keyCode == 37 ? -1 : 1;
20578                 
20579                 this.vIndex = this.vIndex + dir;
20580                 
20581                 if(this.vIndex < 0){
20582                     this.vIndex = 0;
20583                 }
20584                 
20585                 if(this.vIndex > 11){
20586                     this.vIndex = 11;
20587                 }
20588                 
20589                 if(isNaN(this.vIndex)){
20590                     this.vIndex = 0;
20591                 }
20592                 
20593                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20594                 
20595                 break;
20596             case 38: // up
20597             case 40: // down
20598                 
20599                 dir = e.keyCode == 38 ? -1 : 1;
20600                 
20601                 this.vIndex = this.vIndex + dir * 4;
20602                 
20603                 if(this.vIndex < 0){
20604                     this.vIndex = 0;
20605                 }
20606                 
20607                 if(this.vIndex > 11){
20608                     this.vIndex = 11;
20609                 }
20610                 
20611                 if(isNaN(this.vIndex)){
20612                     this.vIndex = 0;
20613                 }
20614                 
20615                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20616                 break;
20617                 
20618             case 13: // enter
20619                 
20620                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20621                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20622                 }
20623                 
20624                 this.hide();
20625                 e.preventDefault();
20626                 break;
20627             case 9: // tab
20628                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20629                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20630                 }
20631                 this.hide();
20632                 break;
20633             case 16: // shift
20634             case 17: // ctrl
20635             case 18: // alt
20636                 break;
20637             default :
20638                 this.hide();
20639                 
20640         }
20641     },
20642     
20643     remove: function() 
20644     {
20645         this.picker().remove();
20646     }
20647    
20648 });
20649
20650 Roo.apply(Roo.bootstrap.MonthField,  {
20651     
20652     content : {
20653         tag: 'tbody',
20654         cn: [
20655         {
20656             tag: 'tr',
20657             cn: [
20658             {
20659                 tag: 'td',
20660                 colspan: '7'
20661             }
20662             ]
20663         }
20664         ]
20665     },
20666     
20667     dates:{
20668         en: {
20669             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20670             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20671         }
20672     }
20673 });
20674
20675 Roo.apply(Roo.bootstrap.MonthField,  {
20676   
20677     template : {
20678         tag: 'div',
20679         cls: 'datepicker dropdown-menu roo-dynamic',
20680         cn: [
20681             {
20682                 tag: 'div',
20683                 cls: 'datepicker-months',
20684                 cn: [
20685                 {
20686                     tag: 'table',
20687                     cls: 'table-condensed',
20688                     cn:[
20689                         Roo.bootstrap.DateField.content
20690                     ]
20691                 }
20692                 ]
20693             }
20694         ]
20695     }
20696 });
20697
20698  
20699
20700  
20701  /*
20702  * - LGPL
20703  *
20704  * CheckBox
20705  * 
20706  */
20707
20708 /**
20709  * @class Roo.bootstrap.CheckBox
20710  * @extends Roo.bootstrap.Input
20711  * Bootstrap CheckBox class
20712  * 
20713  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20714  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20715  * @cfg {String} boxLabel The text that appears beside the checkbox
20716  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20717  * @cfg {Boolean} checked initnal the element
20718  * @cfg {Boolean} inline inline the element (default false)
20719  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20720  * @cfg {String} tooltip label tooltip
20721  * 
20722  * @constructor
20723  * Create a new CheckBox
20724  * @param {Object} config The config object
20725  */
20726
20727 Roo.bootstrap.CheckBox = function(config){
20728     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20729    
20730     this.addEvents({
20731         /**
20732         * @event check
20733         * Fires when the element is checked or unchecked.
20734         * @param {Roo.bootstrap.CheckBox} this This input
20735         * @param {Boolean} checked The new checked value
20736         */
20737        check : true,
20738        /**
20739         * @event click
20740         * Fires when the element is click.
20741         * @param {Roo.bootstrap.CheckBox} this This input
20742         */
20743        click : true
20744     });
20745     
20746 };
20747
20748 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20749   
20750     inputType: 'checkbox',
20751     inputValue: 1,
20752     valueOff: 0,
20753     boxLabel: false,
20754     checked: false,
20755     weight : false,
20756     inline: false,
20757     tooltip : '',
20758     
20759     getAutoCreate : function()
20760     {
20761         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20762         
20763         var id = Roo.id();
20764         
20765         var cfg = {};
20766         
20767         cfg.cls = 'form-group ' + this.inputType; //input-group
20768         
20769         if(this.inline){
20770             cfg.cls += ' ' + this.inputType + '-inline';
20771         }
20772         
20773         var input =  {
20774             tag: 'input',
20775             id : id,
20776             type : this.inputType,
20777             value : this.inputValue,
20778             cls : 'roo-' + this.inputType, //'form-box',
20779             placeholder : this.placeholder || ''
20780             
20781         };
20782         
20783         if(this.inputType != 'radio'){
20784             var hidden =  {
20785                 tag: 'input',
20786                 type : 'hidden',
20787                 cls : 'roo-hidden-value',
20788                 value : this.checked ? this.inputValue : this.valueOff
20789             };
20790         }
20791         
20792             
20793         if (this.weight) { // Validity check?
20794             cfg.cls += " " + this.inputType + "-" + this.weight;
20795         }
20796         
20797         if (this.disabled) {
20798             input.disabled=true;
20799         }
20800         
20801         if(this.checked){
20802             input.checked = this.checked;
20803         }
20804         
20805         if (this.name) {
20806             
20807             input.name = this.name;
20808             
20809             if(this.inputType != 'radio'){
20810                 hidden.name = this.name;
20811                 input.name = '_hidden_' + this.name;
20812             }
20813         }
20814         
20815         if (this.size) {
20816             input.cls += ' input-' + this.size;
20817         }
20818         
20819         var settings=this;
20820         
20821         ['xs','sm','md','lg'].map(function(size){
20822             if (settings[size]) {
20823                 cfg.cls += ' col-' + size + '-' + settings[size];
20824             }
20825         });
20826         
20827         var inputblock = input;
20828          
20829         if (this.before || this.after) {
20830             
20831             inputblock = {
20832                 cls : 'input-group',
20833                 cn :  [] 
20834             };
20835             
20836             if (this.before) {
20837                 inputblock.cn.push({
20838                     tag :'span',
20839                     cls : 'input-group-addon',
20840                     html : this.before
20841                 });
20842             }
20843             
20844             inputblock.cn.push(input);
20845             
20846             if(this.inputType != 'radio'){
20847                 inputblock.cn.push(hidden);
20848             }
20849             
20850             if (this.after) {
20851                 inputblock.cn.push({
20852                     tag :'span',
20853                     cls : 'input-group-addon',
20854                     html : this.after
20855                 });
20856             }
20857             
20858         }
20859         
20860         if (align ==='left' && this.fieldLabel.length) {
20861 //                Roo.log("left and has label");
20862             cfg.cn = [
20863                 {
20864                     tag: 'label',
20865                     'for' :  id,
20866                     cls : 'control-label',
20867                     html : this.fieldLabel
20868                 },
20869                 {
20870                     cls : "", 
20871                     cn: [
20872                         inputblock
20873                     ]
20874                 }
20875             ];
20876             
20877             if(this.labelWidth > 12){
20878                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20879             }
20880             
20881             if(this.labelWidth < 13 && this.labelmd == 0){
20882                 this.labelmd = this.labelWidth;
20883             }
20884             
20885             if(this.labellg > 0){
20886                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20887                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20888             }
20889             
20890             if(this.labelmd > 0){
20891                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20892                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20893             }
20894             
20895             if(this.labelsm > 0){
20896                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20897                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20898             }
20899             
20900             if(this.labelxs > 0){
20901                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20902                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20903             }
20904             
20905         } else if ( this.fieldLabel.length) {
20906 //                Roo.log(" label");
20907                 cfg.cn = [
20908                    
20909                     {
20910                         tag: this.boxLabel ? 'span' : 'label',
20911                         'for': id,
20912                         cls: 'control-label box-input-label',
20913                         //cls : 'input-group-addon',
20914                         html : this.fieldLabel
20915                     },
20916                     
20917                     inputblock
20918                     
20919                 ];
20920
20921         } else {
20922             
20923 //                Roo.log(" no label && no align");
20924                 cfg.cn = [  inputblock ] ;
20925                 
20926                 
20927         }
20928         
20929         if(this.boxLabel){
20930              var boxLabelCfg = {
20931                 tag: 'label',
20932                 //'for': id, // box label is handled by onclick - so no for...
20933                 cls: 'box-label',
20934                 html: this.boxLabel
20935             };
20936             
20937             if(this.tooltip){
20938                 boxLabelCfg.tooltip = this.tooltip;
20939             }
20940              
20941             cfg.cn.push(boxLabelCfg);
20942         }
20943         
20944         if(this.inputType != 'radio'){
20945             cfg.cn.push(hidden);
20946         }
20947         
20948         return cfg;
20949         
20950     },
20951     
20952     /**
20953      * return the real input element.
20954      */
20955     inputEl: function ()
20956     {
20957         return this.el.select('input.roo-' + this.inputType,true).first();
20958     },
20959     hiddenEl: function ()
20960     {
20961         return this.el.select('input.roo-hidden-value',true).first();
20962     },
20963     
20964     labelEl: function()
20965     {
20966         return this.el.select('label.control-label',true).first();
20967     },
20968     /* depricated... */
20969     
20970     label: function()
20971     {
20972         return this.labelEl();
20973     },
20974     
20975     boxLabelEl: function()
20976     {
20977         return this.el.select('label.box-label',true).first();
20978     },
20979     
20980     initEvents : function()
20981     {
20982 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20983         
20984         this.inputEl().on('click', this.onClick,  this);
20985         
20986         if (this.boxLabel) { 
20987             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20988         }
20989         
20990         this.startValue = this.getValue();
20991         
20992         if(this.groupId){
20993             Roo.bootstrap.CheckBox.register(this);
20994         }
20995     },
20996     
20997     onClick : function(e)
20998     {   
20999         if(this.fireEvent('click', this, e) !== false){
21000             this.setChecked(!this.checked);
21001         }
21002         
21003     },
21004     
21005     setChecked : function(state,suppressEvent)
21006     {
21007         this.startValue = this.getValue();
21008
21009         if(this.inputType == 'radio'){
21010             
21011             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21012                 e.dom.checked = false;
21013             });
21014             
21015             this.inputEl().dom.checked = true;
21016             
21017             this.inputEl().dom.value = this.inputValue;
21018             
21019             if(suppressEvent !== true){
21020                 this.fireEvent('check', this, true);
21021             }
21022             
21023             this.validate();
21024             
21025             return;
21026         }
21027         
21028         this.checked = state;
21029         
21030         this.inputEl().dom.checked = state;
21031         
21032         
21033         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21034         
21035         if(suppressEvent !== true){
21036             this.fireEvent('check', this, state);
21037         }
21038         
21039         this.validate();
21040     },
21041     
21042     getValue : function()
21043     {
21044         if(this.inputType == 'radio'){
21045             return this.getGroupValue();
21046         }
21047         
21048         return this.hiddenEl().dom.value;
21049         
21050     },
21051     
21052     getGroupValue : function()
21053     {
21054         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21055             return '';
21056         }
21057         
21058         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21059     },
21060     
21061     setValue : function(v,suppressEvent)
21062     {
21063         if(this.inputType == 'radio'){
21064             this.setGroupValue(v, suppressEvent);
21065             return;
21066         }
21067         
21068         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21069         
21070         this.validate();
21071     },
21072     
21073     setGroupValue : function(v, suppressEvent)
21074     {
21075         this.startValue = this.getValue();
21076         
21077         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21078             e.dom.checked = false;
21079             
21080             if(e.dom.value == v){
21081                 e.dom.checked = true;
21082             }
21083         });
21084         
21085         if(suppressEvent !== true){
21086             this.fireEvent('check', this, true);
21087         }
21088
21089         this.validate();
21090         
21091         return;
21092     },
21093     
21094     validate : function()
21095     {
21096         if(this.getVisibilityEl().hasClass('hidden')){
21097             return true;
21098         }
21099         
21100         if(
21101                 this.disabled || 
21102                 (this.inputType == 'radio' && this.validateRadio()) ||
21103                 (this.inputType == 'checkbox' && this.validateCheckbox())
21104         ){
21105             this.markValid();
21106             return true;
21107         }
21108         
21109         this.markInvalid();
21110         return false;
21111     },
21112     
21113     validateRadio : function()
21114     {
21115         if(this.getVisibilityEl().hasClass('hidden')){
21116             return true;
21117         }
21118         
21119         if(this.allowBlank){
21120             return true;
21121         }
21122         
21123         var valid = false;
21124         
21125         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21126             if(!e.dom.checked){
21127                 return;
21128             }
21129             
21130             valid = true;
21131             
21132             return false;
21133         });
21134         
21135         return valid;
21136     },
21137     
21138     validateCheckbox : function()
21139     {
21140         if(!this.groupId){
21141             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21142             //return (this.getValue() == this.inputValue) ? true : false;
21143         }
21144         
21145         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21146         
21147         if(!group){
21148             return false;
21149         }
21150         
21151         var r = false;
21152         
21153         for(var i in group){
21154             if(group[i].el.isVisible(true)){
21155                 r = false;
21156                 break;
21157             }
21158             
21159             r = true;
21160         }
21161         
21162         for(var i in group){
21163             if(r){
21164                 break;
21165             }
21166             
21167             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21168         }
21169         
21170         return r;
21171     },
21172     
21173     /**
21174      * Mark this field as valid
21175      */
21176     markValid : function()
21177     {
21178         var _this = this;
21179         
21180         this.fireEvent('valid', this);
21181         
21182         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21183         
21184         if(this.groupId){
21185             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21186         }
21187         
21188         if(label){
21189             label.markValid();
21190         }
21191
21192         if(this.inputType == 'radio'){
21193             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21194                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21195                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21196             });
21197             
21198             return;
21199         }
21200
21201         if(!this.groupId){
21202             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21203             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21204             return;
21205         }
21206         
21207         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21208         
21209         if(!group){
21210             return;
21211         }
21212         
21213         for(var i in group){
21214             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21215             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21216         }
21217     },
21218     
21219      /**
21220      * Mark this field as invalid
21221      * @param {String} msg The validation message
21222      */
21223     markInvalid : function(msg)
21224     {
21225         if(this.allowBlank){
21226             return;
21227         }
21228         
21229         var _this = this;
21230         
21231         this.fireEvent('invalid', this, msg);
21232         
21233         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21234         
21235         if(this.groupId){
21236             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21237         }
21238         
21239         if(label){
21240             label.markInvalid();
21241         }
21242             
21243         if(this.inputType == 'radio'){
21244             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21245                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21246                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21247             });
21248             
21249             return;
21250         }
21251         
21252         if(!this.groupId){
21253             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21254             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21255             return;
21256         }
21257         
21258         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21259         
21260         if(!group){
21261             return;
21262         }
21263         
21264         for(var i in group){
21265             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21266             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21267         }
21268         
21269     },
21270     
21271     clearInvalid : function()
21272     {
21273         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21274         
21275         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21276         
21277         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21278         
21279         if (label && label.iconEl) {
21280             label.iconEl.removeClass(label.validClass);
21281             label.iconEl.removeClass(label.invalidClass);
21282         }
21283     },
21284     
21285     disable : function()
21286     {
21287         if(this.inputType != 'radio'){
21288             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21289             return;
21290         }
21291         
21292         var _this = this;
21293         
21294         if(this.rendered){
21295             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21296                 _this.getActionEl().addClass(this.disabledClass);
21297                 e.dom.disabled = true;
21298             });
21299         }
21300         
21301         this.disabled = true;
21302         this.fireEvent("disable", this);
21303         return this;
21304     },
21305
21306     enable : function()
21307     {
21308         if(this.inputType != 'radio'){
21309             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21310             return;
21311         }
21312         
21313         var _this = this;
21314         
21315         if(this.rendered){
21316             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21317                 _this.getActionEl().removeClass(this.disabledClass);
21318                 e.dom.disabled = false;
21319             });
21320         }
21321         
21322         this.disabled = false;
21323         this.fireEvent("enable", this);
21324         return this;
21325     },
21326     
21327     setBoxLabel : function(v)
21328     {
21329         this.boxLabel = v;
21330         
21331         if(this.rendered){
21332             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21333         }
21334     }
21335
21336 });
21337
21338 Roo.apply(Roo.bootstrap.CheckBox, {
21339     
21340     groups: {},
21341     
21342      /**
21343     * register a CheckBox Group
21344     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21345     */
21346     register : function(checkbox)
21347     {
21348         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21349             this.groups[checkbox.groupId] = {};
21350         }
21351         
21352         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21353             return;
21354         }
21355         
21356         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21357         
21358     },
21359     /**
21360     * fetch a CheckBox Group based on the group ID
21361     * @param {string} the group ID
21362     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21363     */
21364     get: function(groupId) {
21365         if (typeof(this.groups[groupId]) == 'undefined') {
21366             return false;
21367         }
21368         
21369         return this.groups[groupId] ;
21370     }
21371     
21372     
21373 });
21374 /*
21375  * - LGPL
21376  *
21377  * RadioItem
21378  * 
21379  */
21380
21381 /**
21382  * @class Roo.bootstrap.Radio
21383  * @extends Roo.bootstrap.Component
21384  * Bootstrap Radio class
21385  * @cfg {String} boxLabel - the label associated
21386  * @cfg {String} value - the value of radio
21387  * 
21388  * @constructor
21389  * Create a new Radio
21390  * @param {Object} config The config object
21391  */
21392 Roo.bootstrap.Radio = function(config){
21393     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21394     
21395 };
21396
21397 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21398     
21399     boxLabel : '',
21400     
21401     value : '',
21402     
21403     getAutoCreate : function()
21404     {
21405         var cfg = {
21406             tag : 'div',
21407             cls : 'form-group radio',
21408             cn : [
21409                 {
21410                     tag : 'label',
21411                     cls : 'box-label',
21412                     html : this.boxLabel
21413                 }
21414             ]
21415         };
21416         
21417         return cfg;
21418     },
21419     
21420     initEvents : function() 
21421     {
21422         this.parent().register(this);
21423         
21424         this.el.on('click', this.onClick, this);
21425         
21426     },
21427     
21428     onClick : function(e)
21429     {
21430         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21431             this.setChecked(true);
21432         }
21433     },
21434     
21435     setChecked : function(state, suppressEvent)
21436     {
21437         this.parent().setValue(this.value, suppressEvent);
21438         
21439     },
21440     
21441     setBoxLabel : function(v)
21442     {
21443         this.boxLabel = v;
21444         
21445         if(this.rendered){
21446             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21447         }
21448     }
21449     
21450 });
21451  
21452
21453  /*
21454  * - LGPL
21455  *
21456  * Input
21457  * 
21458  */
21459
21460 /**
21461  * @class Roo.bootstrap.SecurePass
21462  * @extends Roo.bootstrap.Input
21463  * Bootstrap SecurePass class
21464  *
21465  * 
21466  * @constructor
21467  * Create a new SecurePass
21468  * @param {Object} config The config object
21469  */
21470  
21471 Roo.bootstrap.SecurePass = function (config) {
21472     // these go here, so the translation tool can replace them..
21473     this.errors = {
21474         PwdEmpty: "Please type a password, and then retype it to confirm.",
21475         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21476         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21477         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21478         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21479         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21480         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21481         TooWeak: "Your password is Too Weak."
21482     },
21483     this.meterLabel = "Password strength:";
21484     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21485     this.meterClass = [
21486         "roo-password-meter-tooweak", 
21487         "roo-password-meter-weak", 
21488         "roo-password-meter-medium", 
21489         "roo-password-meter-strong", 
21490         "roo-password-meter-grey"
21491     ];
21492     
21493     this.errors = {};
21494     
21495     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21496 }
21497
21498 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21499     /**
21500      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21501      * {
21502      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21503      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21504      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21505      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21506      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21507      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21508      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21509      * })
21510      */
21511     // private
21512     
21513     meterWidth: 300,
21514     errorMsg :'',    
21515     errors: false,
21516     imageRoot: '/',
21517     /**
21518      * @cfg {String/Object} Label for the strength meter (defaults to
21519      * 'Password strength:')
21520      */
21521     // private
21522     meterLabel: '',
21523     /**
21524      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21525      * ['Weak', 'Medium', 'Strong'])
21526      */
21527     // private    
21528     pwdStrengths: false,    
21529     // private
21530     strength: 0,
21531     // private
21532     _lastPwd: null,
21533     // private
21534     kCapitalLetter: 0,
21535     kSmallLetter: 1,
21536     kDigit: 2,
21537     kPunctuation: 3,
21538     
21539     insecure: false,
21540     // private
21541     initEvents: function ()
21542     {
21543         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21544
21545         if (this.el.is('input[type=password]') && Roo.isSafari) {
21546             this.el.on('keydown', this.SafariOnKeyDown, this);
21547         }
21548
21549         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21550     },
21551     // private
21552     onRender: function (ct, position)
21553     {
21554         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21555         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21556         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21557
21558         this.trigger.createChild({
21559                    cn: [
21560                     {
21561                     //id: 'PwdMeter',
21562                     tag: 'div',
21563                     cls: 'roo-password-meter-grey col-xs-12',
21564                     style: {
21565                         //width: 0,
21566                         //width: this.meterWidth + 'px'                                                
21567                         }
21568                     },
21569                     {                            
21570                          cls: 'roo-password-meter-text'                          
21571                     }
21572                 ]            
21573         });
21574
21575          
21576         if (this.hideTrigger) {
21577             this.trigger.setDisplayed(false);
21578         }
21579         this.setSize(this.width || '', this.height || '');
21580     },
21581     // private
21582     onDestroy: function ()
21583     {
21584         if (this.trigger) {
21585             this.trigger.removeAllListeners();
21586             this.trigger.remove();
21587         }
21588         if (this.wrap) {
21589             this.wrap.remove();
21590         }
21591         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21592     },
21593     // private
21594     checkStrength: function ()
21595     {
21596         var pwd = this.inputEl().getValue();
21597         if (pwd == this._lastPwd) {
21598             return;
21599         }
21600
21601         var strength;
21602         if (this.ClientSideStrongPassword(pwd)) {
21603             strength = 3;
21604         } else if (this.ClientSideMediumPassword(pwd)) {
21605             strength = 2;
21606         } else if (this.ClientSideWeakPassword(pwd)) {
21607             strength = 1;
21608         } else {
21609             strength = 0;
21610         }
21611         
21612         Roo.log('strength1: ' + strength);
21613         
21614         //var pm = this.trigger.child('div/div/div').dom;
21615         var pm = this.trigger.child('div/div');
21616         pm.removeClass(this.meterClass);
21617         pm.addClass(this.meterClass[strength]);
21618                 
21619         
21620         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21621                 
21622         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21623         
21624         this._lastPwd = pwd;
21625     },
21626     reset: function ()
21627     {
21628         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21629         
21630         this._lastPwd = '';
21631         
21632         var pm = this.trigger.child('div/div');
21633         pm.removeClass(this.meterClass);
21634         pm.addClass('roo-password-meter-grey');        
21635         
21636         
21637         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21638         
21639         pt.innerHTML = '';
21640         this.inputEl().dom.type='password';
21641     },
21642     // private
21643     validateValue: function (value)
21644     {
21645         
21646         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21647             return false;
21648         }
21649         if (value.length == 0) {
21650             if (this.allowBlank) {
21651                 this.clearInvalid();
21652                 return true;
21653             }
21654
21655             this.markInvalid(this.errors.PwdEmpty);
21656             this.errorMsg = this.errors.PwdEmpty;
21657             return false;
21658         }
21659         
21660         if(this.insecure){
21661             return true;
21662         }
21663         
21664         if ('[\x21-\x7e]*'.match(value)) {
21665             this.markInvalid(this.errors.PwdBadChar);
21666             this.errorMsg = this.errors.PwdBadChar;
21667             return false;
21668         }
21669         if (value.length < 6) {
21670             this.markInvalid(this.errors.PwdShort);
21671             this.errorMsg = this.errors.PwdShort;
21672             return false;
21673         }
21674         if (value.length > 16) {
21675             this.markInvalid(this.errors.PwdLong);
21676             this.errorMsg = this.errors.PwdLong;
21677             return false;
21678         }
21679         var strength;
21680         if (this.ClientSideStrongPassword(value)) {
21681             strength = 3;
21682         } else if (this.ClientSideMediumPassword(value)) {
21683             strength = 2;
21684         } else if (this.ClientSideWeakPassword(value)) {
21685             strength = 1;
21686         } else {
21687             strength = 0;
21688         }
21689
21690         
21691         if (strength < 2) {
21692             //this.markInvalid(this.errors.TooWeak);
21693             this.errorMsg = this.errors.TooWeak;
21694             //return false;
21695         }
21696         
21697         
21698         console.log('strength2: ' + strength);
21699         
21700         //var pm = this.trigger.child('div/div/div').dom;
21701         
21702         var pm = this.trigger.child('div/div');
21703         pm.removeClass(this.meterClass);
21704         pm.addClass(this.meterClass[strength]);
21705                 
21706         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21707                 
21708         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21709         
21710         this.errorMsg = ''; 
21711         return true;
21712     },
21713     // private
21714     CharacterSetChecks: function (type)
21715     {
21716         this.type = type;
21717         this.fResult = false;
21718     },
21719     // private
21720     isctype: function (character, type)
21721     {
21722         switch (type) {  
21723             case this.kCapitalLetter:
21724                 if (character >= 'A' && character <= 'Z') {
21725                     return true;
21726                 }
21727                 break;
21728             
21729             case this.kSmallLetter:
21730                 if (character >= 'a' && character <= 'z') {
21731                     return true;
21732                 }
21733                 break;
21734             
21735             case this.kDigit:
21736                 if (character >= '0' && character <= '9') {
21737                     return true;
21738                 }
21739                 break;
21740             
21741             case this.kPunctuation:
21742                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21743                     return true;
21744                 }
21745                 break;
21746             
21747             default:
21748                 return false;
21749         }
21750
21751     },
21752     // private
21753     IsLongEnough: function (pwd, size)
21754     {
21755         return !(pwd == null || isNaN(size) || pwd.length < size);
21756     },
21757     // private
21758     SpansEnoughCharacterSets: function (word, nb)
21759     {
21760         if (!this.IsLongEnough(word, nb))
21761         {
21762             return false;
21763         }
21764
21765         var characterSetChecks = new Array(
21766             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21767             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21768         );
21769         
21770         for (var index = 0; index < word.length; ++index) {
21771             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21772                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21773                     characterSetChecks[nCharSet].fResult = true;
21774                     break;
21775                 }
21776             }
21777         }
21778
21779         var nCharSets = 0;
21780         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21781             if (characterSetChecks[nCharSet].fResult) {
21782                 ++nCharSets;
21783             }
21784         }
21785
21786         if (nCharSets < nb) {
21787             return false;
21788         }
21789         return true;
21790     },
21791     // private
21792     ClientSideStrongPassword: function (pwd)
21793     {
21794         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21795     },
21796     // private
21797     ClientSideMediumPassword: function (pwd)
21798     {
21799         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21800     },
21801     // private
21802     ClientSideWeakPassword: function (pwd)
21803     {
21804         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21805     }
21806           
21807 })//<script type="text/javascript">
21808
21809 /*
21810  * Based  Ext JS Library 1.1.1
21811  * Copyright(c) 2006-2007, Ext JS, LLC.
21812  * LGPL
21813  *
21814  */
21815  
21816 /**
21817  * @class Roo.HtmlEditorCore
21818  * @extends Roo.Component
21819  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21820  *
21821  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21822  */
21823
21824 Roo.HtmlEditorCore = function(config){
21825     
21826     
21827     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21828     
21829     
21830     this.addEvents({
21831         /**
21832          * @event initialize
21833          * Fires when the editor is fully initialized (including the iframe)
21834          * @param {Roo.HtmlEditorCore} this
21835          */
21836         initialize: true,
21837         /**
21838          * @event activate
21839          * Fires when the editor is first receives the focus. Any insertion must wait
21840          * until after this event.
21841          * @param {Roo.HtmlEditorCore} this
21842          */
21843         activate: true,
21844          /**
21845          * @event beforesync
21846          * Fires before the textarea is updated with content from the editor iframe. Return false
21847          * to cancel the sync.
21848          * @param {Roo.HtmlEditorCore} this
21849          * @param {String} html
21850          */
21851         beforesync: true,
21852          /**
21853          * @event beforepush
21854          * Fires before the iframe editor is updated with content from the textarea. Return false
21855          * to cancel the push.
21856          * @param {Roo.HtmlEditorCore} this
21857          * @param {String} html
21858          */
21859         beforepush: true,
21860          /**
21861          * @event sync
21862          * Fires when the textarea is updated with content from the editor iframe.
21863          * @param {Roo.HtmlEditorCore} this
21864          * @param {String} html
21865          */
21866         sync: true,
21867          /**
21868          * @event push
21869          * Fires when the iframe editor is updated with content from the textarea.
21870          * @param {Roo.HtmlEditorCore} this
21871          * @param {String} html
21872          */
21873         push: true,
21874         
21875         /**
21876          * @event editorevent
21877          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21878          * @param {Roo.HtmlEditorCore} this
21879          */
21880         editorevent: true
21881         
21882     });
21883     
21884     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21885     
21886     // defaults : white / black...
21887     this.applyBlacklists();
21888     
21889     
21890     
21891 };
21892
21893
21894 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21895
21896
21897      /**
21898      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21899      */
21900     
21901     owner : false,
21902     
21903      /**
21904      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21905      *                        Roo.resizable.
21906      */
21907     resizable : false,
21908      /**
21909      * @cfg {Number} height (in pixels)
21910      */   
21911     height: 300,
21912    /**
21913      * @cfg {Number} width (in pixels)
21914      */   
21915     width: 500,
21916     
21917     /**
21918      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21919      * 
21920      */
21921     stylesheets: false,
21922     
21923     // id of frame..
21924     frameId: false,
21925     
21926     // private properties
21927     validationEvent : false,
21928     deferHeight: true,
21929     initialized : false,
21930     activated : false,
21931     sourceEditMode : false,
21932     onFocus : Roo.emptyFn,
21933     iframePad:3,
21934     hideMode:'offsets',
21935     
21936     clearUp: true,
21937     
21938     // blacklist + whitelisted elements..
21939     black: false,
21940     white: false,
21941      
21942     bodyCls : '',
21943
21944     /**
21945      * Protected method that will not generally be called directly. It
21946      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21947      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21948      */
21949     getDocMarkup : function(){
21950         // body styles..
21951         var st = '';
21952         
21953         // inherit styels from page...?? 
21954         if (this.stylesheets === false) {
21955             
21956             Roo.get(document.head).select('style').each(function(node) {
21957                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21958             });
21959             
21960             Roo.get(document.head).select('link').each(function(node) { 
21961                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21962             });
21963             
21964         } else if (!this.stylesheets.length) {
21965                 // simple..
21966                 st = '<style type="text/css">' +
21967                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21968                    '</style>';
21969         } else { 
21970             st = '<style type="text/css">' +
21971                     this.stylesheets +
21972                 '</style>';
21973         }
21974         
21975         st +=  '<style type="text/css">' +
21976             'IMG { cursor: pointer } ' +
21977         '</style>';
21978
21979         var cls = 'roo-htmleditor-body';
21980         
21981         if(this.bodyCls.length){
21982             cls += ' ' + this.bodyCls;
21983         }
21984         
21985         return '<html><head>' + st  +
21986             //<style type="text/css">' +
21987             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21988             //'</style>' +
21989             ' </head><body class="' +  cls + '"></body></html>';
21990     },
21991
21992     // private
21993     onRender : function(ct, position)
21994     {
21995         var _t = this;
21996         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21997         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21998         
21999         
22000         this.el.dom.style.border = '0 none';
22001         this.el.dom.setAttribute('tabIndex', -1);
22002         this.el.addClass('x-hidden hide');
22003         
22004         
22005         
22006         if(Roo.isIE){ // fix IE 1px bogus margin
22007             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22008         }
22009        
22010         
22011         this.frameId = Roo.id();
22012         
22013          
22014         
22015         var iframe = this.owner.wrap.createChild({
22016             tag: 'iframe',
22017             cls: 'form-control', // bootstrap..
22018             id: this.frameId,
22019             name: this.frameId,
22020             frameBorder : 'no',
22021             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22022         }, this.el
22023         );
22024         
22025         
22026         this.iframe = iframe.dom;
22027
22028          this.assignDocWin();
22029         
22030         this.doc.designMode = 'on';
22031        
22032         this.doc.open();
22033         this.doc.write(this.getDocMarkup());
22034         this.doc.close();
22035
22036         
22037         var task = { // must defer to wait for browser to be ready
22038             run : function(){
22039                 //console.log("run task?" + this.doc.readyState);
22040                 this.assignDocWin();
22041                 if(this.doc.body || this.doc.readyState == 'complete'){
22042                     try {
22043                         this.doc.designMode="on";
22044                     } catch (e) {
22045                         return;
22046                     }
22047                     Roo.TaskMgr.stop(task);
22048                     this.initEditor.defer(10, this);
22049                 }
22050             },
22051             interval : 10,
22052             duration: 10000,
22053             scope: this
22054         };
22055         Roo.TaskMgr.start(task);
22056
22057     },
22058
22059     // private
22060     onResize : function(w, h)
22061     {
22062          Roo.log('resize: ' +w + ',' + h );
22063         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22064         if(!this.iframe){
22065             return;
22066         }
22067         if(typeof w == 'number'){
22068             
22069             this.iframe.style.width = w + 'px';
22070         }
22071         if(typeof h == 'number'){
22072             
22073             this.iframe.style.height = h + 'px';
22074             if(this.doc){
22075                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22076             }
22077         }
22078         
22079     },
22080
22081     /**
22082      * Toggles the editor between standard and source edit mode.
22083      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22084      */
22085     toggleSourceEdit : function(sourceEditMode){
22086         
22087         this.sourceEditMode = sourceEditMode === true;
22088         
22089         if(this.sourceEditMode){
22090  
22091             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22092             
22093         }else{
22094             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22095             //this.iframe.className = '';
22096             this.deferFocus();
22097         }
22098         //this.setSize(this.owner.wrap.getSize());
22099         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22100     },
22101
22102     
22103   
22104
22105     /**
22106      * Protected method that will not generally be called directly. If you need/want
22107      * custom HTML cleanup, this is the method you should override.
22108      * @param {String} html The HTML to be cleaned
22109      * return {String} The cleaned HTML
22110      */
22111     cleanHtml : function(html){
22112         html = String(html);
22113         if(html.length > 5){
22114             if(Roo.isSafari){ // strip safari nonsense
22115                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22116             }
22117         }
22118         if(html == '&nbsp;'){
22119             html = '';
22120         }
22121         return html;
22122     },
22123
22124     /**
22125      * HTML Editor -> Textarea
22126      * Protected method that will not generally be called directly. Syncs the contents
22127      * of the editor iframe with the textarea.
22128      */
22129     syncValue : function(){
22130         if(this.initialized){
22131             var bd = (this.doc.body || this.doc.documentElement);
22132             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22133             var html = bd.innerHTML;
22134             if(Roo.isSafari){
22135                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22136                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22137                 if(m && m[1]){
22138                     html = '<div style="'+m[0]+'">' + html + '</div>';
22139                 }
22140             }
22141             html = this.cleanHtml(html);
22142             // fix up the special chars.. normaly like back quotes in word...
22143             // however we do not want to do this with chinese..
22144             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22145                 var cc = b.charCodeAt();
22146                 if (
22147                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22148                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22149                     (cc >= 0xf900 && cc < 0xfb00 )
22150                 ) {
22151                         return b;
22152                 }
22153                 return "&#"+cc+";" 
22154             });
22155             if(this.owner.fireEvent('beforesync', this, html) !== false){
22156                 this.el.dom.value = html;
22157                 this.owner.fireEvent('sync', this, html);
22158             }
22159         }
22160     },
22161
22162     /**
22163      * Protected method that will not generally be called directly. Pushes the value of the textarea
22164      * into the iframe editor.
22165      */
22166     pushValue : function(){
22167         if(this.initialized){
22168             var v = this.el.dom.value.trim();
22169             
22170 //            if(v.length < 1){
22171 //                v = '&#160;';
22172 //            }
22173             
22174             if(this.owner.fireEvent('beforepush', this, v) !== false){
22175                 var d = (this.doc.body || this.doc.documentElement);
22176                 d.innerHTML = v;
22177                 this.cleanUpPaste();
22178                 this.el.dom.value = d.innerHTML;
22179                 this.owner.fireEvent('push', this, v);
22180             }
22181         }
22182     },
22183
22184     // private
22185     deferFocus : function(){
22186         this.focus.defer(10, this);
22187     },
22188
22189     // doc'ed in Field
22190     focus : function(){
22191         if(this.win && !this.sourceEditMode){
22192             this.win.focus();
22193         }else{
22194             this.el.focus();
22195         }
22196     },
22197     
22198     assignDocWin: function()
22199     {
22200         var iframe = this.iframe;
22201         
22202          if(Roo.isIE){
22203             this.doc = iframe.contentWindow.document;
22204             this.win = iframe.contentWindow;
22205         } else {
22206 //            if (!Roo.get(this.frameId)) {
22207 //                return;
22208 //            }
22209 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22210 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22211             
22212             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22213                 return;
22214             }
22215             
22216             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22217             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22218         }
22219     },
22220     
22221     // private
22222     initEditor : function(){
22223         //console.log("INIT EDITOR");
22224         this.assignDocWin();
22225         
22226         
22227         
22228         this.doc.designMode="on";
22229         this.doc.open();
22230         this.doc.write(this.getDocMarkup());
22231         this.doc.close();
22232         
22233         var dbody = (this.doc.body || this.doc.documentElement);
22234         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22235         // this copies styles from the containing element into thsi one..
22236         // not sure why we need all of this..
22237         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22238         
22239         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22240         //ss['background-attachment'] = 'fixed'; // w3c
22241         dbody.bgProperties = 'fixed'; // ie
22242         //Roo.DomHelper.applyStyles(dbody, ss);
22243         Roo.EventManager.on(this.doc, {
22244             //'mousedown': this.onEditorEvent,
22245             'mouseup': this.onEditorEvent,
22246             'dblclick': this.onEditorEvent,
22247             'click': this.onEditorEvent,
22248             'keyup': this.onEditorEvent,
22249             buffer:100,
22250             scope: this
22251         });
22252         if(Roo.isGecko){
22253             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22254         }
22255         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22256             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22257         }
22258         this.initialized = true;
22259
22260         this.owner.fireEvent('initialize', this);
22261         this.pushValue();
22262     },
22263
22264     // private
22265     onDestroy : function(){
22266         
22267         
22268         
22269         if(this.rendered){
22270             
22271             //for (var i =0; i < this.toolbars.length;i++) {
22272             //    // fixme - ask toolbars for heights?
22273             //    this.toolbars[i].onDestroy();
22274            // }
22275             
22276             //this.wrap.dom.innerHTML = '';
22277             //this.wrap.remove();
22278         }
22279     },
22280
22281     // private
22282     onFirstFocus : function(){
22283         
22284         this.assignDocWin();
22285         
22286         
22287         this.activated = true;
22288          
22289     
22290         if(Roo.isGecko){ // prevent silly gecko errors
22291             this.win.focus();
22292             var s = this.win.getSelection();
22293             if(!s.focusNode || s.focusNode.nodeType != 3){
22294                 var r = s.getRangeAt(0);
22295                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22296                 r.collapse(true);
22297                 this.deferFocus();
22298             }
22299             try{
22300                 this.execCmd('useCSS', true);
22301                 this.execCmd('styleWithCSS', false);
22302             }catch(e){}
22303         }
22304         this.owner.fireEvent('activate', this);
22305     },
22306
22307     // private
22308     adjustFont: function(btn){
22309         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22310         //if(Roo.isSafari){ // safari
22311         //    adjust *= 2;
22312        // }
22313         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22314         if(Roo.isSafari){ // safari
22315             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22316             v =  (v < 10) ? 10 : v;
22317             v =  (v > 48) ? 48 : v;
22318             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22319             
22320         }
22321         
22322         
22323         v = Math.max(1, v+adjust);
22324         
22325         this.execCmd('FontSize', v  );
22326     },
22327
22328     onEditorEvent : function(e)
22329     {
22330         this.owner.fireEvent('editorevent', this, e);
22331       //  this.updateToolbar();
22332         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22333     },
22334
22335     insertTag : function(tg)
22336     {
22337         // could be a bit smarter... -> wrap the current selected tRoo..
22338         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22339             
22340             range = this.createRange(this.getSelection());
22341             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22342             wrappingNode.appendChild(range.extractContents());
22343             range.insertNode(wrappingNode);
22344
22345             return;
22346             
22347             
22348             
22349         }
22350         this.execCmd("formatblock",   tg);
22351         
22352     },
22353     
22354     insertText : function(txt)
22355     {
22356         
22357         
22358         var range = this.createRange();
22359         range.deleteContents();
22360                //alert(Sender.getAttribute('label'));
22361                
22362         range.insertNode(this.doc.createTextNode(txt));
22363     } ,
22364     
22365      
22366
22367     /**
22368      * Executes a Midas editor command on the editor document and performs necessary focus and
22369      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22370      * @param {String} cmd The Midas command
22371      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22372      */
22373     relayCmd : function(cmd, value){
22374         this.win.focus();
22375         this.execCmd(cmd, value);
22376         this.owner.fireEvent('editorevent', this);
22377         //this.updateToolbar();
22378         this.owner.deferFocus();
22379     },
22380
22381     /**
22382      * Executes a Midas editor command directly on the editor document.
22383      * For visual commands, you should use {@link #relayCmd} instead.
22384      * <b>This should only be called after the editor is initialized.</b>
22385      * @param {String} cmd The Midas command
22386      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22387      */
22388     execCmd : function(cmd, value){
22389         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22390         this.syncValue();
22391     },
22392  
22393  
22394    
22395     /**
22396      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22397      * to insert tRoo.
22398      * @param {String} text | dom node.. 
22399      */
22400     insertAtCursor : function(text)
22401     {
22402         
22403         if(!this.activated){
22404             return;
22405         }
22406         /*
22407         if(Roo.isIE){
22408             this.win.focus();
22409             var r = this.doc.selection.createRange();
22410             if(r){
22411                 r.collapse(true);
22412                 r.pasteHTML(text);
22413                 this.syncValue();
22414                 this.deferFocus();
22415             
22416             }
22417             return;
22418         }
22419         */
22420         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22421             this.win.focus();
22422             
22423             
22424             // from jquery ui (MIT licenced)
22425             var range, node;
22426             var win = this.win;
22427             
22428             if (win.getSelection && win.getSelection().getRangeAt) {
22429                 range = win.getSelection().getRangeAt(0);
22430                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22431                 range.insertNode(node);
22432             } else if (win.document.selection && win.document.selection.createRange) {
22433                 // no firefox support
22434                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22435                 win.document.selection.createRange().pasteHTML(txt);
22436             } else {
22437                 // no firefox support
22438                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22439                 this.execCmd('InsertHTML', txt);
22440             } 
22441             
22442             this.syncValue();
22443             
22444             this.deferFocus();
22445         }
22446     },
22447  // private
22448     mozKeyPress : function(e){
22449         if(e.ctrlKey){
22450             var c = e.getCharCode(), cmd;
22451           
22452             if(c > 0){
22453                 c = String.fromCharCode(c).toLowerCase();
22454                 switch(c){
22455                     case 'b':
22456                         cmd = 'bold';
22457                         break;
22458                     case 'i':
22459                         cmd = 'italic';
22460                         break;
22461                     
22462                     case 'u':
22463                         cmd = 'underline';
22464                         break;
22465                     
22466                     case 'v':
22467                         this.cleanUpPaste.defer(100, this);
22468                         return;
22469                         
22470                 }
22471                 if(cmd){
22472                     this.win.focus();
22473                     this.execCmd(cmd);
22474                     this.deferFocus();
22475                     e.preventDefault();
22476                 }
22477                 
22478             }
22479         }
22480     },
22481
22482     // private
22483     fixKeys : function(){ // load time branching for fastest keydown performance
22484         if(Roo.isIE){
22485             return function(e){
22486                 var k = e.getKey(), r;
22487                 if(k == e.TAB){
22488                     e.stopEvent();
22489                     r = this.doc.selection.createRange();
22490                     if(r){
22491                         r.collapse(true);
22492                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22493                         this.deferFocus();
22494                     }
22495                     return;
22496                 }
22497                 
22498                 if(k == e.ENTER){
22499                     r = this.doc.selection.createRange();
22500                     if(r){
22501                         var target = r.parentElement();
22502                         if(!target || target.tagName.toLowerCase() != 'li'){
22503                             e.stopEvent();
22504                             r.pasteHTML('<br />');
22505                             r.collapse(false);
22506                             r.select();
22507                         }
22508                     }
22509                 }
22510                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22511                     this.cleanUpPaste.defer(100, this);
22512                     return;
22513                 }
22514                 
22515                 
22516             };
22517         }else if(Roo.isOpera){
22518             return function(e){
22519                 var k = e.getKey();
22520                 if(k == e.TAB){
22521                     e.stopEvent();
22522                     this.win.focus();
22523                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22524                     this.deferFocus();
22525                 }
22526                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22527                     this.cleanUpPaste.defer(100, this);
22528                     return;
22529                 }
22530                 
22531             };
22532         }else if(Roo.isSafari){
22533             return function(e){
22534                 var k = e.getKey();
22535                 
22536                 if(k == e.TAB){
22537                     e.stopEvent();
22538                     this.execCmd('InsertText','\t');
22539                     this.deferFocus();
22540                     return;
22541                 }
22542                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22543                     this.cleanUpPaste.defer(100, this);
22544                     return;
22545                 }
22546                 
22547              };
22548         }
22549     }(),
22550     
22551     getAllAncestors: function()
22552     {
22553         var p = this.getSelectedNode();
22554         var a = [];
22555         if (!p) {
22556             a.push(p); // push blank onto stack..
22557             p = this.getParentElement();
22558         }
22559         
22560         
22561         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22562             a.push(p);
22563             p = p.parentNode;
22564         }
22565         a.push(this.doc.body);
22566         return a;
22567     },
22568     lastSel : false,
22569     lastSelNode : false,
22570     
22571     
22572     getSelection : function() 
22573     {
22574         this.assignDocWin();
22575         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22576     },
22577     
22578     getSelectedNode: function() 
22579     {
22580         // this may only work on Gecko!!!
22581         
22582         // should we cache this!!!!
22583         
22584         
22585         
22586          
22587         var range = this.createRange(this.getSelection()).cloneRange();
22588         
22589         if (Roo.isIE) {
22590             var parent = range.parentElement();
22591             while (true) {
22592                 var testRange = range.duplicate();
22593                 testRange.moveToElementText(parent);
22594                 if (testRange.inRange(range)) {
22595                     break;
22596                 }
22597                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22598                     break;
22599                 }
22600                 parent = parent.parentElement;
22601             }
22602             return parent;
22603         }
22604         
22605         // is ancestor a text element.
22606         var ac =  range.commonAncestorContainer;
22607         if (ac.nodeType == 3) {
22608             ac = ac.parentNode;
22609         }
22610         
22611         var ar = ac.childNodes;
22612          
22613         var nodes = [];
22614         var other_nodes = [];
22615         var has_other_nodes = false;
22616         for (var i=0;i<ar.length;i++) {
22617             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22618                 continue;
22619             }
22620             // fullly contained node.
22621             
22622             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22623                 nodes.push(ar[i]);
22624                 continue;
22625             }
22626             
22627             // probably selected..
22628             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22629                 other_nodes.push(ar[i]);
22630                 continue;
22631             }
22632             // outer..
22633             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22634                 continue;
22635             }
22636             
22637             
22638             has_other_nodes = true;
22639         }
22640         if (!nodes.length && other_nodes.length) {
22641             nodes= other_nodes;
22642         }
22643         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22644             return false;
22645         }
22646         
22647         return nodes[0];
22648     },
22649     createRange: function(sel)
22650     {
22651         // this has strange effects when using with 
22652         // top toolbar - not sure if it's a great idea.
22653         //this.editor.contentWindow.focus();
22654         if (typeof sel != "undefined") {
22655             try {
22656                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22657             } catch(e) {
22658                 return this.doc.createRange();
22659             }
22660         } else {
22661             return this.doc.createRange();
22662         }
22663     },
22664     getParentElement: function()
22665     {
22666         
22667         this.assignDocWin();
22668         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22669         
22670         var range = this.createRange(sel);
22671          
22672         try {
22673             var p = range.commonAncestorContainer;
22674             while (p.nodeType == 3) { // text node
22675                 p = p.parentNode;
22676             }
22677             return p;
22678         } catch (e) {
22679             return null;
22680         }
22681     
22682     },
22683     /***
22684      *
22685      * Range intersection.. the hard stuff...
22686      *  '-1' = before
22687      *  '0' = hits..
22688      *  '1' = after.
22689      *         [ -- selected range --- ]
22690      *   [fail]                        [fail]
22691      *
22692      *    basically..
22693      *      if end is before start or  hits it. fail.
22694      *      if start is after end or hits it fail.
22695      *
22696      *   if either hits (but other is outside. - then it's not 
22697      *   
22698      *    
22699      **/
22700     
22701     
22702     // @see http://www.thismuchiknow.co.uk/?p=64.
22703     rangeIntersectsNode : function(range, node)
22704     {
22705         var nodeRange = node.ownerDocument.createRange();
22706         try {
22707             nodeRange.selectNode(node);
22708         } catch (e) {
22709             nodeRange.selectNodeContents(node);
22710         }
22711     
22712         var rangeStartRange = range.cloneRange();
22713         rangeStartRange.collapse(true);
22714     
22715         var rangeEndRange = range.cloneRange();
22716         rangeEndRange.collapse(false);
22717     
22718         var nodeStartRange = nodeRange.cloneRange();
22719         nodeStartRange.collapse(true);
22720     
22721         var nodeEndRange = nodeRange.cloneRange();
22722         nodeEndRange.collapse(false);
22723     
22724         return rangeStartRange.compareBoundaryPoints(
22725                  Range.START_TO_START, nodeEndRange) == -1 &&
22726                rangeEndRange.compareBoundaryPoints(
22727                  Range.START_TO_START, nodeStartRange) == 1;
22728         
22729          
22730     },
22731     rangeCompareNode : function(range, node)
22732     {
22733         var nodeRange = node.ownerDocument.createRange();
22734         try {
22735             nodeRange.selectNode(node);
22736         } catch (e) {
22737             nodeRange.selectNodeContents(node);
22738         }
22739         
22740         
22741         range.collapse(true);
22742     
22743         nodeRange.collapse(true);
22744      
22745         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22746         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22747          
22748         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22749         
22750         var nodeIsBefore   =  ss == 1;
22751         var nodeIsAfter    = ee == -1;
22752         
22753         if (nodeIsBefore && nodeIsAfter) {
22754             return 0; // outer
22755         }
22756         if (!nodeIsBefore && nodeIsAfter) {
22757             return 1; //right trailed.
22758         }
22759         
22760         if (nodeIsBefore && !nodeIsAfter) {
22761             return 2;  // left trailed.
22762         }
22763         // fully contined.
22764         return 3;
22765     },
22766
22767     // private? - in a new class?
22768     cleanUpPaste :  function()
22769     {
22770         // cleans up the whole document..
22771         Roo.log('cleanuppaste');
22772         
22773         this.cleanUpChildren(this.doc.body);
22774         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22775         if (clean != this.doc.body.innerHTML) {
22776             this.doc.body.innerHTML = clean;
22777         }
22778         
22779     },
22780     
22781     cleanWordChars : function(input) {// change the chars to hex code
22782         var he = Roo.HtmlEditorCore;
22783         
22784         var output = input;
22785         Roo.each(he.swapCodes, function(sw) { 
22786             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22787             
22788             output = output.replace(swapper, sw[1]);
22789         });
22790         
22791         return output;
22792     },
22793     
22794     
22795     cleanUpChildren : function (n)
22796     {
22797         if (!n.childNodes.length) {
22798             return;
22799         }
22800         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22801            this.cleanUpChild(n.childNodes[i]);
22802         }
22803     },
22804     
22805     
22806         
22807     
22808     cleanUpChild : function (node)
22809     {
22810         var ed = this;
22811         //console.log(node);
22812         if (node.nodeName == "#text") {
22813             // clean up silly Windows -- stuff?
22814             return; 
22815         }
22816         if (node.nodeName == "#comment") {
22817             node.parentNode.removeChild(node);
22818             // clean up silly Windows -- stuff?
22819             return; 
22820         }
22821         var lcname = node.tagName.toLowerCase();
22822         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22823         // whitelist of tags..
22824         
22825         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22826             // remove node.
22827             node.parentNode.removeChild(node);
22828             return;
22829             
22830         }
22831         
22832         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22833         
22834         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22835         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22836         
22837         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22838         //    remove_keep_children = true;
22839         //}
22840         
22841         if (remove_keep_children) {
22842             this.cleanUpChildren(node);
22843             // inserts everything just before this node...
22844             while (node.childNodes.length) {
22845                 var cn = node.childNodes[0];
22846                 node.removeChild(cn);
22847                 node.parentNode.insertBefore(cn, node);
22848             }
22849             node.parentNode.removeChild(node);
22850             return;
22851         }
22852         
22853         if (!node.attributes || !node.attributes.length) {
22854             this.cleanUpChildren(node);
22855             return;
22856         }
22857         
22858         function cleanAttr(n,v)
22859         {
22860             
22861             if (v.match(/^\./) || v.match(/^\//)) {
22862                 return;
22863             }
22864             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22865                 return;
22866             }
22867             if (v.match(/^#/)) {
22868                 return;
22869             }
22870 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22871             node.removeAttribute(n);
22872             
22873         }
22874         
22875         var cwhite = this.cwhite;
22876         var cblack = this.cblack;
22877             
22878         function cleanStyle(n,v)
22879         {
22880             if (v.match(/expression/)) { //XSS?? should we even bother..
22881                 node.removeAttribute(n);
22882                 return;
22883             }
22884             
22885             var parts = v.split(/;/);
22886             var clean = [];
22887             
22888             Roo.each(parts, function(p) {
22889                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22890                 if (!p.length) {
22891                     return true;
22892                 }
22893                 var l = p.split(':').shift().replace(/\s+/g,'');
22894                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22895                 
22896                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22897 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22898                     //node.removeAttribute(n);
22899                     return true;
22900                 }
22901                 //Roo.log()
22902                 // only allow 'c whitelisted system attributes'
22903                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22904 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22905                     //node.removeAttribute(n);
22906                     return true;
22907                 }
22908                 
22909                 
22910                  
22911                 
22912                 clean.push(p);
22913                 return true;
22914             });
22915             if (clean.length) { 
22916                 node.setAttribute(n, clean.join(';'));
22917             } else {
22918                 node.removeAttribute(n);
22919             }
22920             
22921         }
22922         
22923         
22924         for (var i = node.attributes.length-1; i > -1 ; i--) {
22925             var a = node.attributes[i];
22926             //console.log(a);
22927             
22928             if (a.name.toLowerCase().substr(0,2)=='on')  {
22929                 node.removeAttribute(a.name);
22930                 continue;
22931             }
22932             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22933                 node.removeAttribute(a.name);
22934                 continue;
22935             }
22936             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22937                 cleanAttr(a.name,a.value); // fixme..
22938                 continue;
22939             }
22940             if (a.name == 'style') {
22941                 cleanStyle(a.name,a.value);
22942                 continue;
22943             }
22944             /// clean up MS crap..
22945             // tecnically this should be a list of valid class'es..
22946             
22947             
22948             if (a.name == 'class') {
22949                 if (a.value.match(/^Mso/)) {
22950                     node.className = '';
22951                 }
22952                 
22953                 if (a.value.match(/^body$/)) {
22954                     node.className = '';
22955                 }
22956                 continue;
22957             }
22958             
22959             // style cleanup!?
22960             // class cleanup?
22961             
22962         }
22963         
22964         
22965         this.cleanUpChildren(node);
22966         
22967         
22968     },
22969     
22970     /**
22971      * Clean up MS wordisms...
22972      */
22973     cleanWord : function(node)
22974     {
22975         
22976         
22977         if (!node) {
22978             this.cleanWord(this.doc.body);
22979             return;
22980         }
22981         if (node.nodeName == "#text") {
22982             // clean up silly Windows -- stuff?
22983             return; 
22984         }
22985         if (node.nodeName == "#comment") {
22986             node.parentNode.removeChild(node);
22987             // clean up silly Windows -- stuff?
22988             return; 
22989         }
22990         
22991         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22992             node.parentNode.removeChild(node);
22993             return;
22994         }
22995         
22996         // remove - but keep children..
22997         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22998             while (node.childNodes.length) {
22999                 var cn = node.childNodes[0];
23000                 node.removeChild(cn);
23001                 node.parentNode.insertBefore(cn, node);
23002             }
23003             node.parentNode.removeChild(node);
23004             this.iterateChildren(node, this.cleanWord);
23005             return;
23006         }
23007         // clean styles
23008         if (node.className.length) {
23009             
23010             var cn = node.className.split(/\W+/);
23011             var cna = [];
23012             Roo.each(cn, function(cls) {
23013                 if (cls.match(/Mso[a-zA-Z]+/)) {
23014                     return;
23015                 }
23016                 cna.push(cls);
23017             });
23018             node.className = cna.length ? cna.join(' ') : '';
23019             if (!cna.length) {
23020                 node.removeAttribute("class");
23021             }
23022         }
23023         
23024         if (node.hasAttribute("lang")) {
23025             node.removeAttribute("lang");
23026         }
23027         
23028         if (node.hasAttribute("style")) {
23029             
23030             var styles = node.getAttribute("style").split(";");
23031             var nstyle = [];
23032             Roo.each(styles, function(s) {
23033                 if (!s.match(/:/)) {
23034                     return;
23035                 }
23036                 var kv = s.split(":");
23037                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23038                     return;
23039                 }
23040                 // what ever is left... we allow.
23041                 nstyle.push(s);
23042             });
23043             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23044             if (!nstyle.length) {
23045                 node.removeAttribute('style');
23046             }
23047         }
23048         this.iterateChildren(node, this.cleanWord);
23049         
23050         
23051         
23052     },
23053     /**
23054      * iterateChildren of a Node, calling fn each time, using this as the scole..
23055      * @param {DomNode} node node to iterate children of.
23056      * @param {Function} fn method of this class to call on each item.
23057      */
23058     iterateChildren : function(node, fn)
23059     {
23060         if (!node.childNodes.length) {
23061                 return;
23062         }
23063         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23064            fn.call(this, node.childNodes[i])
23065         }
23066     },
23067     
23068     
23069     /**
23070      * cleanTableWidths.
23071      *
23072      * Quite often pasting from word etc.. results in tables with column and widths.
23073      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23074      *
23075      */
23076     cleanTableWidths : function(node)
23077     {
23078          
23079          
23080         if (!node) {
23081             this.cleanTableWidths(this.doc.body);
23082             return;
23083         }
23084         
23085         // ignore list...
23086         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23087             return; 
23088         }
23089         Roo.log(node.tagName);
23090         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23091             this.iterateChildren(node, this.cleanTableWidths);
23092             return;
23093         }
23094         if (node.hasAttribute('width')) {
23095             node.removeAttribute('width');
23096         }
23097         
23098          
23099         if (node.hasAttribute("style")) {
23100             // pretty basic...
23101             
23102             var styles = node.getAttribute("style").split(";");
23103             var nstyle = [];
23104             Roo.each(styles, function(s) {
23105                 if (!s.match(/:/)) {
23106                     return;
23107                 }
23108                 var kv = s.split(":");
23109                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23110                     return;
23111                 }
23112                 // what ever is left... we allow.
23113                 nstyle.push(s);
23114             });
23115             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23116             if (!nstyle.length) {
23117                 node.removeAttribute('style');
23118             }
23119         }
23120         
23121         this.iterateChildren(node, this.cleanTableWidths);
23122         
23123         
23124     },
23125     
23126     
23127     
23128     
23129     domToHTML : function(currentElement, depth, nopadtext) {
23130         
23131         depth = depth || 0;
23132         nopadtext = nopadtext || false;
23133     
23134         if (!currentElement) {
23135             return this.domToHTML(this.doc.body);
23136         }
23137         
23138         //Roo.log(currentElement);
23139         var j;
23140         var allText = false;
23141         var nodeName = currentElement.nodeName;
23142         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23143         
23144         if  (nodeName == '#text') {
23145             
23146             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23147         }
23148         
23149         
23150         var ret = '';
23151         if (nodeName != 'BODY') {
23152              
23153             var i = 0;
23154             // Prints the node tagName, such as <A>, <IMG>, etc
23155             if (tagName) {
23156                 var attr = [];
23157                 for(i = 0; i < currentElement.attributes.length;i++) {
23158                     // quoting?
23159                     var aname = currentElement.attributes.item(i).name;
23160                     if (!currentElement.attributes.item(i).value.length) {
23161                         continue;
23162                     }
23163                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23164                 }
23165                 
23166                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23167             } 
23168             else {
23169                 
23170                 // eack
23171             }
23172         } else {
23173             tagName = false;
23174         }
23175         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23176             return ret;
23177         }
23178         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23179             nopadtext = true;
23180         }
23181         
23182         
23183         // Traverse the tree
23184         i = 0;
23185         var currentElementChild = currentElement.childNodes.item(i);
23186         var allText = true;
23187         var innerHTML  = '';
23188         lastnode = '';
23189         while (currentElementChild) {
23190             // Formatting code (indent the tree so it looks nice on the screen)
23191             var nopad = nopadtext;
23192             if (lastnode == 'SPAN') {
23193                 nopad  = true;
23194             }
23195             // text
23196             if  (currentElementChild.nodeName == '#text') {
23197                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23198                 toadd = nopadtext ? toadd : toadd.trim();
23199                 if (!nopad && toadd.length > 80) {
23200                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23201                 }
23202                 innerHTML  += toadd;
23203                 
23204                 i++;
23205                 currentElementChild = currentElement.childNodes.item(i);
23206                 lastNode = '';
23207                 continue;
23208             }
23209             allText = false;
23210             
23211             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23212                 
23213             // Recursively traverse the tree structure of the child node
23214             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23215             lastnode = currentElementChild.nodeName;
23216             i++;
23217             currentElementChild=currentElement.childNodes.item(i);
23218         }
23219         
23220         ret += innerHTML;
23221         
23222         if (!allText) {
23223                 // The remaining code is mostly for formatting the tree
23224             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23225         }
23226         
23227         
23228         if (tagName) {
23229             ret+= "</"+tagName+">";
23230         }
23231         return ret;
23232         
23233     },
23234         
23235     applyBlacklists : function()
23236     {
23237         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23238         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23239         
23240         this.white = [];
23241         this.black = [];
23242         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23243             if (b.indexOf(tag) > -1) {
23244                 return;
23245             }
23246             this.white.push(tag);
23247             
23248         }, this);
23249         
23250         Roo.each(w, function(tag) {
23251             if (b.indexOf(tag) > -1) {
23252                 return;
23253             }
23254             if (this.white.indexOf(tag) > -1) {
23255                 return;
23256             }
23257             this.white.push(tag);
23258             
23259         }, this);
23260         
23261         
23262         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23263             if (w.indexOf(tag) > -1) {
23264                 return;
23265             }
23266             this.black.push(tag);
23267             
23268         }, this);
23269         
23270         Roo.each(b, function(tag) {
23271             if (w.indexOf(tag) > -1) {
23272                 return;
23273             }
23274             if (this.black.indexOf(tag) > -1) {
23275                 return;
23276             }
23277             this.black.push(tag);
23278             
23279         }, this);
23280         
23281         
23282         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23283         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23284         
23285         this.cwhite = [];
23286         this.cblack = [];
23287         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23288             if (b.indexOf(tag) > -1) {
23289                 return;
23290             }
23291             this.cwhite.push(tag);
23292             
23293         }, this);
23294         
23295         Roo.each(w, function(tag) {
23296             if (b.indexOf(tag) > -1) {
23297                 return;
23298             }
23299             if (this.cwhite.indexOf(tag) > -1) {
23300                 return;
23301             }
23302             this.cwhite.push(tag);
23303             
23304         }, this);
23305         
23306         
23307         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23308             if (w.indexOf(tag) > -1) {
23309                 return;
23310             }
23311             this.cblack.push(tag);
23312             
23313         }, this);
23314         
23315         Roo.each(b, function(tag) {
23316             if (w.indexOf(tag) > -1) {
23317                 return;
23318             }
23319             if (this.cblack.indexOf(tag) > -1) {
23320                 return;
23321             }
23322             this.cblack.push(tag);
23323             
23324         }, this);
23325     },
23326     
23327     setStylesheets : function(stylesheets)
23328     {
23329         if(typeof(stylesheets) == 'string'){
23330             Roo.get(this.iframe.contentDocument.head).createChild({
23331                 tag : 'link',
23332                 rel : 'stylesheet',
23333                 type : 'text/css',
23334                 href : stylesheets
23335             });
23336             
23337             return;
23338         }
23339         var _this = this;
23340      
23341         Roo.each(stylesheets, function(s) {
23342             if(!s.length){
23343                 return;
23344             }
23345             
23346             Roo.get(_this.iframe.contentDocument.head).createChild({
23347                 tag : 'link',
23348                 rel : 'stylesheet',
23349                 type : 'text/css',
23350                 href : s
23351             });
23352         });
23353
23354         
23355     },
23356     
23357     removeStylesheets : function()
23358     {
23359         var _this = this;
23360         
23361         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23362             s.remove();
23363         });
23364     },
23365     
23366     setStyle : function(style)
23367     {
23368         Roo.get(this.iframe.contentDocument.head).createChild({
23369             tag : 'style',
23370             type : 'text/css',
23371             html : style
23372         });
23373
23374         return;
23375     }
23376     
23377     // hide stuff that is not compatible
23378     /**
23379      * @event blur
23380      * @hide
23381      */
23382     /**
23383      * @event change
23384      * @hide
23385      */
23386     /**
23387      * @event focus
23388      * @hide
23389      */
23390     /**
23391      * @event specialkey
23392      * @hide
23393      */
23394     /**
23395      * @cfg {String} fieldClass @hide
23396      */
23397     /**
23398      * @cfg {String} focusClass @hide
23399      */
23400     /**
23401      * @cfg {String} autoCreate @hide
23402      */
23403     /**
23404      * @cfg {String} inputType @hide
23405      */
23406     /**
23407      * @cfg {String} invalidClass @hide
23408      */
23409     /**
23410      * @cfg {String} invalidText @hide
23411      */
23412     /**
23413      * @cfg {String} msgFx @hide
23414      */
23415     /**
23416      * @cfg {String} validateOnBlur @hide
23417      */
23418 });
23419
23420 Roo.HtmlEditorCore.white = [
23421         'area', 'br', 'img', 'input', 'hr', 'wbr',
23422         
23423        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23424        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23425        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23426        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23427        'table',   'ul',         'xmp', 
23428        
23429        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23430       'thead',   'tr', 
23431      
23432       'dir', 'menu', 'ol', 'ul', 'dl',
23433        
23434       'embed',  'object'
23435 ];
23436
23437
23438 Roo.HtmlEditorCore.black = [
23439     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23440         'applet', // 
23441         'base',   'basefont', 'bgsound', 'blink',  'body', 
23442         'frame',  'frameset', 'head',    'html',   'ilayer', 
23443         'iframe', 'layer',  'link',     'meta',    'object',   
23444         'script', 'style' ,'title',  'xml' // clean later..
23445 ];
23446 Roo.HtmlEditorCore.clean = [
23447     'script', 'style', 'title', 'xml'
23448 ];
23449 Roo.HtmlEditorCore.remove = [
23450     'font'
23451 ];
23452 // attributes..
23453
23454 Roo.HtmlEditorCore.ablack = [
23455     'on'
23456 ];
23457     
23458 Roo.HtmlEditorCore.aclean = [ 
23459     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23460 ];
23461
23462 // protocols..
23463 Roo.HtmlEditorCore.pwhite= [
23464         'http',  'https',  'mailto'
23465 ];
23466
23467 // white listed style attributes.
23468 Roo.HtmlEditorCore.cwhite= [
23469       //  'text-align', /// default is to allow most things..
23470       
23471          
23472 //        'font-size'//??
23473 ];
23474
23475 // black listed style attributes.
23476 Roo.HtmlEditorCore.cblack= [
23477       //  'font-size' -- this can be set by the project 
23478 ];
23479
23480
23481 Roo.HtmlEditorCore.swapCodes   =[ 
23482     [    8211, "--" ], 
23483     [    8212, "--" ], 
23484     [    8216,  "'" ],  
23485     [    8217, "'" ],  
23486     [    8220, '"' ],  
23487     [    8221, '"' ],  
23488     [    8226, "*" ],  
23489     [    8230, "..." ]
23490 ]; 
23491
23492     /*
23493  * - LGPL
23494  *
23495  * HtmlEditor
23496  * 
23497  */
23498
23499 /**
23500  * @class Roo.bootstrap.HtmlEditor
23501  * @extends Roo.bootstrap.TextArea
23502  * Bootstrap HtmlEditor class
23503
23504  * @constructor
23505  * Create a new HtmlEditor
23506  * @param {Object} config The config object
23507  */
23508
23509 Roo.bootstrap.HtmlEditor = function(config){
23510     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23511     if (!this.toolbars) {
23512         this.toolbars = [];
23513     }
23514     
23515     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23516     this.addEvents({
23517             /**
23518              * @event initialize
23519              * Fires when the editor is fully initialized (including the iframe)
23520              * @param {HtmlEditor} this
23521              */
23522             initialize: true,
23523             /**
23524              * @event activate
23525              * Fires when the editor is first receives the focus. Any insertion must wait
23526              * until after this event.
23527              * @param {HtmlEditor} this
23528              */
23529             activate: true,
23530              /**
23531              * @event beforesync
23532              * Fires before the textarea is updated with content from the editor iframe. Return false
23533              * to cancel the sync.
23534              * @param {HtmlEditor} this
23535              * @param {String} html
23536              */
23537             beforesync: true,
23538              /**
23539              * @event beforepush
23540              * Fires before the iframe editor is updated with content from the textarea. Return false
23541              * to cancel the push.
23542              * @param {HtmlEditor} this
23543              * @param {String} html
23544              */
23545             beforepush: true,
23546              /**
23547              * @event sync
23548              * Fires when the textarea is updated with content from the editor iframe.
23549              * @param {HtmlEditor} this
23550              * @param {String} html
23551              */
23552             sync: true,
23553              /**
23554              * @event push
23555              * Fires when the iframe editor is updated with content from the textarea.
23556              * @param {HtmlEditor} this
23557              * @param {String} html
23558              */
23559             push: true,
23560              /**
23561              * @event editmodechange
23562              * Fires when the editor switches edit modes
23563              * @param {HtmlEditor} this
23564              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23565              */
23566             editmodechange: true,
23567             /**
23568              * @event editorevent
23569              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23570              * @param {HtmlEditor} this
23571              */
23572             editorevent: true,
23573             /**
23574              * @event firstfocus
23575              * Fires when on first focus - needed by toolbars..
23576              * @param {HtmlEditor} this
23577              */
23578             firstfocus: true,
23579             /**
23580              * @event autosave
23581              * Auto save the htmlEditor value as a file into Events
23582              * @param {HtmlEditor} this
23583              */
23584             autosave: true,
23585             /**
23586              * @event savedpreview
23587              * preview the saved version of htmlEditor
23588              * @param {HtmlEditor} this
23589              */
23590             savedpreview: true
23591         });
23592 };
23593
23594
23595 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23596     
23597     
23598       /**
23599      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23600      */
23601     toolbars : false,
23602     
23603      /**
23604     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23605     */
23606     btns : [],
23607    
23608      /**
23609      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23610      *                        Roo.resizable.
23611      */
23612     resizable : false,
23613      /**
23614      * @cfg {Number} height (in pixels)
23615      */   
23616     height: 300,
23617    /**
23618      * @cfg {Number} width (in pixels)
23619      */   
23620     width: false,
23621     
23622     /**
23623      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23624      * 
23625      */
23626     stylesheets: false,
23627     
23628     // id of frame..
23629     frameId: false,
23630     
23631     // private properties
23632     validationEvent : false,
23633     deferHeight: true,
23634     initialized : false,
23635     activated : false,
23636     
23637     onFocus : Roo.emptyFn,
23638     iframePad:3,
23639     hideMode:'offsets',
23640     
23641     tbContainer : false,
23642     
23643     bodyCls : '',
23644     
23645     toolbarContainer :function() {
23646         return this.wrap.select('.x-html-editor-tb',true).first();
23647     },
23648
23649     /**
23650      * Protected method that will not generally be called directly. It
23651      * is called when the editor creates its toolbar. Override this method if you need to
23652      * add custom toolbar buttons.
23653      * @param {HtmlEditor} editor
23654      */
23655     createToolbar : function(){
23656         Roo.log('renewing');
23657         Roo.log("create toolbars");
23658         
23659         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23660         this.toolbars[0].render(this.toolbarContainer());
23661         
23662         return;
23663         
23664 //        if (!editor.toolbars || !editor.toolbars.length) {
23665 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23666 //        }
23667 //        
23668 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23669 //            editor.toolbars[i] = Roo.factory(
23670 //                    typeof(editor.toolbars[i]) == 'string' ?
23671 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23672 //                Roo.bootstrap.HtmlEditor);
23673 //            editor.toolbars[i].init(editor);
23674 //        }
23675     },
23676
23677      
23678     // private
23679     onRender : function(ct, position)
23680     {
23681        // Roo.log("Call onRender: " + this.xtype);
23682         var _t = this;
23683         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23684       
23685         this.wrap = this.inputEl().wrap({
23686             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23687         });
23688         
23689         this.editorcore.onRender(ct, position);
23690          
23691         if (this.resizable) {
23692             this.resizeEl = new Roo.Resizable(this.wrap, {
23693                 pinned : true,
23694                 wrap: true,
23695                 dynamic : true,
23696                 minHeight : this.height,
23697                 height: this.height,
23698                 handles : this.resizable,
23699                 width: this.width,
23700                 listeners : {
23701                     resize : function(r, w, h) {
23702                         _t.onResize(w,h); // -something
23703                     }
23704                 }
23705             });
23706             
23707         }
23708         this.createToolbar(this);
23709        
23710         
23711         if(!this.width && this.resizable){
23712             this.setSize(this.wrap.getSize());
23713         }
23714         if (this.resizeEl) {
23715             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23716             // should trigger onReize..
23717         }
23718         
23719     },
23720
23721     // private
23722     onResize : function(w, h)
23723     {
23724         Roo.log('resize: ' +w + ',' + h );
23725         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23726         var ew = false;
23727         var eh = false;
23728         
23729         if(this.inputEl() ){
23730             if(typeof w == 'number'){
23731                 var aw = w - this.wrap.getFrameWidth('lr');
23732                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23733                 ew = aw;
23734             }
23735             if(typeof h == 'number'){
23736                  var tbh = -11;  // fixme it needs to tool bar size!
23737                 for (var i =0; i < this.toolbars.length;i++) {
23738                     // fixme - ask toolbars for heights?
23739                     tbh += this.toolbars[i].el.getHeight();
23740                     //if (this.toolbars[i].footer) {
23741                     //    tbh += this.toolbars[i].footer.el.getHeight();
23742                     //}
23743                 }
23744               
23745                 
23746                 
23747                 
23748                 
23749                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23750                 ah -= 5; // knock a few pixes off for look..
23751                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23752                 var eh = ah;
23753             }
23754         }
23755         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23756         this.editorcore.onResize(ew,eh);
23757         
23758     },
23759
23760     /**
23761      * Toggles the editor between standard and source edit mode.
23762      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23763      */
23764     toggleSourceEdit : function(sourceEditMode)
23765     {
23766         this.editorcore.toggleSourceEdit(sourceEditMode);
23767         
23768         if(this.editorcore.sourceEditMode){
23769             Roo.log('editor - showing textarea');
23770             
23771 //            Roo.log('in');
23772 //            Roo.log(this.syncValue());
23773             this.syncValue();
23774             this.inputEl().removeClass(['hide', 'x-hidden']);
23775             this.inputEl().dom.removeAttribute('tabIndex');
23776             this.inputEl().focus();
23777         }else{
23778             Roo.log('editor - hiding textarea');
23779 //            Roo.log('out')
23780 //            Roo.log(this.pushValue()); 
23781             this.pushValue();
23782             
23783             this.inputEl().addClass(['hide', 'x-hidden']);
23784             this.inputEl().dom.setAttribute('tabIndex', -1);
23785             //this.deferFocus();
23786         }
23787          
23788         if(this.resizable){
23789             this.setSize(this.wrap.getSize());
23790         }
23791         
23792         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23793     },
23794  
23795     // private (for BoxComponent)
23796     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23797
23798     // private (for BoxComponent)
23799     getResizeEl : function(){
23800         return this.wrap;
23801     },
23802
23803     // private (for BoxComponent)
23804     getPositionEl : function(){
23805         return this.wrap;
23806     },
23807
23808     // private
23809     initEvents : function(){
23810         this.originalValue = this.getValue();
23811     },
23812
23813 //    /**
23814 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23815 //     * @method
23816 //     */
23817 //    markInvalid : Roo.emptyFn,
23818 //    /**
23819 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23820 //     * @method
23821 //     */
23822 //    clearInvalid : Roo.emptyFn,
23823
23824     setValue : function(v){
23825         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23826         this.editorcore.pushValue();
23827     },
23828
23829      
23830     // private
23831     deferFocus : function(){
23832         this.focus.defer(10, this);
23833     },
23834
23835     // doc'ed in Field
23836     focus : function(){
23837         this.editorcore.focus();
23838         
23839     },
23840       
23841
23842     // private
23843     onDestroy : function(){
23844         
23845         
23846         
23847         if(this.rendered){
23848             
23849             for (var i =0; i < this.toolbars.length;i++) {
23850                 // fixme - ask toolbars for heights?
23851                 this.toolbars[i].onDestroy();
23852             }
23853             
23854             this.wrap.dom.innerHTML = '';
23855             this.wrap.remove();
23856         }
23857     },
23858
23859     // private
23860     onFirstFocus : function(){
23861         //Roo.log("onFirstFocus");
23862         this.editorcore.onFirstFocus();
23863          for (var i =0; i < this.toolbars.length;i++) {
23864             this.toolbars[i].onFirstFocus();
23865         }
23866         
23867     },
23868     
23869     // private
23870     syncValue : function()
23871     {   
23872         this.editorcore.syncValue();
23873     },
23874     
23875     pushValue : function()
23876     {   
23877         this.editorcore.pushValue();
23878     }
23879      
23880     
23881     // hide stuff that is not compatible
23882     /**
23883      * @event blur
23884      * @hide
23885      */
23886     /**
23887      * @event change
23888      * @hide
23889      */
23890     /**
23891      * @event focus
23892      * @hide
23893      */
23894     /**
23895      * @event specialkey
23896      * @hide
23897      */
23898     /**
23899      * @cfg {String} fieldClass @hide
23900      */
23901     /**
23902      * @cfg {String} focusClass @hide
23903      */
23904     /**
23905      * @cfg {String} autoCreate @hide
23906      */
23907     /**
23908      * @cfg {String} inputType @hide
23909      */
23910     /**
23911      * @cfg {String} invalidClass @hide
23912      */
23913     /**
23914      * @cfg {String} invalidText @hide
23915      */
23916     /**
23917      * @cfg {String} msgFx @hide
23918      */
23919     /**
23920      * @cfg {String} validateOnBlur @hide
23921      */
23922 });
23923  
23924     
23925    
23926    
23927    
23928       
23929 Roo.namespace('Roo.bootstrap.htmleditor');
23930 /**
23931  * @class Roo.bootstrap.HtmlEditorToolbar1
23932  * Basic Toolbar
23933  * 
23934  * Usage:
23935  *
23936  new Roo.bootstrap.HtmlEditor({
23937     ....
23938     toolbars : [
23939         new Roo.bootstrap.HtmlEditorToolbar1({
23940             disable : { fonts: 1 , format: 1, ..., ... , ...],
23941             btns : [ .... ]
23942         })
23943     }
23944      
23945  * 
23946  * @cfg {Object} disable List of elements to disable..
23947  * @cfg {Array} btns List of additional buttons.
23948  * 
23949  * 
23950  * NEEDS Extra CSS? 
23951  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23952  */
23953  
23954 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23955 {
23956     
23957     Roo.apply(this, config);
23958     
23959     // default disabled, based on 'good practice'..
23960     this.disable = this.disable || {};
23961     Roo.applyIf(this.disable, {
23962         fontSize : true,
23963         colors : true,
23964         specialElements : true
23965     });
23966     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23967     
23968     this.editor = config.editor;
23969     this.editorcore = config.editor.editorcore;
23970     
23971     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23972     
23973     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23974     // dont call parent... till later.
23975 }
23976 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23977      
23978     bar : true,
23979     
23980     editor : false,
23981     editorcore : false,
23982     
23983     
23984     formats : [
23985         "p" ,  
23986         "h1","h2","h3","h4","h5","h6", 
23987         "pre", "code", 
23988         "abbr", "acronym", "address", "cite", "samp", "var",
23989         'div','span'
23990     ],
23991     
23992     onRender : function(ct, position)
23993     {
23994        // Roo.log("Call onRender: " + this.xtype);
23995         
23996        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23997        Roo.log(this.el);
23998        this.el.dom.style.marginBottom = '0';
23999        var _this = this;
24000        var editorcore = this.editorcore;
24001        var editor= this.editor;
24002        
24003        var children = [];
24004        var btn = function(id,cmd , toggle, handler, html){
24005        
24006             var  event = toggle ? 'toggle' : 'click';
24007        
24008             var a = {
24009                 size : 'sm',
24010                 xtype: 'Button',
24011                 xns: Roo.bootstrap,
24012                 //glyphicon : id,
24013                 fa: id,
24014                 cmd : id || cmd,
24015                 enableToggle:toggle !== false,
24016                 html : html || '',
24017                 pressed : toggle ? false : null,
24018                 listeners : {}
24019             };
24020             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24021                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24022             };
24023             children.push(a);
24024             return a;
24025        }
24026        
24027     //    var cb_box = function...
24028         
24029         var style = {
24030                 xtype: 'Button',
24031                 size : 'sm',
24032                 xns: Roo.bootstrap,
24033                 fa : 'font',
24034                 //html : 'submit'
24035                 menu : {
24036                     xtype: 'Menu',
24037                     xns: Roo.bootstrap,
24038                     items:  []
24039                 }
24040         };
24041         Roo.each(this.formats, function(f) {
24042             style.menu.items.push({
24043                 xtype :'MenuItem',
24044                 xns: Roo.bootstrap,
24045                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24046                 tagname : f,
24047                 listeners : {
24048                     click : function()
24049                     {
24050                         editorcore.insertTag(this.tagname);
24051                         editor.focus();
24052                     }
24053                 }
24054                 
24055             });
24056         });
24057         children.push(style);   
24058         
24059         btn('bold',false,true);
24060         btn('italic',false,true);
24061         btn('align-left', 'justifyleft',true);
24062         btn('align-center', 'justifycenter',true);
24063         btn('align-right' , 'justifyright',true);
24064         btn('link', false, false, function(btn) {
24065             //Roo.log("create link?");
24066             var url = prompt(this.createLinkText, this.defaultLinkValue);
24067             if(url && url != 'http:/'+'/'){
24068                 this.editorcore.relayCmd('createlink', url);
24069             }
24070         }),
24071         btn('list','insertunorderedlist',true);
24072         btn('pencil', false,true, function(btn){
24073                 Roo.log(this);
24074                 this.toggleSourceEdit(btn.pressed);
24075         });
24076         
24077         if (this.editor.btns.length > 0) {
24078             for (var i = 0; i<this.editor.btns.length; i++) {
24079                 children.push(this.editor.btns[i]);
24080             }
24081         }
24082         
24083         /*
24084         var cog = {
24085                 xtype: 'Button',
24086                 size : 'sm',
24087                 xns: Roo.bootstrap,
24088                 glyphicon : 'cog',
24089                 //html : 'submit'
24090                 menu : {
24091                     xtype: 'Menu',
24092                     xns: Roo.bootstrap,
24093                     items:  []
24094                 }
24095         };
24096         
24097         cog.menu.items.push({
24098             xtype :'MenuItem',
24099             xns: Roo.bootstrap,
24100             html : Clean styles,
24101             tagname : f,
24102             listeners : {
24103                 click : function()
24104                 {
24105                     editorcore.insertTag(this.tagname);
24106                     editor.focus();
24107                 }
24108             }
24109             
24110         });
24111        */
24112         
24113          
24114        this.xtype = 'NavSimplebar';
24115         
24116         for(var i=0;i< children.length;i++) {
24117             
24118             this.buttons.add(this.addxtypeChild(children[i]));
24119             
24120         }
24121         
24122         editor.on('editorevent', this.updateToolbar, this);
24123     },
24124     onBtnClick : function(id)
24125     {
24126        this.editorcore.relayCmd(id);
24127        this.editorcore.focus();
24128     },
24129     
24130     /**
24131      * Protected method that will not generally be called directly. It triggers
24132      * a toolbar update by reading the markup state of the current selection in the editor.
24133      */
24134     updateToolbar: function(){
24135
24136         if(!this.editorcore.activated){
24137             this.editor.onFirstFocus(); // is this neeed?
24138             return;
24139         }
24140
24141         var btns = this.buttons; 
24142         var doc = this.editorcore.doc;
24143         btns.get('bold').setActive(doc.queryCommandState('bold'));
24144         btns.get('italic').setActive(doc.queryCommandState('italic'));
24145         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24146         
24147         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24148         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24149         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24150         
24151         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24152         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24153          /*
24154         
24155         var ans = this.editorcore.getAllAncestors();
24156         if (this.formatCombo) {
24157             
24158             
24159             var store = this.formatCombo.store;
24160             this.formatCombo.setValue("");
24161             for (var i =0; i < ans.length;i++) {
24162                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24163                     // select it..
24164                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24165                     break;
24166                 }
24167             }
24168         }
24169         
24170         
24171         
24172         // hides menus... - so this cant be on a menu...
24173         Roo.bootstrap.MenuMgr.hideAll();
24174         */
24175         Roo.bootstrap.MenuMgr.hideAll();
24176         //this.editorsyncValue();
24177     },
24178     onFirstFocus: function() {
24179         this.buttons.each(function(item){
24180            item.enable();
24181         });
24182     },
24183     toggleSourceEdit : function(sourceEditMode){
24184         
24185           
24186         if(sourceEditMode){
24187             Roo.log("disabling buttons");
24188            this.buttons.each( function(item){
24189                 if(item.cmd != 'pencil'){
24190                     item.disable();
24191                 }
24192             });
24193           
24194         }else{
24195             Roo.log("enabling buttons");
24196             if(this.editorcore.initialized){
24197                 this.buttons.each( function(item){
24198                     item.enable();
24199                 });
24200             }
24201             
24202         }
24203         Roo.log("calling toggole on editor");
24204         // tell the editor that it's been pressed..
24205         this.editor.toggleSourceEdit(sourceEditMode);
24206        
24207     }
24208 });
24209
24210
24211
24212
24213
24214 /**
24215  * @class Roo.bootstrap.Table.AbstractSelectionModel
24216  * @extends Roo.util.Observable
24217  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24218  * implemented by descendant classes.  This class should not be directly instantiated.
24219  * @constructor
24220  */
24221 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24222     this.locked = false;
24223     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24224 };
24225
24226
24227 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24228     /** @ignore Called by the grid automatically. Do not call directly. */
24229     init : function(grid){
24230         this.grid = grid;
24231         this.initEvents();
24232     },
24233
24234     /**
24235      * Locks the selections.
24236      */
24237     lock : function(){
24238         this.locked = true;
24239     },
24240
24241     /**
24242      * Unlocks the selections.
24243      */
24244     unlock : function(){
24245         this.locked = false;
24246     },
24247
24248     /**
24249      * Returns true if the selections are locked.
24250      * @return {Boolean}
24251      */
24252     isLocked : function(){
24253         return this.locked;
24254     }
24255 });
24256 /**
24257  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24258  * @class Roo.bootstrap.Table.RowSelectionModel
24259  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24260  * It supports multiple selections and keyboard selection/navigation. 
24261  * @constructor
24262  * @param {Object} config
24263  */
24264
24265 Roo.bootstrap.Table.RowSelectionModel = function(config){
24266     Roo.apply(this, config);
24267     this.selections = new Roo.util.MixedCollection(false, function(o){
24268         return o.id;
24269     });
24270
24271     this.last = false;
24272     this.lastActive = false;
24273
24274     this.addEvents({
24275         /**
24276              * @event selectionchange
24277              * Fires when the selection changes
24278              * @param {SelectionModel} this
24279              */
24280             "selectionchange" : true,
24281         /**
24282              * @event afterselectionchange
24283              * Fires after the selection changes (eg. by key press or clicking)
24284              * @param {SelectionModel} this
24285              */
24286             "afterselectionchange" : true,
24287         /**
24288              * @event beforerowselect
24289              * Fires when a row is selected being selected, return false to cancel.
24290              * @param {SelectionModel} this
24291              * @param {Number} rowIndex The selected index
24292              * @param {Boolean} keepExisting False if other selections will be cleared
24293              */
24294             "beforerowselect" : true,
24295         /**
24296              * @event rowselect
24297              * Fires when a row is selected.
24298              * @param {SelectionModel} this
24299              * @param {Number} rowIndex The selected index
24300              * @param {Roo.data.Record} r The record
24301              */
24302             "rowselect" : true,
24303         /**
24304              * @event rowdeselect
24305              * Fires when a row is deselected.
24306              * @param {SelectionModel} this
24307              * @param {Number} rowIndex The selected index
24308              */
24309         "rowdeselect" : true
24310     });
24311     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24312     this.locked = false;
24313  };
24314
24315 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24316     /**
24317      * @cfg {Boolean} singleSelect
24318      * True to allow selection of only one row at a time (defaults to false)
24319      */
24320     singleSelect : false,
24321
24322     // private
24323     initEvents : function()
24324     {
24325
24326         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24327         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24328         //}else{ // allow click to work like normal
24329          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24330         //}
24331         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24332         this.grid.on("rowclick", this.handleMouseDown, this);
24333         
24334         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24335             "up" : function(e){
24336                 if(!e.shiftKey){
24337                     this.selectPrevious(e.shiftKey);
24338                 }else if(this.last !== false && this.lastActive !== false){
24339                     var last = this.last;
24340                     this.selectRange(this.last,  this.lastActive-1);
24341                     this.grid.getView().focusRow(this.lastActive);
24342                     if(last !== false){
24343                         this.last = last;
24344                     }
24345                 }else{
24346                     this.selectFirstRow();
24347                 }
24348                 this.fireEvent("afterselectionchange", this);
24349             },
24350             "down" : function(e){
24351                 if(!e.shiftKey){
24352                     this.selectNext(e.shiftKey);
24353                 }else if(this.last !== false && this.lastActive !== false){
24354                     var last = this.last;
24355                     this.selectRange(this.last,  this.lastActive+1);
24356                     this.grid.getView().focusRow(this.lastActive);
24357                     if(last !== false){
24358                         this.last = last;
24359                     }
24360                 }else{
24361                     this.selectFirstRow();
24362                 }
24363                 this.fireEvent("afterselectionchange", this);
24364             },
24365             scope: this
24366         });
24367         this.grid.store.on('load', function(){
24368             this.selections.clear();
24369         },this);
24370         /*
24371         var view = this.grid.view;
24372         view.on("refresh", this.onRefresh, this);
24373         view.on("rowupdated", this.onRowUpdated, this);
24374         view.on("rowremoved", this.onRemove, this);
24375         */
24376     },
24377
24378     // private
24379     onRefresh : function()
24380     {
24381         var ds = this.grid.store, i, v = this.grid.view;
24382         var s = this.selections;
24383         s.each(function(r){
24384             if((i = ds.indexOfId(r.id)) != -1){
24385                 v.onRowSelect(i);
24386             }else{
24387                 s.remove(r);
24388             }
24389         });
24390     },
24391
24392     // private
24393     onRemove : function(v, index, r){
24394         this.selections.remove(r);
24395     },
24396
24397     // private
24398     onRowUpdated : function(v, index, r){
24399         if(this.isSelected(r)){
24400             v.onRowSelect(index);
24401         }
24402     },
24403
24404     /**
24405      * Select records.
24406      * @param {Array} records The records to select
24407      * @param {Boolean} keepExisting (optional) True to keep existing selections
24408      */
24409     selectRecords : function(records, keepExisting)
24410     {
24411         if(!keepExisting){
24412             this.clearSelections();
24413         }
24414             var ds = this.grid.store;
24415         for(var i = 0, len = records.length; i < len; i++){
24416             this.selectRow(ds.indexOf(records[i]), true);
24417         }
24418     },
24419
24420     /**
24421      * Gets the number of selected rows.
24422      * @return {Number}
24423      */
24424     getCount : function(){
24425         return this.selections.length;
24426     },
24427
24428     /**
24429      * Selects the first row in the grid.
24430      */
24431     selectFirstRow : function(){
24432         this.selectRow(0);
24433     },
24434
24435     /**
24436      * Select the last row.
24437      * @param {Boolean} keepExisting (optional) True to keep existing selections
24438      */
24439     selectLastRow : function(keepExisting){
24440         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24441         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24442     },
24443
24444     /**
24445      * Selects the row immediately following the last selected row.
24446      * @param {Boolean} keepExisting (optional) True to keep existing selections
24447      */
24448     selectNext : function(keepExisting)
24449     {
24450             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24451             this.selectRow(this.last+1, keepExisting);
24452             this.grid.getView().focusRow(this.last);
24453         }
24454     },
24455
24456     /**
24457      * Selects the row that precedes the last selected row.
24458      * @param {Boolean} keepExisting (optional) True to keep existing selections
24459      */
24460     selectPrevious : function(keepExisting){
24461         if(this.last){
24462             this.selectRow(this.last-1, keepExisting);
24463             this.grid.getView().focusRow(this.last);
24464         }
24465     },
24466
24467     /**
24468      * Returns the selected records
24469      * @return {Array} Array of selected records
24470      */
24471     getSelections : function(){
24472         return [].concat(this.selections.items);
24473     },
24474
24475     /**
24476      * Returns the first selected record.
24477      * @return {Record}
24478      */
24479     getSelected : function(){
24480         return this.selections.itemAt(0);
24481     },
24482
24483
24484     /**
24485      * Clears all selections.
24486      */
24487     clearSelections : function(fast)
24488     {
24489         if(this.locked) {
24490             return;
24491         }
24492         if(fast !== true){
24493                 var ds = this.grid.store;
24494             var s = this.selections;
24495             s.each(function(r){
24496                 this.deselectRow(ds.indexOfId(r.id));
24497             }, this);
24498             s.clear();
24499         }else{
24500             this.selections.clear();
24501         }
24502         this.last = false;
24503     },
24504
24505
24506     /**
24507      * Selects all rows.
24508      */
24509     selectAll : function(){
24510         if(this.locked) {
24511             return;
24512         }
24513         this.selections.clear();
24514         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24515             this.selectRow(i, true);
24516         }
24517     },
24518
24519     /**
24520      * Returns True if there is a selection.
24521      * @return {Boolean}
24522      */
24523     hasSelection : function(){
24524         return this.selections.length > 0;
24525     },
24526
24527     /**
24528      * Returns True if the specified row is selected.
24529      * @param {Number/Record} record The record or index of the record to check
24530      * @return {Boolean}
24531      */
24532     isSelected : function(index){
24533             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24534         return (r && this.selections.key(r.id) ? true : false);
24535     },
24536
24537     /**
24538      * Returns True if the specified record id is selected.
24539      * @param {String} id The id of record to check
24540      * @return {Boolean}
24541      */
24542     isIdSelected : function(id){
24543         return (this.selections.key(id) ? true : false);
24544     },
24545
24546
24547     // private
24548     handleMouseDBClick : function(e, t){
24549         
24550     },
24551     // private
24552     handleMouseDown : function(e, t)
24553     {
24554             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24555         if(this.isLocked() || rowIndex < 0 ){
24556             return;
24557         };
24558         if(e.shiftKey && this.last !== false){
24559             var last = this.last;
24560             this.selectRange(last, rowIndex, e.ctrlKey);
24561             this.last = last; // reset the last
24562             t.focus();
24563     
24564         }else{
24565             var isSelected = this.isSelected(rowIndex);
24566             //Roo.log("select row:" + rowIndex);
24567             if(isSelected){
24568                 this.deselectRow(rowIndex);
24569             } else {
24570                         this.selectRow(rowIndex, true);
24571             }
24572     
24573             /*
24574                 if(e.button !== 0 && isSelected){
24575                 alert('rowIndex 2: ' + rowIndex);
24576                     view.focusRow(rowIndex);
24577                 }else if(e.ctrlKey && isSelected){
24578                     this.deselectRow(rowIndex);
24579                 }else if(!isSelected){
24580                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24581                     view.focusRow(rowIndex);
24582                 }
24583             */
24584         }
24585         this.fireEvent("afterselectionchange", this);
24586     },
24587     // private
24588     handleDragableRowClick :  function(grid, rowIndex, e) 
24589     {
24590         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24591             this.selectRow(rowIndex, false);
24592             grid.view.focusRow(rowIndex);
24593              this.fireEvent("afterselectionchange", this);
24594         }
24595     },
24596     
24597     /**
24598      * Selects multiple rows.
24599      * @param {Array} rows Array of the indexes of the row to select
24600      * @param {Boolean} keepExisting (optional) True to keep existing selections
24601      */
24602     selectRows : function(rows, keepExisting){
24603         if(!keepExisting){
24604             this.clearSelections();
24605         }
24606         for(var i = 0, len = rows.length; i < len; i++){
24607             this.selectRow(rows[i], true);
24608         }
24609     },
24610
24611     /**
24612      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24613      * @param {Number} startRow The index of the first row in the range
24614      * @param {Number} endRow The index of the last row in the range
24615      * @param {Boolean} keepExisting (optional) True to retain existing selections
24616      */
24617     selectRange : function(startRow, endRow, keepExisting){
24618         if(this.locked) {
24619             return;
24620         }
24621         if(!keepExisting){
24622             this.clearSelections();
24623         }
24624         if(startRow <= endRow){
24625             for(var i = startRow; i <= endRow; i++){
24626                 this.selectRow(i, true);
24627             }
24628         }else{
24629             for(var i = startRow; i >= endRow; i--){
24630                 this.selectRow(i, true);
24631             }
24632         }
24633     },
24634
24635     /**
24636      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24637      * @param {Number} startRow The index of the first row in the range
24638      * @param {Number} endRow The index of the last row in the range
24639      */
24640     deselectRange : function(startRow, endRow, preventViewNotify){
24641         if(this.locked) {
24642             return;
24643         }
24644         for(var i = startRow; i <= endRow; i++){
24645             this.deselectRow(i, preventViewNotify);
24646         }
24647     },
24648
24649     /**
24650      * Selects a row.
24651      * @param {Number} row The index of the row to select
24652      * @param {Boolean} keepExisting (optional) True to keep existing selections
24653      */
24654     selectRow : function(index, keepExisting, preventViewNotify)
24655     {
24656             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24657             return;
24658         }
24659         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24660             if(!keepExisting || this.singleSelect){
24661                 this.clearSelections();
24662             }
24663             
24664             var r = this.grid.store.getAt(index);
24665             //console.log('selectRow - record id :' + r.id);
24666             
24667             this.selections.add(r);
24668             this.last = this.lastActive = index;
24669             if(!preventViewNotify){
24670                 var proxy = new Roo.Element(
24671                                 this.grid.getRowDom(index)
24672                 );
24673                 proxy.addClass('bg-info info');
24674             }
24675             this.fireEvent("rowselect", this, index, r);
24676             this.fireEvent("selectionchange", this);
24677         }
24678     },
24679
24680     /**
24681      * Deselects a row.
24682      * @param {Number} row The index of the row to deselect
24683      */
24684     deselectRow : function(index, preventViewNotify)
24685     {
24686         if(this.locked) {
24687             return;
24688         }
24689         if(this.last == index){
24690             this.last = false;
24691         }
24692         if(this.lastActive == index){
24693             this.lastActive = false;
24694         }
24695         
24696         var r = this.grid.store.getAt(index);
24697         if (!r) {
24698             return;
24699         }
24700         
24701         this.selections.remove(r);
24702         //.console.log('deselectRow - record id :' + r.id);
24703         if(!preventViewNotify){
24704         
24705             var proxy = new Roo.Element(
24706                 this.grid.getRowDom(index)
24707             );
24708             proxy.removeClass('bg-info info');
24709         }
24710         this.fireEvent("rowdeselect", this, index);
24711         this.fireEvent("selectionchange", this);
24712     },
24713
24714     // private
24715     restoreLast : function(){
24716         if(this._last){
24717             this.last = this._last;
24718         }
24719     },
24720
24721     // private
24722     acceptsNav : function(row, col, cm){
24723         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24724     },
24725
24726     // private
24727     onEditorKey : function(field, e){
24728         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24729         if(k == e.TAB){
24730             e.stopEvent();
24731             ed.completeEdit();
24732             if(e.shiftKey){
24733                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24734             }else{
24735                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24736             }
24737         }else if(k == e.ENTER && !e.ctrlKey){
24738             e.stopEvent();
24739             ed.completeEdit();
24740             if(e.shiftKey){
24741                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24742             }else{
24743                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24744             }
24745         }else if(k == e.ESC){
24746             ed.cancelEdit();
24747         }
24748         if(newCell){
24749             g.startEditing(newCell[0], newCell[1]);
24750         }
24751     }
24752 });
24753 /*
24754  * Based on:
24755  * Ext JS Library 1.1.1
24756  * Copyright(c) 2006-2007, Ext JS, LLC.
24757  *
24758  * Originally Released Under LGPL - original licence link has changed is not relivant.
24759  *
24760  * Fork - LGPL
24761  * <script type="text/javascript">
24762  */
24763  
24764 /**
24765  * @class Roo.bootstrap.PagingToolbar
24766  * @extends Roo.bootstrap.NavSimplebar
24767  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24768  * @constructor
24769  * Create a new PagingToolbar
24770  * @param {Object} config The config object
24771  * @param {Roo.data.Store} store
24772  */
24773 Roo.bootstrap.PagingToolbar = function(config)
24774 {
24775     // old args format still supported... - xtype is prefered..
24776         // created from xtype...
24777     
24778     this.ds = config.dataSource;
24779     
24780     if (config.store && !this.ds) {
24781         this.store= Roo.factory(config.store, Roo.data);
24782         this.ds = this.store;
24783         this.ds.xmodule = this.xmodule || false;
24784     }
24785     
24786     this.toolbarItems = [];
24787     if (config.items) {
24788         this.toolbarItems = config.items;
24789     }
24790     
24791     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24792     
24793     this.cursor = 0;
24794     
24795     if (this.ds) { 
24796         this.bind(this.ds);
24797     }
24798     
24799     if (Roo.bootstrap.version == 4) {
24800         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24801     } else {
24802         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24803     }
24804     
24805 };
24806
24807 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24808     /**
24809      * @cfg {Roo.data.Store} dataSource
24810      * The underlying data store providing the paged data
24811      */
24812     /**
24813      * @cfg {String/HTMLElement/Element} container
24814      * container The id or element that will contain the toolbar
24815      */
24816     /**
24817      * @cfg {Boolean} displayInfo
24818      * True to display the displayMsg (defaults to false)
24819      */
24820     /**
24821      * @cfg {Number} pageSize
24822      * The number of records to display per page (defaults to 20)
24823      */
24824     pageSize: 20,
24825     /**
24826      * @cfg {String} displayMsg
24827      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24828      */
24829     displayMsg : 'Displaying {0} - {1} of {2}',
24830     /**
24831      * @cfg {String} emptyMsg
24832      * The message to display when no records are found (defaults to "No data to display")
24833      */
24834     emptyMsg : 'No data to display',
24835     /**
24836      * Customizable piece of the default paging text (defaults to "Page")
24837      * @type String
24838      */
24839     beforePageText : "Page",
24840     /**
24841      * Customizable piece of the default paging text (defaults to "of %0")
24842      * @type String
24843      */
24844     afterPageText : "of {0}",
24845     /**
24846      * Customizable piece of the default paging text (defaults to "First Page")
24847      * @type String
24848      */
24849     firstText : "First Page",
24850     /**
24851      * Customizable piece of the default paging text (defaults to "Previous Page")
24852      * @type String
24853      */
24854     prevText : "Previous Page",
24855     /**
24856      * Customizable piece of the default paging text (defaults to "Next Page")
24857      * @type String
24858      */
24859     nextText : "Next Page",
24860     /**
24861      * Customizable piece of the default paging text (defaults to "Last Page")
24862      * @type String
24863      */
24864     lastText : "Last Page",
24865     /**
24866      * Customizable piece of the default paging text (defaults to "Refresh")
24867      * @type String
24868      */
24869     refreshText : "Refresh",
24870
24871     buttons : false,
24872     // private
24873     onRender : function(ct, position) 
24874     {
24875         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24876         this.navgroup.parentId = this.id;
24877         this.navgroup.onRender(this.el, null);
24878         // add the buttons to the navgroup
24879         
24880         if(this.displayInfo){
24881             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24882             this.displayEl = this.el.select('.x-paging-info', true).first();
24883 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24884 //            this.displayEl = navel.el.select('span',true).first();
24885         }
24886         
24887         var _this = this;
24888         
24889         if(this.buttons){
24890             Roo.each(_this.buttons, function(e){ // this might need to use render????
24891                Roo.factory(e).render(_this.el);
24892             });
24893         }
24894             
24895         Roo.each(_this.toolbarItems, function(e) {
24896             _this.navgroup.addItem(e);
24897         });
24898         
24899         
24900         this.first = this.navgroup.addItem({
24901             tooltip: this.firstText,
24902             cls: "prev btn-outline-secondary",
24903             html : ' <i class="fa fa-step-backward"></i>',
24904             disabled: true,
24905             preventDefault: true,
24906             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24907         });
24908         
24909         this.prev =  this.navgroup.addItem({
24910             tooltip: this.prevText,
24911             cls: "prev btn-outline-secondary",
24912             html : ' <i class="fa fa-backward"></i>',
24913             disabled: true,
24914             preventDefault: true,
24915             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24916         });
24917     //this.addSeparator();
24918         
24919         
24920         var field = this.navgroup.addItem( {
24921             tagtype : 'span',
24922             cls : 'x-paging-position  btn-outline-secondary',
24923              disabled: true,
24924             html : this.beforePageText  +
24925                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24926                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24927          } ); //?? escaped?
24928         
24929         this.field = field.el.select('input', true).first();
24930         this.field.on("keydown", this.onPagingKeydown, this);
24931         this.field.on("focus", function(){this.dom.select();});
24932     
24933     
24934         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24935         //this.field.setHeight(18);
24936         //this.addSeparator();
24937         this.next = this.navgroup.addItem({
24938             tooltip: this.nextText,
24939             cls: "next btn-outline-secondary",
24940             html : ' <i class="fa fa-forward"></i>',
24941             disabled: true,
24942             preventDefault: true,
24943             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24944         });
24945         this.last = this.navgroup.addItem({
24946             tooltip: this.lastText,
24947             html : ' <i class="fa fa-step-forward"></i>',
24948             cls: "next btn-outline-secondary",
24949             disabled: true,
24950             preventDefault: true,
24951             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24952         });
24953     //this.addSeparator();
24954         this.loading = this.navgroup.addItem({
24955             tooltip: this.refreshText,
24956             cls: "btn-outline-secondary",
24957             html : ' <i class="fa fa-refresh"></i>',
24958             preventDefault: true,
24959             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24960         });
24961         
24962     },
24963
24964     // private
24965     updateInfo : function(){
24966         if(this.displayEl){
24967             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24968             var msg = count == 0 ?
24969                 this.emptyMsg :
24970                 String.format(
24971                     this.displayMsg,
24972                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24973                 );
24974             this.displayEl.update(msg);
24975         }
24976     },
24977
24978     // private
24979     onLoad : function(ds, r, o)
24980     {
24981         this.cursor = o.params.start ? o.params.start : 0;
24982         
24983         var d = this.getPageData(),
24984             ap = d.activePage,
24985             ps = d.pages;
24986         
24987         
24988         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24989         this.field.dom.value = ap;
24990         this.first.setDisabled(ap == 1);
24991         this.prev.setDisabled(ap == 1);
24992         this.next.setDisabled(ap == ps);
24993         this.last.setDisabled(ap == ps);
24994         this.loading.enable();
24995         this.updateInfo();
24996     },
24997
24998     // private
24999     getPageData : function(){
25000         var total = this.ds.getTotalCount();
25001         return {
25002             total : total,
25003             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25004             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25005         };
25006     },
25007
25008     // private
25009     onLoadError : function(){
25010         this.loading.enable();
25011     },
25012
25013     // private
25014     onPagingKeydown : function(e){
25015         var k = e.getKey();
25016         var d = this.getPageData();
25017         if(k == e.RETURN){
25018             var v = this.field.dom.value, pageNum;
25019             if(!v || isNaN(pageNum = parseInt(v, 10))){
25020                 this.field.dom.value = d.activePage;
25021                 return;
25022             }
25023             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25024             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25025             e.stopEvent();
25026         }
25027         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))
25028         {
25029           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25030           this.field.dom.value = pageNum;
25031           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25032           e.stopEvent();
25033         }
25034         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25035         {
25036           var v = this.field.dom.value, pageNum; 
25037           var increment = (e.shiftKey) ? 10 : 1;
25038           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25039                 increment *= -1;
25040           }
25041           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25042             this.field.dom.value = d.activePage;
25043             return;
25044           }
25045           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25046           {
25047             this.field.dom.value = parseInt(v, 10) + increment;
25048             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25049             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25050           }
25051           e.stopEvent();
25052         }
25053     },
25054
25055     // private
25056     beforeLoad : function(){
25057         if(this.loading){
25058             this.loading.disable();
25059         }
25060     },
25061
25062     // private
25063     onClick : function(which){
25064         
25065         var ds = this.ds;
25066         if (!ds) {
25067             return;
25068         }
25069         
25070         switch(which){
25071             case "first":
25072                 ds.load({params:{start: 0, limit: this.pageSize}});
25073             break;
25074             case "prev":
25075                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25076             break;
25077             case "next":
25078                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25079             break;
25080             case "last":
25081                 var total = ds.getTotalCount();
25082                 var extra = total % this.pageSize;
25083                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25084                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25085             break;
25086             case "refresh":
25087                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25088             break;
25089         }
25090     },
25091
25092     /**
25093      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25094      * @param {Roo.data.Store} store The data store to unbind
25095      */
25096     unbind : function(ds){
25097         ds.un("beforeload", this.beforeLoad, this);
25098         ds.un("load", this.onLoad, this);
25099         ds.un("loadexception", this.onLoadError, this);
25100         ds.un("remove", this.updateInfo, this);
25101         ds.un("add", this.updateInfo, this);
25102         this.ds = undefined;
25103     },
25104
25105     /**
25106      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25107      * @param {Roo.data.Store} store The data store to bind
25108      */
25109     bind : function(ds){
25110         ds.on("beforeload", this.beforeLoad, this);
25111         ds.on("load", this.onLoad, this);
25112         ds.on("loadexception", this.onLoadError, this);
25113         ds.on("remove", this.updateInfo, this);
25114         ds.on("add", this.updateInfo, this);
25115         this.ds = ds;
25116     }
25117 });/*
25118  * - LGPL
25119  *
25120  * element
25121  * 
25122  */
25123
25124 /**
25125  * @class Roo.bootstrap.MessageBar
25126  * @extends Roo.bootstrap.Component
25127  * Bootstrap MessageBar class
25128  * @cfg {String} html contents of the MessageBar
25129  * @cfg {String} weight (info | success | warning | danger) default info
25130  * @cfg {String} beforeClass insert the bar before the given class
25131  * @cfg {Boolean} closable (true | false) default false
25132  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25133  * 
25134  * @constructor
25135  * Create a new Element
25136  * @param {Object} config The config object
25137  */
25138
25139 Roo.bootstrap.MessageBar = function(config){
25140     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25141 };
25142
25143 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25144     
25145     html: '',
25146     weight: 'info',
25147     closable: false,
25148     fixed: false,
25149     beforeClass: 'bootstrap-sticky-wrap',
25150     
25151     getAutoCreate : function(){
25152         
25153         var cfg = {
25154             tag: 'div',
25155             cls: 'alert alert-dismissable alert-' + this.weight,
25156             cn: [
25157                 {
25158                     tag: 'span',
25159                     cls: 'message',
25160                     html: this.html || ''
25161                 }
25162             ]
25163         };
25164         
25165         if(this.fixed){
25166             cfg.cls += ' alert-messages-fixed';
25167         }
25168         
25169         if(this.closable){
25170             cfg.cn.push({
25171                 tag: 'button',
25172                 cls: 'close',
25173                 html: 'x'
25174             });
25175         }
25176         
25177         return cfg;
25178     },
25179     
25180     onRender : function(ct, position)
25181     {
25182         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25183         
25184         if(!this.el){
25185             var cfg = Roo.apply({},  this.getAutoCreate());
25186             cfg.id = Roo.id();
25187             
25188             if (this.cls) {
25189                 cfg.cls += ' ' + this.cls;
25190             }
25191             if (this.style) {
25192                 cfg.style = this.style;
25193             }
25194             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25195             
25196             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25197         }
25198         
25199         this.el.select('>button.close').on('click', this.hide, this);
25200         
25201     },
25202     
25203     show : function()
25204     {
25205         if (!this.rendered) {
25206             this.render();
25207         }
25208         
25209         this.el.show();
25210         
25211         this.fireEvent('show', this);
25212         
25213     },
25214     
25215     hide : function()
25216     {
25217         if (!this.rendered) {
25218             this.render();
25219         }
25220         
25221         this.el.hide();
25222         
25223         this.fireEvent('hide', this);
25224     },
25225     
25226     update : function()
25227     {
25228 //        var e = this.el.dom.firstChild;
25229 //        
25230 //        if(this.closable){
25231 //            e = e.nextSibling;
25232 //        }
25233 //        
25234 //        e.data = this.html || '';
25235
25236         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25237     }
25238    
25239 });
25240
25241  
25242
25243      /*
25244  * - LGPL
25245  *
25246  * Graph
25247  * 
25248  */
25249
25250
25251 /**
25252  * @class Roo.bootstrap.Graph
25253  * @extends Roo.bootstrap.Component
25254  * Bootstrap Graph class
25255 > Prameters
25256  -sm {number} sm 4
25257  -md {number} md 5
25258  @cfg {String} graphtype  bar | vbar | pie
25259  @cfg {number} g_x coodinator | centre x (pie)
25260  @cfg {number} g_y coodinator | centre y (pie)
25261  @cfg {number} g_r radius (pie)
25262  @cfg {number} g_height height of the chart (respected by all elements in the set)
25263  @cfg {number} g_width width of the chart (respected by all elements in the set)
25264  @cfg {Object} title The title of the chart
25265     
25266  -{Array}  values
25267  -opts (object) options for the chart 
25268      o {
25269      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25270      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25271      o vgutter (number)
25272      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.
25273      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25274      o to
25275      o stretch (boolean)
25276      o }
25277  -opts (object) options for the pie
25278      o{
25279      o cut
25280      o startAngle (number)
25281      o endAngle (number)
25282      } 
25283  *
25284  * @constructor
25285  * Create a new Input
25286  * @param {Object} config The config object
25287  */
25288
25289 Roo.bootstrap.Graph = function(config){
25290     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25291     
25292     this.addEvents({
25293         // img events
25294         /**
25295          * @event click
25296          * The img click event for the img.
25297          * @param {Roo.EventObject} e
25298          */
25299         "click" : true
25300     });
25301 };
25302
25303 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25304     
25305     sm: 4,
25306     md: 5,
25307     graphtype: 'bar',
25308     g_height: 250,
25309     g_width: 400,
25310     g_x: 50,
25311     g_y: 50,
25312     g_r: 30,
25313     opts:{
25314         //g_colors: this.colors,
25315         g_type: 'soft',
25316         g_gutter: '20%'
25317
25318     },
25319     title : false,
25320
25321     getAutoCreate : function(){
25322         
25323         var cfg = {
25324             tag: 'div',
25325             html : null
25326         };
25327         
25328         
25329         return  cfg;
25330     },
25331
25332     onRender : function(ct,position){
25333         
25334         
25335         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25336         
25337         if (typeof(Raphael) == 'undefined') {
25338             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25339             return;
25340         }
25341         
25342         this.raphael = Raphael(this.el.dom);
25343         
25344                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25345                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25346                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25347                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25348                 /*
25349                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25350                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25351                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25352                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25353                 
25354                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25355                 r.barchart(330, 10, 300, 220, data1);
25356                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25357                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25358                 */
25359                 
25360                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25361                 // r.barchart(30, 30, 560, 250,  xdata, {
25362                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25363                 //     axis : "0 0 1 1",
25364                 //     axisxlabels :  xdata
25365                 //     //yvalues : cols,
25366                    
25367                 // });
25368 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25369 //        
25370 //        this.load(null,xdata,{
25371 //                axis : "0 0 1 1",
25372 //                axisxlabels :  xdata
25373 //                });
25374
25375     },
25376
25377     load : function(graphtype,xdata,opts)
25378     {
25379         this.raphael.clear();
25380         if(!graphtype) {
25381             graphtype = this.graphtype;
25382         }
25383         if(!opts){
25384             opts = this.opts;
25385         }
25386         var r = this.raphael,
25387             fin = function () {
25388                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25389             },
25390             fout = function () {
25391                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25392             },
25393             pfin = function() {
25394                 this.sector.stop();
25395                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25396
25397                 if (this.label) {
25398                     this.label[0].stop();
25399                     this.label[0].attr({ r: 7.5 });
25400                     this.label[1].attr({ "font-weight": 800 });
25401                 }
25402             },
25403             pfout = function() {
25404                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25405
25406                 if (this.label) {
25407                     this.label[0].animate({ r: 5 }, 500, "bounce");
25408                     this.label[1].attr({ "font-weight": 400 });
25409                 }
25410             };
25411
25412         switch(graphtype){
25413             case 'bar':
25414                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25415                 break;
25416             case 'hbar':
25417                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25418                 break;
25419             case 'pie':
25420 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25421 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25422 //            
25423                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25424                 
25425                 break;
25426
25427         }
25428         
25429         if(this.title){
25430             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25431         }
25432         
25433     },
25434     
25435     setTitle: function(o)
25436     {
25437         this.title = o;
25438     },
25439     
25440     initEvents: function() {
25441         
25442         if(!this.href){
25443             this.el.on('click', this.onClick, this);
25444         }
25445     },
25446     
25447     onClick : function(e)
25448     {
25449         Roo.log('img onclick');
25450         this.fireEvent('click', this, e);
25451     }
25452    
25453 });
25454
25455  
25456 /*
25457  * - LGPL
25458  *
25459  * numberBox
25460  * 
25461  */
25462 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25463
25464 /**
25465  * @class Roo.bootstrap.dash.NumberBox
25466  * @extends Roo.bootstrap.Component
25467  * Bootstrap NumberBox class
25468  * @cfg {String} headline Box headline
25469  * @cfg {String} content Box content
25470  * @cfg {String} icon Box icon
25471  * @cfg {String} footer Footer text
25472  * @cfg {String} fhref Footer href
25473  * 
25474  * @constructor
25475  * Create a new NumberBox
25476  * @param {Object} config The config object
25477  */
25478
25479
25480 Roo.bootstrap.dash.NumberBox = function(config){
25481     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25482     
25483 };
25484
25485 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25486     
25487     headline : '',
25488     content : '',
25489     icon : '',
25490     footer : '',
25491     fhref : '',
25492     ficon : '',
25493     
25494     getAutoCreate : function(){
25495         
25496         var cfg = {
25497             tag : 'div',
25498             cls : 'small-box ',
25499             cn : [
25500                 {
25501                     tag : 'div',
25502                     cls : 'inner',
25503                     cn :[
25504                         {
25505                             tag : 'h3',
25506                             cls : 'roo-headline',
25507                             html : this.headline
25508                         },
25509                         {
25510                             tag : 'p',
25511                             cls : 'roo-content',
25512                             html : this.content
25513                         }
25514                     ]
25515                 }
25516             ]
25517         };
25518         
25519         if(this.icon){
25520             cfg.cn.push({
25521                 tag : 'div',
25522                 cls : 'icon',
25523                 cn :[
25524                     {
25525                         tag : 'i',
25526                         cls : 'ion ' + this.icon
25527                     }
25528                 ]
25529             });
25530         }
25531         
25532         if(this.footer){
25533             var footer = {
25534                 tag : 'a',
25535                 cls : 'small-box-footer',
25536                 href : this.fhref || '#',
25537                 html : this.footer
25538             };
25539             
25540             cfg.cn.push(footer);
25541             
25542         }
25543         
25544         return  cfg;
25545     },
25546
25547     onRender : function(ct,position){
25548         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25549
25550
25551        
25552                 
25553     },
25554
25555     setHeadline: function (value)
25556     {
25557         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25558     },
25559     
25560     setFooter: function (value, href)
25561     {
25562         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25563         
25564         if(href){
25565             this.el.select('a.small-box-footer',true).first().attr('href', href);
25566         }
25567         
25568     },
25569
25570     setContent: function (value)
25571     {
25572         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25573     },
25574
25575     initEvents: function() 
25576     {   
25577         
25578     }
25579     
25580 });
25581
25582  
25583 /*
25584  * - LGPL
25585  *
25586  * TabBox
25587  * 
25588  */
25589 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25590
25591 /**
25592  * @class Roo.bootstrap.dash.TabBox
25593  * @extends Roo.bootstrap.Component
25594  * Bootstrap TabBox class
25595  * @cfg {String} title Title of the TabBox
25596  * @cfg {String} icon Icon of the TabBox
25597  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25598  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25599  * 
25600  * @constructor
25601  * Create a new TabBox
25602  * @param {Object} config The config object
25603  */
25604
25605
25606 Roo.bootstrap.dash.TabBox = function(config){
25607     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25608     this.addEvents({
25609         // raw events
25610         /**
25611          * @event addpane
25612          * When a pane is added
25613          * @param {Roo.bootstrap.dash.TabPane} pane
25614          */
25615         "addpane" : true,
25616         /**
25617          * @event activatepane
25618          * When a pane is activated
25619          * @param {Roo.bootstrap.dash.TabPane} pane
25620          */
25621         "activatepane" : true
25622         
25623          
25624     });
25625     
25626     this.panes = [];
25627 };
25628
25629 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25630
25631     title : '',
25632     icon : false,
25633     showtabs : true,
25634     tabScrollable : false,
25635     
25636     getChildContainer : function()
25637     {
25638         return this.el.select('.tab-content', true).first();
25639     },
25640     
25641     getAutoCreate : function(){
25642         
25643         var header = {
25644             tag: 'li',
25645             cls: 'pull-left header',
25646             html: this.title,
25647             cn : []
25648         };
25649         
25650         if(this.icon){
25651             header.cn.push({
25652                 tag: 'i',
25653                 cls: 'fa ' + this.icon
25654             });
25655         }
25656         
25657         var h = {
25658             tag: 'ul',
25659             cls: 'nav nav-tabs pull-right',
25660             cn: [
25661                 header
25662             ]
25663         };
25664         
25665         if(this.tabScrollable){
25666             h = {
25667                 tag: 'div',
25668                 cls: 'tab-header',
25669                 cn: [
25670                     {
25671                         tag: 'ul',
25672                         cls: 'nav nav-tabs pull-right',
25673                         cn: [
25674                             header
25675                         ]
25676                     }
25677                 ]
25678             };
25679         }
25680         
25681         var cfg = {
25682             tag: 'div',
25683             cls: 'nav-tabs-custom',
25684             cn: [
25685                 h,
25686                 {
25687                     tag: 'div',
25688                     cls: 'tab-content no-padding',
25689                     cn: []
25690                 }
25691             ]
25692         };
25693
25694         return  cfg;
25695     },
25696     initEvents : function()
25697     {
25698         //Roo.log('add add pane handler');
25699         this.on('addpane', this.onAddPane, this);
25700     },
25701      /**
25702      * Updates the box title
25703      * @param {String} html to set the title to.
25704      */
25705     setTitle : function(value)
25706     {
25707         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25708     },
25709     onAddPane : function(pane)
25710     {
25711         this.panes.push(pane);
25712         //Roo.log('addpane');
25713         //Roo.log(pane);
25714         // tabs are rendere left to right..
25715         if(!this.showtabs){
25716             return;
25717         }
25718         
25719         var ctr = this.el.select('.nav-tabs', true).first();
25720          
25721          
25722         var existing = ctr.select('.nav-tab',true);
25723         var qty = existing.getCount();;
25724         
25725         
25726         var tab = ctr.createChild({
25727             tag : 'li',
25728             cls : 'nav-tab' + (qty ? '' : ' active'),
25729             cn : [
25730                 {
25731                     tag : 'a',
25732                     href:'#',
25733                     html : pane.title
25734                 }
25735             ]
25736         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25737         pane.tab = tab;
25738         
25739         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25740         if (!qty) {
25741             pane.el.addClass('active');
25742         }
25743         
25744                 
25745     },
25746     onTabClick : function(ev,un,ob,pane)
25747     {
25748         //Roo.log('tab - prev default');
25749         ev.preventDefault();
25750         
25751         
25752         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25753         pane.tab.addClass('active');
25754         //Roo.log(pane.title);
25755         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25756         // technically we should have a deactivate event.. but maybe add later.
25757         // and it should not de-activate the selected tab...
25758         this.fireEvent('activatepane', pane);
25759         pane.el.addClass('active');
25760         pane.fireEvent('activate');
25761         
25762         
25763     },
25764     
25765     getActivePane : function()
25766     {
25767         var r = false;
25768         Roo.each(this.panes, function(p) {
25769             if(p.el.hasClass('active')){
25770                 r = p;
25771                 return false;
25772             }
25773             
25774             return;
25775         });
25776         
25777         return r;
25778     }
25779     
25780     
25781 });
25782
25783  
25784 /*
25785  * - LGPL
25786  *
25787  * Tab pane
25788  * 
25789  */
25790 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25791 /**
25792  * @class Roo.bootstrap.TabPane
25793  * @extends Roo.bootstrap.Component
25794  * Bootstrap TabPane class
25795  * @cfg {Boolean} active (false | true) Default false
25796  * @cfg {String} title title of panel
25797
25798  * 
25799  * @constructor
25800  * Create a new TabPane
25801  * @param {Object} config The config object
25802  */
25803
25804 Roo.bootstrap.dash.TabPane = function(config){
25805     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25806     
25807     this.addEvents({
25808         // raw events
25809         /**
25810          * @event activate
25811          * When a pane is activated
25812          * @param {Roo.bootstrap.dash.TabPane} pane
25813          */
25814         "activate" : true
25815          
25816     });
25817 };
25818
25819 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25820     
25821     active : false,
25822     title : '',
25823     
25824     // the tabBox that this is attached to.
25825     tab : false,
25826      
25827     getAutoCreate : function() 
25828     {
25829         var cfg = {
25830             tag: 'div',
25831             cls: 'tab-pane'
25832         };
25833         
25834         if(this.active){
25835             cfg.cls += ' active';
25836         }
25837         
25838         return cfg;
25839     },
25840     initEvents  : function()
25841     {
25842         //Roo.log('trigger add pane handler');
25843         this.parent().fireEvent('addpane', this)
25844     },
25845     
25846      /**
25847      * Updates the tab title 
25848      * @param {String} html to set the title to.
25849      */
25850     setTitle: function(str)
25851     {
25852         if (!this.tab) {
25853             return;
25854         }
25855         this.title = str;
25856         this.tab.select('a', true).first().dom.innerHTML = str;
25857         
25858     }
25859     
25860     
25861     
25862 });
25863
25864  
25865
25866
25867  /*
25868  * - LGPL
25869  *
25870  * menu
25871  * 
25872  */
25873 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25874
25875 /**
25876  * @class Roo.bootstrap.menu.Menu
25877  * @extends Roo.bootstrap.Component
25878  * Bootstrap Menu class - container for Menu
25879  * @cfg {String} html Text of the menu
25880  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25881  * @cfg {String} icon Font awesome icon
25882  * @cfg {String} pos Menu align to (top | bottom) default bottom
25883  * 
25884  * 
25885  * @constructor
25886  * Create a new Menu
25887  * @param {Object} config The config object
25888  */
25889
25890
25891 Roo.bootstrap.menu.Menu = function(config){
25892     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25893     
25894     this.addEvents({
25895         /**
25896          * @event beforeshow
25897          * Fires before this menu is displayed
25898          * @param {Roo.bootstrap.menu.Menu} this
25899          */
25900         beforeshow : true,
25901         /**
25902          * @event beforehide
25903          * Fires before this menu is hidden
25904          * @param {Roo.bootstrap.menu.Menu} this
25905          */
25906         beforehide : true,
25907         /**
25908          * @event show
25909          * Fires after this menu is displayed
25910          * @param {Roo.bootstrap.menu.Menu} this
25911          */
25912         show : true,
25913         /**
25914          * @event hide
25915          * Fires after this menu is hidden
25916          * @param {Roo.bootstrap.menu.Menu} this
25917          */
25918         hide : true,
25919         /**
25920          * @event click
25921          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25922          * @param {Roo.bootstrap.menu.Menu} this
25923          * @param {Roo.EventObject} e
25924          */
25925         click : true
25926     });
25927     
25928 };
25929
25930 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25931     
25932     submenu : false,
25933     html : '',
25934     weight : 'default',
25935     icon : false,
25936     pos : 'bottom',
25937     
25938     
25939     getChildContainer : function() {
25940         if(this.isSubMenu){
25941             return this.el;
25942         }
25943         
25944         return this.el.select('ul.dropdown-menu', true).first();  
25945     },
25946     
25947     getAutoCreate : function()
25948     {
25949         var text = [
25950             {
25951                 tag : 'span',
25952                 cls : 'roo-menu-text',
25953                 html : this.html
25954             }
25955         ];
25956         
25957         if(this.icon){
25958             text.unshift({
25959                 tag : 'i',
25960                 cls : 'fa ' + this.icon
25961             })
25962         }
25963         
25964         
25965         var cfg = {
25966             tag : 'div',
25967             cls : 'btn-group',
25968             cn : [
25969                 {
25970                     tag : 'button',
25971                     cls : 'dropdown-button btn btn-' + this.weight,
25972                     cn : text
25973                 },
25974                 {
25975                     tag : 'button',
25976                     cls : 'dropdown-toggle btn btn-' + this.weight,
25977                     cn : [
25978                         {
25979                             tag : 'span',
25980                             cls : 'caret'
25981                         }
25982                     ]
25983                 },
25984                 {
25985                     tag : 'ul',
25986                     cls : 'dropdown-menu'
25987                 }
25988             ]
25989             
25990         };
25991         
25992         if(this.pos == 'top'){
25993             cfg.cls += ' dropup';
25994         }
25995         
25996         if(this.isSubMenu){
25997             cfg = {
25998                 tag : 'ul',
25999                 cls : 'dropdown-menu'
26000             }
26001         }
26002         
26003         return cfg;
26004     },
26005     
26006     onRender : function(ct, position)
26007     {
26008         this.isSubMenu = ct.hasClass('dropdown-submenu');
26009         
26010         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26011     },
26012     
26013     initEvents : function() 
26014     {
26015         if(this.isSubMenu){
26016             return;
26017         }
26018         
26019         this.hidden = true;
26020         
26021         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26022         this.triggerEl.on('click', this.onTriggerPress, this);
26023         
26024         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26025         this.buttonEl.on('click', this.onClick, this);
26026         
26027     },
26028     
26029     list : function()
26030     {
26031         if(this.isSubMenu){
26032             return this.el;
26033         }
26034         
26035         return this.el.select('ul.dropdown-menu', true).first();
26036     },
26037     
26038     onClick : function(e)
26039     {
26040         this.fireEvent("click", this, e);
26041     },
26042     
26043     onTriggerPress  : function(e)
26044     {   
26045         if (this.isVisible()) {
26046             this.hide();
26047         } else {
26048             this.show();
26049         }
26050     },
26051     
26052     isVisible : function(){
26053         return !this.hidden;
26054     },
26055     
26056     show : function()
26057     {
26058         this.fireEvent("beforeshow", this);
26059         
26060         this.hidden = false;
26061         this.el.addClass('open');
26062         
26063         Roo.get(document).on("mouseup", this.onMouseUp, this);
26064         
26065         this.fireEvent("show", this);
26066         
26067         
26068     },
26069     
26070     hide : function()
26071     {
26072         this.fireEvent("beforehide", this);
26073         
26074         this.hidden = true;
26075         this.el.removeClass('open');
26076         
26077         Roo.get(document).un("mouseup", this.onMouseUp);
26078         
26079         this.fireEvent("hide", this);
26080     },
26081     
26082     onMouseUp : function()
26083     {
26084         this.hide();
26085     }
26086     
26087 });
26088
26089  
26090  /*
26091  * - LGPL
26092  *
26093  * menu item
26094  * 
26095  */
26096 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26097
26098 /**
26099  * @class Roo.bootstrap.menu.Item
26100  * @extends Roo.bootstrap.Component
26101  * Bootstrap MenuItem class
26102  * @cfg {Boolean} submenu (true | false) default false
26103  * @cfg {String} html text of the item
26104  * @cfg {String} href the link
26105  * @cfg {Boolean} disable (true | false) default false
26106  * @cfg {Boolean} preventDefault (true | false) default true
26107  * @cfg {String} icon Font awesome icon
26108  * @cfg {String} pos Submenu align to (left | right) default right 
26109  * 
26110  * 
26111  * @constructor
26112  * Create a new Item
26113  * @param {Object} config The config object
26114  */
26115
26116
26117 Roo.bootstrap.menu.Item = function(config){
26118     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26119     this.addEvents({
26120         /**
26121          * @event mouseover
26122          * Fires when the mouse is hovering over this menu
26123          * @param {Roo.bootstrap.menu.Item} this
26124          * @param {Roo.EventObject} e
26125          */
26126         mouseover : true,
26127         /**
26128          * @event mouseout
26129          * Fires when the mouse exits this menu
26130          * @param {Roo.bootstrap.menu.Item} this
26131          * @param {Roo.EventObject} e
26132          */
26133         mouseout : true,
26134         // raw events
26135         /**
26136          * @event click
26137          * The raw click event for the entire grid.
26138          * @param {Roo.EventObject} e
26139          */
26140         click : true
26141     });
26142 };
26143
26144 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26145     
26146     submenu : false,
26147     href : '',
26148     html : '',
26149     preventDefault: true,
26150     disable : false,
26151     icon : false,
26152     pos : 'right',
26153     
26154     getAutoCreate : function()
26155     {
26156         var text = [
26157             {
26158                 tag : 'span',
26159                 cls : 'roo-menu-item-text',
26160                 html : this.html
26161             }
26162         ];
26163         
26164         if(this.icon){
26165             text.unshift({
26166                 tag : 'i',
26167                 cls : 'fa ' + this.icon
26168             })
26169         }
26170         
26171         var cfg = {
26172             tag : 'li',
26173             cn : [
26174                 {
26175                     tag : 'a',
26176                     href : this.href || '#',
26177                     cn : text
26178                 }
26179             ]
26180         };
26181         
26182         if(this.disable){
26183             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26184         }
26185         
26186         if(this.submenu){
26187             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26188             
26189             if(this.pos == 'left'){
26190                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26191             }
26192         }
26193         
26194         return cfg;
26195     },
26196     
26197     initEvents : function() 
26198     {
26199         this.el.on('mouseover', this.onMouseOver, this);
26200         this.el.on('mouseout', this.onMouseOut, this);
26201         
26202         this.el.select('a', true).first().on('click', this.onClick, this);
26203         
26204     },
26205     
26206     onClick : function(e)
26207     {
26208         if(this.preventDefault){
26209             e.preventDefault();
26210         }
26211         
26212         this.fireEvent("click", this, e);
26213     },
26214     
26215     onMouseOver : function(e)
26216     {
26217         if(this.submenu && this.pos == 'left'){
26218             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26219         }
26220         
26221         this.fireEvent("mouseover", this, e);
26222     },
26223     
26224     onMouseOut : function(e)
26225     {
26226         this.fireEvent("mouseout", this, e);
26227     }
26228 });
26229
26230  
26231
26232  /*
26233  * - LGPL
26234  *
26235  * menu separator
26236  * 
26237  */
26238 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26239
26240 /**
26241  * @class Roo.bootstrap.menu.Separator
26242  * @extends Roo.bootstrap.Component
26243  * Bootstrap Separator class
26244  * 
26245  * @constructor
26246  * Create a new Separator
26247  * @param {Object} config The config object
26248  */
26249
26250
26251 Roo.bootstrap.menu.Separator = function(config){
26252     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26253 };
26254
26255 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26256     
26257     getAutoCreate : function(){
26258         var cfg = {
26259             tag : 'li',
26260             cls: 'divider'
26261         };
26262         
26263         return cfg;
26264     }
26265    
26266 });
26267
26268  
26269
26270  /*
26271  * - LGPL
26272  *
26273  * Tooltip
26274  * 
26275  */
26276
26277 /**
26278  * @class Roo.bootstrap.Tooltip
26279  * Bootstrap Tooltip class
26280  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26281  * to determine which dom element triggers the tooltip.
26282  * 
26283  * It needs to add support for additional attributes like tooltip-position
26284  * 
26285  * @constructor
26286  * Create a new Toolti
26287  * @param {Object} config The config object
26288  */
26289
26290 Roo.bootstrap.Tooltip = function(config){
26291     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26292     
26293     this.alignment = Roo.bootstrap.Tooltip.alignment;
26294     
26295     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26296         this.alignment = config.alignment;
26297     }
26298     
26299 };
26300
26301 Roo.apply(Roo.bootstrap.Tooltip, {
26302     /**
26303      * @function init initialize tooltip monitoring.
26304      * @static
26305      */
26306     currentEl : false,
26307     currentTip : false,
26308     currentRegion : false,
26309     
26310     //  init : delay?
26311     
26312     init : function()
26313     {
26314         Roo.get(document).on('mouseover', this.enter ,this);
26315         Roo.get(document).on('mouseout', this.leave, this);
26316          
26317         
26318         this.currentTip = new Roo.bootstrap.Tooltip();
26319     },
26320     
26321     enter : function(ev)
26322     {
26323         var dom = ev.getTarget();
26324         
26325         //Roo.log(['enter',dom]);
26326         var el = Roo.fly(dom);
26327         if (this.currentEl) {
26328             //Roo.log(dom);
26329             //Roo.log(this.currentEl);
26330             //Roo.log(this.currentEl.contains(dom));
26331             if (this.currentEl == el) {
26332                 return;
26333             }
26334             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26335                 return;
26336             }
26337
26338         }
26339         
26340         if (this.currentTip.el) {
26341             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26342         }    
26343         //Roo.log(ev);
26344         
26345         if(!el || el.dom == document){
26346             return;
26347         }
26348         
26349         var bindEl = el;
26350         
26351         // you can not look for children, as if el is the body.. then everythign is the child..
26352         if (!el.attr('tooltip')) { //
26353             if (!el.select("[tooltip]").elements.length) {
26354                 return;
26355             }
26356             // is the mouse over this child...?
26357             bindEl = el.select("[tooltip]").first();
26358             var xy = ev.getXY();
26359             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26360                 //Roo.log("not in region.");
26361                 return;
26362             }
26363             //Roo.log("child element over..");
26364             
26365         }
26366         this.currentEl = bindEl;
26367         this.currentTip.bind(bindEl);
26368         this.currentRegion = Roo.lib.Region.getRegion(dom);
26369         this.currentTip.enter();
26370         
26371     },
26372     leave : function(ev)
26373     {
26374         var dom = ev.getTarget();
26375         //Roo.log(['leave',dom]);
26376         if (!this.currentEl) {
26377             return;
26378         }
26379         
26380         
26381         if (dom != this.currentEl.dom) {
26382             return;
26383         }
26384         var xy = ev.getXY();
26385         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26386             return;
26387         }
26388         // only activate leave if mouse cursor is outside... bounding box..
26389         
26390         
26391         
26392         
26393         if (this.currentTip) {
26394             this.currentTip.leave();
26395         }
26396         //Roo.log('clear currentEl');
26397         this.currentEl = false;
26398         
26399         
26400     },
26401     alignment : {
26402         'left' : ['r-l', [-2,0], 'right'],
26403         'right' : ['l-r', [2,0], 'left'],
26404         'bottom' : ['t-b', [0,2], 'top'],
26405         'top' : [ 'b-t', [0,-2], 'bottom']
26406     }
26407     
26408 });
26409
26410
26411 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26412     
26413     
26414     bindEl : false,
26415     
26416     delay : null, // can be { show : 300 , hide: 500}
26417     
26418     timeout : null,
26419     
26420     hoverState : null, //???
26421     
26422     placement : 'bottom', 
26423     
26424     alignment : false,
26425     
26426     getAutoCreate : function(){
26427     
26428         var cfg = {
26429            cls : 'tooltip',
26430            role : 'tooltip',
26431            cn : [
26432                 {
26433                     cls : 'tooltip-arrow'
26434                 },
26435                 {
26436                     cls : 'tooltip-inner'
26437                 }
26438            ]
26439         };
26440         
26441         return cfg;
26442     },
26443     bind : function(el)
26444     {
26445         this.bindEl = el;
26446     },
26447       
26448     
26449     enter : function () {
26450        
26451         if (this.timeout != null) {
26452             clearTimeout(this.timeout);
26453         }
26454         
26455         this.hoverState = 'in';
26456          //Roo.log("enter - show");
26457         if (!this.delay || !this.delay.show) {
26458             this.show();
26459             return;
26460         }
26461         var _t = this;
26462         this.timeout = setTimeout(function () {
26463             if (_t.hoverState == 'in') {
26464                 _t.show();
26465             }
26466         }, this.delay.show);
26467     },
26468     leave : function()
26469     {
26470         clearTimeout(this.timeout);
26471     
26472         this.hoverState = 'out';
26473          if (!this.delay || !this.delay.hide) {
26474             this.hide();
26475             return;
26476         }
26477        
26478         var _t = this;
26479         this.timeout = setTimeout(function () {
26480             //Roo.log("leave - timeout");
26481             
26482             if (_t.hoverState == 'out') {
26483                 _t.hide();
26484                 Roo.bootstrap.Tooltip.currentEl = false;
26485             }
26486         }, delay);
26487     },
26488     
26489     show : function (msg)
26490     {
26491         if (!this.el) {
26492             this.render(document.body);
26493         }
26494         // set content.
26495         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26496         
26497         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26498         
26499         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26500         
26501         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26502         
26503         var placement = typeof this.placement == 'function' ?
26504             this.placement.call(this, this.el, on_el) :
26505             this.placement;
26506             
26507         var autoToken = /\s?auto?\s?/i;
26508         var autoPlace = autoToken.test(placement);
26509         if (autoPlace) {
26510             placement = placement.replace(autoToken, '') || 'top';
26511         }
26512         
26513         //this.el.detach()
26514         //this.el.setXY([0,0]);
26515         this.el.show();
26516         //this.el.dom.style.display='block';
26517         
26518         //this.el.appendTo(on_el);
26519         
26520         var p = this.getPosition();
26521         var box = this.el.getBox();
26522         
26523         if (autoPlace) {
26524             // fixme..
26525         }
26526         
26527         var align = this.alignment[placement];
26528         
26529         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26530         
26531         if(placement == 'top' || placement == 'bottom'){
26532             if(xy[0] < 0){
26533                 placement = 'right';
26534             }
26535             
26536             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26537                 placement = 'left';
26538             }
26539             
26540             var scroll = Roo.select('body', true).first().getScroll();
26541             
26542             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26543                 placement = 'top';
26544             }
26545             
26546             align = this.alignment[placement];
26547         }
26548         
26549         this.el.alignTo(this.bindEl, align[0],align[1]);
26550         //var arrow = this.el.select('.arrow',true).first();
26551         //arrow.set(align[2], 
26552         
26553         this.el.addClass(placement);
26554         
26555         this.el.addClass('in fade');
26556         
26557         this.hoverState = null;
26558         
26559         if (this.el.hasClass('fade')) {
26560             // fade it?
26561         }
26562         
26563     },
26564     hide : function()
26565     {
26566          
26567         if (!this.el) {
26568             return;
26569         }
26570         //this.el.setXY([0,0]);
26571         this.el.removeClass('in');
26572         //this.el.hide();
26573         
26574     }
26575     
26576 });
26577  
26578
26579  /*
26580  * - LGPL
26581  *
26582  * Location Picker
26583  * 
26584  */
26585
26586 /**
26587  * @class Roo.bootstrap.LocationPicker
26588  * @extends Roo.bootstrap.Component
26589  * Bootstrap LocationPicker class
26590  * @cfg {Number} latitude Position when init default 0
26591  * @cfg {Number} longitude Position when init default 0
26592  * @cfg {Number} zoom default 15
26593  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26594  * @cfg {Boolean} mapTypeControl default false
26595  * @cfg {Boolean} disableDoubleClickZoom default false
26596  * @cfg {Boolean} scrollwheel default true
26597  * @cfg {Boolean} streetViewControl default false
26598  * @cfg {Number} radius default 0
26599  * @cfg {String} locationName
26600  * @cfg {Boolean} draggable default true
26601  * @cfg {Boolean} enableAutocomplete default false
26602  * @cfg {Boolean} enableReverseGeocode default true
26603  * @cfg {String} markerTitle
26604  * 
26605  * @constructor
26606  * Create a new LocationPicker
26607  * @param {Object} config The config object
26608  */
26609
26610
26611 Roo.bootstrap.LocationPicker = function(config){
26612     
26613     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26614     
26615     this.addEvents({
26616         /**
26617          * @event initial
26618          * Fires when the picker initialized.
26619          * @param {Roo.bootstrap.LocationPicker} this
26620          * @param {Google Location} location
26621          */
26622         initial : true,
26623         /**
26624          * @event positionchanged
26625          * Fires when the picker position changed.
26626          * @param {Roo.bootstrap.LocationPicker} this
26627          * @param {Google Location} location
26628          */
26629         positionchanged : true,
26630         /**
26631          * @event resize
26632          * Fires when the map resize.
26633          * @param {Roo.bootstrap.LocationPicker} this
26634          */
26635         resize : true,
26636         /**
26637          * @event show
26638          * Fires when the map show.
26639          * @param {Roo.bootstrap.LocationPicker} this
26640          */
26641         show : true,
26642         /**
26643          * @event hide
26644          * Fires when the map hide.
26645          * @param {Roo.bootstrap.LocationPicker} this
26646          */
26647         hide : true,
26648         /**
26649          * @event mapClick
26650          * Fires when click the map.
26651          * @param {Roo.bootstrap.LocationPicker} this
26652          * @param {Map event} e
26653          */
26654         mapClick : true,
26655         /**
26656          * @event mapRightClick
26657          * Fires when right click the map.
26658          * @param {Roo.bootstrap.LocationPicker} this
26659          * @param {Map event} e
26660          */
26661         mapRightClick : true,
26662         /**
26663          * @event markerClick
26664          * Fires when click the marker.
26665          * @param {Roo.bootstrap.LocationPicker} this
26666          * @param {Map event} e
26667          */
26668         markerClick : true,
26669         /**
26670          * @event markerRightClick
26671          * Fires when right click the marker.
26672          * @param {Roo.bootstrap.LocationPicker} this
26673          * @param {Map event} e
26674          */
26675         markerRightClick : true,
26676         /**
26677          * @event OverlayViewDraw
26678          * Fires when OverlayView Draw
26679          * @param {Roo.bootstrap.LocationPicker} this
26680          */
26681         OverlayViewDraw : true,
26682         /**
26683          * @event OverlayViewOnAdd
26684          * Fires when OverlayView Draw
26685          * @param {Roo.bootstrap.LocationPicker} this
26686          */
26687         OverlayViewOnAdd : true,
26688         /**
26689          * @event OverlayViewOnRemove
26690          * Fires when OverlayView Draw
26691          * @param {Roo.bootstrap.LocationPicker} this
26692          */
26693         OverlayViewOnRemove : true,
26694         /**
26695          * @event OverlayViewShow
26696          * Fires when OverlayView Draw
26697          * @param {Roo.bootstrap.LocationPicker} this
26698          * @param {Pixel} cpx
26699          */
26700         OverlayViewShow : true,
26701         /**
26702          * @event OverlayViewHide
26703          * Fires when OverlayView Draw
26704          * @param {Roo.bootstrap.LocationPicker} this
26705          */
26706         OverlayViewHide : true,
26707         /**
26708          * @event loadexception
26709          * Fires when load google lib failed.
26710          * @param {Roo.bootstrap.LocationPicker} this
26711          */
26712         loadexception : true
26713     });
26714         
26715 };
26716
26717 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26718     
26719     gMapContext: false,
26720     
26721     latitude: 0,
26722     longitude: 0,
26723     zoom: 15,
26724     mapTypeId: false,
26725     mapTypeControl: false,
26726     disableDoubleClickZoom: false,
26727     scrollwheel: true,
26728     streetViewControl: false,
26729     radius: 0,
26730     locationName: '',
26731     draggable: true,
26732     enableAutocomplete: false,
26733     enableReverseGeocode: true,
26734     markerTitle: '',
26735     
26736     getAutoCreate: function()
26737     {
26738
26739         var cfg = {
26740             tag: 'div',
26741             cls: 'roo-location-picker'
26742         };
26743         
26744         return cfg
26745     },
26746     
26747     initEvents: function(ct, position)
26748     {       
26749         if(!this.el.getWidth() || this.isApplied()){
26750             return;
26751         }
26752         
26753         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26754         
26755         this.initial();
26756     },
26757     
26758     initial: function()
26759     {
26760         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26761             this.fireEvent('loadexception', this);
26762             return;
26763         }
26764         
26765         if(!this.mapTypeId){
26766             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26767         }
26768         
26769         this.gMapContext = this.GMapContext();
26770         
26771         this.initOverlayView();
26772         
26773         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26774         
26775         var _this = this;
26776                 
26777         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26778             _this.setPosition(_this.gMapContext.marker.position);
26779         });
26780         
26781         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26782             _this.fireEvent('mapClick', this, event);
26783             
26784         });
26785
26786         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26787             _this.fireEvent('mapRightClick', this, event);
26788             
26789         });
26790         
26791         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26792             _this.fireEvent('markerClick', this, event);
26793             
26794         });
26795
26796         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26797             _this.fireEvent('markerRightClick', this, event);
26798             
26799         });
26800         
26801         this.setPosition(this.gMapContext.location);
26802         
26803         this.fireEvent('initial', this, this.gMapContext.location);
26804     },
26805     
26806     initOverlayView: function()
26807     {
26808         var _this = this;
26809         
26810         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26811             
26812             draw: function()
26813             {
26814                 _this.fireEvent('OverlayViewDraw', _this);
26815             },
26816             
26817             onAdd: function()
26818             {
26819                 _this.fireEvent('OverlayViewOnAdd', _this);
26820             },
26821             
26822             onRemove: function()
26823             {
26824                 _this.fireEvent('OverlayViewOnRemove', _this);
26825             },
26826             
26827             show: function(cpx)
26828             {
26829                 _this.fireEvent('OverlayViewShow', _this, cpx);
26830             },
26831             
26832             hide: function()
26833             {
26834                 _this.fireEvent('OverlayViewHide', _this);
26835             }
26836             
26837         });
26838     },
26839     
26840     fromLatLngToContainerPixel: function(event)
26841     {
26842         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26843     },
26844     
26845     isApplied: function() 
26846     {
26847         return this.getGmapContext() == false ? false : true;
26848     },
26849     
26850     getGmapContext: function() 
26851     {
26852         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26853     },
26854     
26855     GMapContext: function() 
26856     {
26857         var position = new google.maps.LatLng(this.latitude, this.longitude);
26858         
26859         var _map = new google.maps.Map(this.el.dom, {
26860             center: position,
26861             zoom: this.zoom,
26862             mapTypeId: this.mapTypeId,
26863             mapTypeControl: this.mapTypeControl,
26864             disableDoubleClickZoom: this.disableDoubleClickZoom,
26865             scrollwheel: this.scrollwheel,
26866             streetViewControl: this.streetViewControl,
26867             locationName: this.locationName,
26868             draggable: this.draggable,
26869             enableAutocomplete: this.enableAutocomplete,
26870             enableReverseGeocode: this.enableReverseGeocode
26871         });
26872         
26873         var _marker = new google.maps.Marker({
26874             position: position,
26875             map: _map,
26876             title: this.markerTitle,
26877             draggable: this.draggable
26878         });
26879         
26880         return {
26881             map: _map,
26882             marker: _marker,
26883             circle: null,
26884             location: position,
26885             radius: this.radius,
26886             locationName: this.locationName,
26887             addressComponents: {
26888                 formatted_address: null,
26889                 addressLine1: null,
26890                 addressLine2: null,
26891                 streetName: null,
26892                 streetNumber: null,
26893                 city: null,
26894                 district: null,
26895                 state: null,
26896                 stateOrProvince: null
26897             },
26898             settings: this,
26899             domContainer: this.el.dom,
26900             geodecoder: new google.maps.Geocoder()
26901         };
26902     },
26903     
26904     drawCircle: function(center, radius, options) 
26905     {
26906         if (this.gMapContext.circle != null) {
26907             this.gMapContext.circle.setMap(null);
26908         }
26909         if (radius > 0) {
26910             radius *= 1;
26911             options = Roo.apply({}, options, {
26912                 strokeColor: "#0000FF",
26913                 strokeOpacity: .35,
26914                 strokeWeight: 2,
26915                 fillColor: "#0000FF",
26916                 fillOpacity: .2
26917             });
26918             
26919             options.map = this.gMapContext.map;
26920             options.radius = radius;
26921             options.center = center;
26922             this.gMapContext.circle = new google.maps.Circle(options);
26923             return this.gMapContext.circle;
26924         }
26925         
26926         return null;
26927     },
26928     
26929     setPosition: function(location) 
26930     {
26931         this.gMapContext.location = location;
26932         this.gMapContext.marker.setPosition(location);
26933         this.gMapContext.map.panTo(location);
26934         this.drawCircle(location, this.gMapContext.radius, {});
26935         
26936         var _this = this;
26937         
26938         if (this.gMapContext.settings.enableReverseGeocode) {
26939             this.gMapContext.geodecoder.geocode({
26940                 latLng: this.gMapContext.location
26941             }, function(results, status) {
26942                 
26943                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26944                     _this.gMapContext.locationName = results[0].formatted_address;
26945                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26946                     
26947                     _this.fireEvent('positionchanged', this, location);
26948                 }
26949             });
26950             
26951             return;
26952         }
26953         
26954         this.fireEvent('positionchanged', this, location);
26955     },
26956     
26957     resize: function()
26958     {
26959         google.maps.event.trigger(this.gMapContext.map, "resize");
26960         
26961         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26962         
26963         this.fireEvent('resize', this);
26964     },
26965     
26966     setPositionByLatLng: function(latitude, longitude)
26967     {
26968         this.setPosition(new google.maps.LatLng(latitude, longitude));
26969     },
26970     
26971     getCurrentPosition: function() 
26972     {
26973         return {
26974             latitude: this.gMapContext.location.lat(),
26975             longitude: this.gMapContext.location.lng()
26976         };
26977     },
26978     
26979     getAddressName: function() 
26980     {
26981         return this.gMapContext.locationName;
26982     },
26983     
26984     getAddressComponents: function() 
26985     {
26986         return this.gMapContext.addressComponents;
26987     },
26988     
26989     address_component_from_google_geocode: function(address_components) 
26990     {
26991         var result = {};
26992         
26993         for (var i = 0; i < address_components.length; i++) {
26994             var component = address_components[i];
26995             if (component.types.indexOf("postal_code") >= 0) {
26996                 result.postalCode = component.short_name;
26997             } else if (component.types.indexOf("street_number") >= 0) {
26998                 result.streetNumber = component.short_name;
26999             } else if (component.types.indexOf("route") >= 0) {
27000                 result.streetName = component.short_name;
27001             } else if (component.types.indexOf("neighborhood") >= 0) {
27002                 result.city = component.short_name;
27003             } else if (component.types.indexOf("locality") >= 0) {
27004                 result.city = component.short_name;
27005             } else if (component.types.indexOf("sublocality") >= 0) {
27006                 result.district = component.short_name;
27007             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27008                 result.stateOrProvince = component.short_name;
27009             } else if (component.types.indexOf("country") >= 0) {
27010                 result.country = component.short_name;
27011             }
27012         }
27013         
27014         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27015         result.addressLine2 = "";
27016         return result;
27017     },
27018     
27019     setZoomLevel: function(zoom)
27020     {
27021         this.gMapContext.map.setZoom(zoom);
27022     },
27023     
27024     show: function()
27025     {
27026         if(!this.el){
27027             return;
27028         }
27029         
27030         this.el.show();
27031         
27032         this.resize();
27033         
27034         this.fireEvent('show', this);
27035     },
27036     
27037     hide: function()
27038     {
27039         if(!this.el){
27040             return;
27041         }
27042         
27043         this.el.hide();
27044         
27045         this.fireEvent('hide', this);
27046     }
27047     
27048 });
27049
27050 Roo.apply(Roo.bootstrap.LocationPicker, {
27051     
27052     OverlayView : function(map, options)
27053     {
27054         options = options || {};
27055         
27056         this.setMap(map);
27057     }
27058     
27059     
27060 });/*
27061  * - LGPL
27062  *
27063  * Alert
27064  * 
27065  */
27066
27067 /**
27068  * @class Roo.bootstrap.Alert
27069  * @extends Roo.bootstrap.Component
27070  * Bootstrap Alert class
27071  * @cfg {String} title The title of alert
27072  * @cfg {String} html The content of alert
27073  * @cfg {String} weight (  success | info | warning | danger )
27074  * @cfg {String} faicon font-awesomeicon
27075  * 
27076  * @constructor
27077  * Create a new alert
27078  * @param {Object} config The config object
27079  */
27080
27081
27082 Roo.bootstrap.Alert = function(config){
27083     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27084     
27085 };
27086
27087 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27088     
27089     title: '',
27090     html: '',
27091     weight: false,
27092     faicon: false,
27093     
27094     getAutoCreate : function()
27095     {
27096         
27097         var cfg = {
27098             tag : 'div',
27099             cls : 'alert',
27100             cn : [
27101                 {
27102                     tag : 'i',
27103                     cls : 'roo-alert-icon'
27104                     
27105                 },
27106                 {
27107                     tag : 'b',
27108                     cls : 'roo-alert-title',
27109                     html : this.title
27110                 },
27111                 {
27112                     tag : 'span',
27113                     cls : 'roo-alert-text',
27114                     html : this.html
27115                 }
27116             ]
27117         };
27118         
27119         if(this.faicon){
27120             cfg.cn[0].cls += ' fa ' + this.faicon;
27121         }
27122         
27123         if(this.weight){
27124             cfg.cls += ' alert-' + this.weight;
27125         }
27126         
27127         return cfg;
27128     },
27129     
27130     initEvents: function() 
27131     {
27132         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27133     },
27134     
27135     setTitle : function(str)
27136     {
27137         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27138     },
27139     
27140     setText : function(str)
27141     {
27142         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27143     },
27144     
27145     setWeight : function(weight)
27146     {
27147         if(this.weight){
27148             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27149         }
27150         
27151         this.weight = weight;
27152         
27153         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27154     },
27155     
27156     setIcon : function(icon)
27157     {
27158         if(this.faicon){
27159             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27160         }
27161         
27162         this.faicon = icon;
27163         
27164         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27165     },
27166     
27167     hide: function() 
27168     {
27169         this.el.hide();   
27170     },
27171     
27172     show: function() 
27173     {  
27174         this.el.show();   
27175     }
27176     
27177 });
27178
27179  
27180 /*
27181 * Licence: LGPL
27182 */
27183
27184 /**
27185  * @class Roo.bootstrap.UploadCropbox
27186  * @extends Roo.bootstrap.Component
27187  * Bootstrap UploadCropbox class
27188  * @cfg {String} emptyText show when image has been loaded
27189  * @cfg {String} rotateNotify show when image too small to rotate
27190  * @cfg {Number} errorTimeout default 3000
27191  * @cfg {Number} minWidth default 300
27192  * @cfg {Number} minHeight default 300
27193  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27194  * @cfg {Boolean} isDocument (true|false) default false
27195  * @cfg {String} url action url
27196  * @cfg {String} paramName default 'imageUpload'
27197  * @cfg {String} method default POST
27198  * @cfg {Boolean} loadMask (true|false) default true
27199  * @cfg {Boolean} loadingText default 'Loading...'
27200  * 
27201  * @constructor
27202  * Create a new UploadCropbox
27203  * @param {Object} config The config object
27204  */
27205
27206 Roo.bootstrap.UploadCropbox = function(config){
27207     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27208     
27209     this.addEvents({
27210         /**
27211          * @event beforeselectfile
27212          * Fire before select file
27213          * @param {Roo.bootstrap.UploadCropbox} this
27214          */
27215         "beforeselectfile" : true,
27216         /**
27217          * @event initial
27218          * Fire after initEvent
27219          * @param {Roo.bootstrap.UploadCropbox} this
27220          */
27221         "initial" : true,
27222         /**
27223          * @event crop
27224          * Fire after initEvent
27225          * @param {Roo.bootstrap.UploadCropbox} this
27226          * @param {String} data
27227          */
27228         "crop" : true,
27229         /**
27230          * @event prepare
27231          * Fire when preparing the file data
27232          * @param {Roo.bootstrap.UploadCropbox} this
27233          * @param {Object} file
27234          */
27235         "prepare" : true,
27236         /**
27237          * @event exception
27238          * Fire when get exception
27239          * @param {Roo.bootstrap.UploadCropbox} this
27240          * @param {XMLHttpRequest} xhr
27241          */
27242         "exception" : true,
27243         /**
27244          * @event beforeloadcanvas
27245          * Fire before load the canvas
27246          * @param {Roo.bootstrap.UploadCropbox} this
27247          * @param {String} src
27248          */
27249         "beforeloadcanvas" : true,
27250         /**
27251          * @event trash
27252          * Fire when trash image
27253          * @param {Roo.bootstrap.UploadCropbox} this
27254          */
27255         "trash" : true,
27256         /**
27257          * @event download
27258          * Fire when download the image
27259          * @param {Roo.bootstrap.UploadCropbox} this
27260          */
27261         "download" : true,
27262         /**
27263          * @event footerbuttonclick
27264          * Fire when footerbuttonclick
27265          * @param {Roo.bootstrap.UploadCropbox} this
27266          * @param {String} type
27267          */
27268         "footerbuttonclick" : true,
27269         /**
27270          * @event resize
27271          * Fire when resize
27272          * @param {Roo.bootstrap.UploadCropbox} this
27273          */
27274         "resize" : true,
27275         /**
27276          * @event rotate
27277          * Fire when rotate the image
27278          * @param {Roo.bootstrap.UploadCropbox} this
27279          * @param {String} pos
27280          */
27281         "rotate" : true,
27282         /**
27283          * @event inspect
27284          * Fire when inspect the file
27285          * @param {Roo.bootstrap.UploadCropbox} this
27286          * @param {Object} file
27287          */
27288         "inspect" : true,
27289         /**
27290          * @event upload
27291          * Fire when xhr upload the file
27292          * @param {Roo.bootstrap.UploadCropbox} this
27293          * @param {Object} data
27294          */
27295         "upload" : true,
27296         /**
27297          * @event arrange
27298          * Fire when arrange the file data
27299          * @param {Roo.bootstrap.UploadCropbox} this
27300          * @param {Object} formData
27301          */
27302         "arrange" : true
27303     });
27304     
27305     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27306 };
27307
27308 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27309     
27310     emptyText : 'Click to upload image',
27311     rotateNotify : 'Image is too small to rotate',
27312     errorTimeout : 3000,
27313     scale : 0,
27314     baseScale : 1,
27315     rotate : 0,
27316     dragable : false,
27317     pinching : false,
27318     mouseX : 0,
27319     mouseY : 0,
27320     cropData : false,
27321     minWidth : 300,
27322     minHeight : 300,
27323     file : false,
27324     exif : {},
27325     baseRotate : 1,
27326     cropType : 'image/jpeg',
27327     buttons : false,
27328     canvasLoaded : false,
27329     isDocument : false,
27330     method : 'POST',
27331     paramName : 'imageUpload',
27332     loadMask : true,
27333     loadingText : 'Loading...',
27334     maskEl : false,
27335     
27336     getAutoCreate : function()
27337     {
27338         var cfg = {
27339             tag : 'div',
27340             cls : 'roo-upload-cropbox',
27341             cn : [
27342                 {
27343                     tag : 'input',
27344                     cls : 'roo-upload-cropbox-selector',
27345                     type : 'file'
27346                 },
27347                 {
27348                     tag : 'div',
27349                     cls : 'roo-upload-cropbox-body',
27350                     style : 'cursor:pointer',
27351                     cn : [
27352                         {
27353                             tag : 'div',
27354                             cls : 'roo-upload-cropbox-preview'
27355                         },
27356                         {
27357                             tag : 'div',
27358                             cls : 'roo-upload-cropbox-thumb'
27359                         },
27360                         {
27361                             tag : 'div',
27362                             cls : 'roo-upload-cropbox-empty-notify',
27363                             html : this.emptyText
27364                         },
27365                         {
27366                             tag : 'div',
27367                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27368                             html : this.rotateNotify
27369                         }
27370                     ]
27371                 },
27372                 {
27373                     tag : 'div',
27374                     cls : 'roo-upload-cropbox-footer',
27375                     cn : {
27376                         tag : 'div',
27377                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27378                         cn : []
27379                     }
27380                 }
27381             ]
27382         };
27383         
27384         return cfg;
27385     },
27386     
27387     onRender : function(ct, position)
27388     {
27389         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27390         
27391         if (this.buttons.length) {
27392             
27393             Roo.each(this.buttons, function(bb) {
27394                 
27395                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27396                 
27397                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27398                 
27399             }, this);
27400         }
27401         
27402         if(this.loadMask){
27403             this.maskEl = this.el;
27404         }
27405     },
27406     
27407     initEvents : function()
27408     {
27409         this.urlAPI = (window.createObjectURL && window) || 
27410                                 (window.URL && URL.revokeObjectURL && URL) || 
27411                                 (window.webkitURL && webkitURL);
27412                         
27413         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27414         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27415         
27416         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27417         this.selectorEl.hide();
27418         
27419         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27420         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27421         
27422         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27423         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27424         this.thumbEl.hide();
27425         
27426         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27427         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27428         
27429         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27430         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27431         this.errorEl.hide();
27432         
27433         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27434         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27435         this.footerEl.hide();
27436         
27437         this.setThumbBoxSize();
27438         
27439         this.bind();
27440         
27441         this.resize();
27442         
27443         this.fireEvent('initial', this);
27444     },
27445
27446     bind : function()
27447     {
27448         var _this = this;
27449         
27450         window.addEventListener("resize", function() { _this.resize(); } );
27451         
27452         this.bodyEl.on('click', this.beforeSelectFile, this);
27453         
27454         if(Roo.isTouch){
27455             this.bodyEl.on('touchstart', this.onTouchStart, this);
27456             this.bodyEl.on('touchmove', this.onTouchMove, this);
27457             this.bodyEl.on('touchend', this.onTouchEnd, this);
27458         }
27459         
27460         if(!Roo.isTouch){
27461             this.bodyEl.on('mousedown', this.onMouseDown, this);
27462             this.bodyEl.on('mousemove', this.onMouseMove, this);
27463             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27464             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27465             Roo.get(document).on('mouseup', this.onMouseUp, this);
27466         }
27467         
27468         this.selectorEl.on('change', this.onFileSelected, this);
27469     },
27470     
27471     reset : function()
27472     {    
27473         this.scale = 0;
27474         this.baseScale = 1;
27475         this.rotate = 0;
27476         this.baseRotate = 1;
27477         this.dragable = false;
27478         this.pinching = false;
27479         this.mouseX = 0;
27480         this.mouseY = 0;
27481         this.cropData = false;
27482         this.notifyEl.dom.innerHTML = this.emptyText;
27483         
27484         this.selectorEl.dom.value = '';
27485         
27486     },
27487     
27488     resize : function()
27489     {
27490         if(this.fireEvent('resize', this) != false){
27491             this.setThumbBoxPosition();
27492             this.setCanvasPosition();
27493         }
27494     },
27495     
27496     onFooterButtonClick : function(e, el, o, type)
27497     {
27498         switch (type) {
27499             case 'rotate-left' :
27500                 this.onRotateLeft(e);
27501                 break;
27502             case 'rotate-right' :
27503                 this.onRotateRight(e);
27504                 break;
27505             case 'picture' :
27506                 this.beforeSelectFile(e);
27507                 break;
27508             case 'trash' :
27509                 this.trash(e);
27510                 break;
27511             case 'crop' :
27512                 this.crop(e);
27513                 break;
27514             case 'download' :
27515                 this.download(e);
27516                 break;
27517             default :
27518                 break;
27519         }
27520         
27521         this.fireEvent('footerbuttonclick', this, type);
27522     },
27523     
27524     beforeSelectFile : function(e)
27525     {
27526         e.preventDefault();
27527         
27528         if(this.fireEvent('beforeselectfile', this) != false){
27529             this.selectorEl.dom.click();
27530         }
27531     },
27532     
27533     onFileSelected : function(e)
27534     {
27535         e.preventDefault();
27536         
27537         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27538             return;
27539         }
27540         
27541         var file = this.selectorEl.dom.files[0];
27542         
27543         if(this.fireEvent('inspect', this, file) != false){
27544             this.prepare(file);
27545         }
27546         
27547     },
27548     
27549     trash : function(e)
27550     {
27551         this.fireEvent('trash', this);
27552     },
27553     
27554     download : function(e)
27555     {
27556         this.fireEvent('download', this);
27557     },
27558     
27559     loadCanvas : function(src)
27560     {   
27561         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27562             
27563             this.reset();
27564             
27565             this.imageEl = document.createElement('img');
27566             
27567             var _this = this;
27568             
27569             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27570             
27571             this.imageEl.src = src;
27572         }
27573     },
27574     
27575     onLoadCanvas : function()
27576     {   
27577         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27578         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27579         
27580         this.bodyEl.un('click', this.beforeSelectFile, this);
27581         
27582         this.notifyEl.hide();
27583         this.thumbEl.show();
27584         this.footerEl.show();
27585         
27586         this.baseRotateLevel();
27587         
27588         if(this.isDocument){
27589             this.setThumbBoxSize();
27590         }
27591         
27592         this.setThumbBoxPosition();
27593         
27594         this.baseScaleLevel();
27595         
27596         this.draw();
27597         
27598         this.resize();
27599         
27600         this.canvasLoaded = true;
27601         
27602         if(this.loadMask){
27603             this.maskEl.unmask();
27604         }
27605         
27606     },
27607     
27608     setCanvasPosition : function()
27609     {   
27610         if(!this.canvasEl){
27611             return;
27612         }
27613         
27614         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27615         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27616         
27617         this.previewEl.setLeft(pw);
27618         this.previewEl.setTop(ph);
27619         
27620     },
27621     
27622     onMouseDown : function(e)
27623     {   
27624         e.stopEvent();
27625         
27626         this.dragable = true;
27627         this.pinching = false;
27628         
27629         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27630             this.dragable = false;
27631             return;
27632         }
27633         
27634         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27635         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27636         
27637     },
27638     
27639     onMouseMove : function(e)
27640     {   
27641         e.stopEvent();
27642         
27643         if(!this.canvasLoaded){
27644             return;
27645         }
27646         
27647         if (!this.dragable){
27648             return;
27649         }
27650         
27651         var minX = Math.ceil(this.thumbEl.getLeft(true));
27652         var minY = Math.ceil(this.thumbEl.getTop(true));
27653         
27654         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27655         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27656         
27657         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27658         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27659         
27660         x = x - this.mouseX;
27661         y = y - this.mouseY;
27662         
27663         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27664         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27665         
27666         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27667         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27668         
27669         this.previewEl.setLeft(bgX);
27670         this.previewEl.setTop(bgY);
27671         
27672         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27673         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27674     },
27675     
27676     onMouseUp : function(e)
27677     {   
27678         e.stopEvent();
27679         
27680         this.dragable = false;
27681     },
27682     
27683     onMouseWheel : function(e)
27684     {   
27685         e.stopEvent();
27686         
27687         this.startScale = this.scale;
27688         
27689         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27690         
27691         if(!this.zoomable()){
27692             this.scale = this.startScale;
27693             return;
27694         }
27695         
27696         this.draw();
27697         
27698         return;
27699     },
27700     
27701     zoomable : function()
27702     {
27703         var minScale = this.thumbEl.getWidth() / this.minWidth;
27704         
27705         if(this.minWidth < this.minHeight){
27706             minScale = this.thumbEl.getHeight() / this.minHeight;
27707         }
27708         
27709         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27710         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27711         
27712         if(
27713                 this.isDocument &&
27714                 (this.rotate == 0 || this.rotate == 180) && 
27715                 (
27716                     width > this.imageEl.OriginWidth || 
27717                     height > this.imageEl.OriginHeight ||
27718                     (width < this.minWidth && height < this.minHeight)
27719                 )
27720         ){
27721             return false;
27722         }
27723         
27724         if(
27725                 this.isDocument &&
27726                 (this.rotate == 90 || this.rotate == 270) && 
27727                 (
27728                     width > this.imageEl.OriginWidth || 
27729                     height > this.imageEl.OriginHeight ||
27730                     (width < this.minHeight && height < this.minWidth)
27731                 )
27732         ){
27733             return false;
27734         }
27735         
27736         if(
27737                 !this.isDocument &&
27738                 (this.rotate == 0 || this.rotate == 180) && 
27739                 (
27740                     width < this.minWidth || 
27741                     width > this.imageEl.OriginWidth || 
27742                     height < this.minHeight || 
27743                     height > this.imageEl.OriginHeight
27744                 )
27745         ){
27746             return false;
27747         }
27748         
27749         if(
27750                 !this.isDocument &&
27751                 (this.rotate == 90 || this.rotate == 270) && 
27752                 (
27753                     width < this.minHeight || 
27754                     width > this.imageEl.OriginWidth || 
27755                     height < this.minWidth || 
27756                     height > this.imageEl.OriginHeight
27757                 )
27758         ){
27759             return false;
27760         }
27761         
27762         return true;
27763         
27764     },
27765     
27766     onRotateLeft : function(e)
27767     {   
27768         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27769             
27770             var minScale = this.thumbEl.getWidth() / this.minWidth;
27771             
27772             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27773             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27774             
27775             this.startScale = this.scale;
27776             
27777             while (this.getScaleLevel() < minScale){
27778             
27779                 this.scale = this.scale + 1;
27780                 
27781                 if(!this.zoomable()){
27782                     break;
27783                 }
27784                 
27785                 if(
27786                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27787                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27788                 ){
27789                     continue;
27790                 }
27791                 
27792                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27793
27794                 this.draw();
27795                 
27796                 return;
27797             }
27798             
27799             this.scale = this.startScale;
27800             
27801             this.onRotateFail();
27802             
27803             return false;
27804         }
27805         
27806         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27807
27808         if(this.isDocument){
27809             this.setThumbBoxSize();
27810             this.setThumbBoxPosition();
27811             this.setCanvasPosition();
27812         }
27813         
27814         this.draw();
27815         
27816         this.fireEvent('rotate', this, 'left');
27817         
27818     },
27819     
27820     onRotateRight : function(e)
27821     {
27822         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27823             
27824             var minScale = this.thumbEl.getWidth() / this.minWidth;
27825         
27826             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27827             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27828             
27829             this.startScale = this.scale;
27830             
27831             while (this.getScaleLevel() < minScale){
27832             
27833                 this.scale = this.scale + 1;
27834                 
27835                 if(!this.zoomable()){
27836                     break;
27837                 }
27838                 
27839                 if(
27840                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27841                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27842                 ){
27843                     continue;
27844                 }
27845                 
27846                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27847
27848                 this.draw();
27849                 
27850                 return;
27851             }
27852             
27853             this.scale = this.startScale;
27854             
27855             this.onRotateFail();
27856             
27857             return false;
27858         }
27859         
27860         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27861
27862         if(this.isDocument){
27863             this.setThumbBoxSize();
27864             this.setThumbBoxPosition();
27865             this.setCanvasPosition();
27866         }
27867         
27868         this.draw();
27869         
27870         this.fireEvent('rotate', this, 'right');
27871     },
27872     
27873     onRotateFail : function()
27874     {
27875         this.errorEl.show(true);
27876         
27877         var _this = this;
27878         
27879         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27880     },
27881     
27882     draw : function()
27883     {
27884         this.previewEl.dom.innerHTML = '';
27885         
27886         var canvasEl = document.createElement("canvas");
27887         
27888         var contextEl = canvasEl.getContext("2d");
27889         
27890         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27891         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27892         var center = this.imageEl.OriginWidth / 2;
27893         
27894         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27895             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27896             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27897             center = this.imageEl.OriginHeight / 2;
27898         }
27899         
27900         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27901         
27902         contextEl.translate(center, center);
27903         contextEl.rotate(this.rotate * Math.PI / 180);
27904
27905         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27906         
27907         this.canvasEl = document.createElement("canvas");
27908         
27909         this.contextEl = this.canvasEl.getContext("2d");
27910         
27911         switch (this.rotate) {
27912             case 0 :
27913                 
27914                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27915                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27916                 
27917                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27918                 
27919                 break;
27920             case 90 : 
27921                 
27922                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27923                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27924                 
27925                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27926                     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);
27927                     break;
27928                 }
27929                 
27930                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27931                 
27932                 break;
27933             case 180 :
27934                 
27935                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27936                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27937                 
27938                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27939                     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);
27940                     break;
27941                 }
27942                 
27943                 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);
27944                 
27945                 break;
27946             case 270 :
27947                 
27948                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27949                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27950         
27951                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27952                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27953                     break;
27954                 }
27955                 
27956                 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);
27957                 
27958                 break;
27959             default : 
27960                 break;
27961         }
27962         
27963         this.previewEl.appendChild(this.canvasEl);
27964         
27965         this.setCanvasPosition();
27966     },
27967     
27968     crop : function()
27969     {
27970         if(!this.canvasLoaded){
27971             return;
27972         }
27973         
27974         var imageCanvas = document.createElement("canvas");
27975         
27976         var imageContext = imageCanvas.getContext("2d");
27977         
27978         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27979         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27980         
27981         var center = imageCanvas.width / 2;
27982         
27983         imageContext.translate(center, center);
27984         
27985         imageContext.rotate(this.rotate * Math.PI / 180);
27986         
27987         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27988         
27989         var canvas = document.createElement("canvas");
27990         
27991         var context = canvas.getContext("2d");
27992                 
27993         canvas.width = this.minWidth;
27994         canvas.height = this.minHeight;
27995
27996         switch (this.rotate) {
27997             case 0 :
27998                 
27999                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28000                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28001                 
28002                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28003                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28004                 
28005                 var targetWidth = this.minWidth - 2 * x;
28006                 var targetHeight = this.minHeight - 2 * y;
28007                 
28008                 var scale = 1;
28009                 
28010                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28011                     scale = targetWidth / width;
28012                 }
28013                 
28014                 if(x > 0 && y == 0){
28015                     scale = targetHeight / height;
28016                 }
28017                 
28018                 if(x > 0 && y > 0){
28019                     scale = targetWidth / width;
28020                     
28021                     if(width < height){
28022                         scale = targetHeight / height;
28023                     }
28024                 }
28025                 
28026                 context.scale(scale, scale);
28027                 
28028                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28029                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28030
28031                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28032                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28033
28034                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28035                 
28036                 break;
28037             case 90 : 
28038                 
28039                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28040                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28041                 
28042                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28043                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28044                 
28045                 var targetWidth = this.minWidth - 2 * x;
28046                 var targetHeight = this.minHeight - 2 * y;
28047                 
28048                 var scale = 1;
28049                 
28050                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28051                     scale = targetWidth / width;
28052                 }
28053                 
28054                 if(x > 0 && y == 0){
28055                     scale = targetHeight / height;
28056                 }
28057                 
28058                 if(x > 0 && y > 0){
28059                     scale = targetWidth / width;
28060                     
28061                     if(width < height){
28062                         scale = targetHeight / height;
28063                     }
28064                 }
28065                 
28066                 context.scale(scale, scale);
28067                 
28068                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28069                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28070
28071                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28072                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28073                 
28074                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28075                 
28076                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28077                 
28078                 break;
28079             case 180 :
28080                 
28081                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28082                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28083                 
28084                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28085                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28086                 
28087                 var targetWidth = this.minWidth - 2 * x;
28088                 var targetHeight = this.minHeight - 2 * y;
28089                 
28090                 var scale = 1;
28091                 
28092                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28093                     scale = targetWidth / width;
28094                 }
28095                 
28096                 if(x > 0 && y == 0){
28097                     scale = targetHeight / height;
28098                 }
28099                 
28100                 if(x > 0 && y > 0){
28101                     scale = targetWidth / width;
28102                     
28103                     if(width < height){
28104                         scale = targetHeight / height;
28105                     }
28106                 }
28107                 
28108                 context.scale(scale, scale);
28109                 
28110                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28111                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28112
28113                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28114                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28115
28116                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28117                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28118                 
28119                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28120                 
28121                 break;
28122             case 270 :
28123                 
28124                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28125                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28126                 
28127                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28128                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28129                 
28130                 var targetWidth = this.minWidth - 2 * x;
28131                 var targetHeight = this.minHeight - 2 * y;
28132                 
28133                 var scale = 1;
28134                 
28135                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28136                     scale = targetWidth / width;
28137                 }
28138                 
28139                 if(x > 0 && y == 0){
28140                     scale = targetHeight / height;
28141                 }
28142                 
28143                 if(x > 0 && y > 0){
28144                     scale = targetWidth / width;
28145                     
28146                     if(width < height){
28147                         scale = targetHeight / height;
28148                     }
28149                 }
28150                 
28151                 context.scale(scale, scale);
28152                 
28153                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28154                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28155
28156                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28157                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28158                 
28159                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28160                 
28161                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28162                 
28163                 break;
28164             default : 
28165                 break;
28166         }
28167         
28168         this.cropData = canvas.toDataURL(this.cropType);
28169         
28170         if(this.fireEvent('crop', this, this.cropData) !== false){
28171             this.process(this.file, this.cropData);
28172         }
28173         
28174         return;
28175         
28176     },
28177     
28178     setThumbBoxSize : function()
28179     {
28180         var width, height;
28181         
28182         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28183             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28184             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28185             
28186             this.minWidth = width;
28187             this.minHeight = height;
28188             
28189             if(this.rotate == 90 || this.rotate == 270){
28190                 this.minWidth = height;
28191                 this.minHeight = width;
28192             }
28193         }
28194         
28195         height = 300;
28196         width = Math.ceil(this.minWidth * height / this.minHeight);
28197         
28198         if(this.minWidth > this.minHeight){
28199             width = 300;
28200             height = Math.ceil(this.minHeight * width / this.minWidth);
28201         }
28202         
28203         this.thumbEl.setStyle({
28204             width : width + 'px',
28205             height : height + 'px'
28206         });
28207
28208         return;
28209             
28210     },
28211     
28212     setThumbBoxPosition : function()
28213     {
28214         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28215         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28216         
28217         this.thumbEl.setLeft(x);
28218         this.thumbEl.setTop(y);
28219         
28220     },
28221     
28222     baseRotateLevel : function()
28223     {
28224         this.baseRotate = 1;
28225         
28226         if(
28227                 typeof(this.exif) != 'undefined' &&
28228                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28229                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28230         ){
28231             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28232         }
28233         
28234         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28235         
28236     },
28237     
28238     baseScaleLevel : function()
28239     {
28240         var width, height;
28241         
28242         if(this.isDocument){
28243             
28244             if(this.baseRotate == 6 || this.baseRotate == 8){
28245             
28246                 height = this.thumbEl.getHeight();
28247                 this.baseScale = height / this.imageEl.OriginWidth;
28248
28249                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28250                     width = this.thumbEl.getWidth();
28251                     this.baseScale = width / this.imageEl.OriginHeight;
28252                 }
28253
28254                 return;
28255             }
28256
28257             height = this.thumbEl.getHeight();
28258             this.baseScale = height / this.imageEl.OriginHeight;
28259
28260             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28261                 width = this.thumbEl.getWidth();
28262                 this.baseScale = width / this.imageEl.OriginWidth;
28263             }
28264
28265             return;
28266         }
28267         
28268         if(this.baseRotate == 6 || this.baseRotate == 8){
28269             
28270             width = this.thumbEl.getHeight();
28271             this.baseScale = width / this.imageEl.OriginHeight;
28272             
28273             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28274                 height = this.thumbEl.getWidth();
28275                 this.baseScale = height / this.imageEl.OriginHeight;
28276             }
28277             
28278             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28279                 height = this.thumbEl.getWidth();
28280                 this.baseScale = height / this.imageEl.OriginHeight;
28281                 
28282                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28283                     width = this.thumbEl.getHeight();
28284                     this.baseScale = width / this.imageEl.OriginWidth;
28285                 }
28286             }
28287             
28288             return;
28289         }
28290         
28291         width = this.thumbEl.getWidth();
28292         this.baseScale = width / this.imageEl.OriginWidth;
28293         
28294         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28295             height = this.thumbEl.getHeight();
28296             this.baseScale = height / this.imageEl.OriginHeight;
28297         }
28298         
28299         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28300             
28301             height = this.thumbEl.getHeight();
28302             this.baseScale = height / this.imageEl.OriginHeight;
28303             
28304             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28305                 width = this.thumbEl.getWidth();
28306                 this.baseScale = width / this.imageEl.OriginWidth;
28307             }
28308             
28309         }
28310         
28311         return;
28312     },
28313     
28314     getScaleLevel : function()
28315     {
28316         return this.baseScale * Math.pow(1.1, this.scale);
28317     },
28318     
28319     onTouchStart : function(e)
28320     {
28321         if(!this.canvasLoaded){
28322             this.beforeSelectFile(e);
28323             return;
28324         }
28325         
28326         var touches = e.browserEvent.touches;
28327         
28328         if(!touches){
28329             return;
28330         }
28331         
28332         if(touches.length == 1){
28333             this.onMouseDown(e);
28334             return;
28335         }
28336         
28337         if(touches.length != 2){
28338             return;
28339         }
28340         
28341         var coords = [];
28342         
28343         for(var i = 0, finger; finger = touches[i]; i++){
28344             coords.push(finger.pageX, finger.pageY);
28345         }
28346         
28347         var x = Math.pow(coords[0] - coords[2], 2);
28348         var y = Math.pow(coords[1] - coords[3], 2);
28349         
28350         this.startDistance = Math.sqrt(x + y);
28351         
28352         this.startScale = this.scale;
28353         
28354         this.pinching = true;
28355         this.dragable = false;
28356         
28357     },
28358     
28359     onTouchMove : function(e)
28360     {
28361         if(!this.pinching && !this.dragable){
28362             return;
28363         }
28364         
28365         var touches = e.browserEvent.touches;
28366         
28367         if(!touches){
28368             return;
28369         }
28370         
28371         if(this.dragable){
28372             this.onMouseMove(e);
28373             return;
28374         }
28375         
28376         var coords = [];
28377         
28378         for(var i = 0, finger; finger = touches[i]; i++){
28379             coords.push(finger.pageX, finger.pageY);
28380         }
28381         
28382         var x = Math.pow(coords[0] - coords[2], 2);
28383         var y = Math.pow(coords[1] - coords[3], 2);
28384         
28385         this.endDistance = Math.sqrt(x + y);
28386         
28387         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28388         
28389         if(!this.zoomable()){
28390             this.scale = this.startScale;
28391             return;
28392         }
28393         
28394         this.draw();
28395         
28396     },
28397     
28398     onTouchEnd : function(e)
28399     {
28400         this.pinching = false;
28401         this.dragable = false;
28402         
28403     },
28404     
28405     process : function(file, crop)
28406     {
28407         if(this.loadMask){
28408             this.maskEl.mask(this.loadingText);
28409         }
28410         
28411         this.xhr = new XMLHttpRequest();
28412         
28413         file.xhr = this.xhr;
28414
28415         this.xhr.open(this.method, this.url, true);
28416         
28417         var headers = {
28418             "Accept": "application/json",
28419             "Cache-Control": "no-cache",
28420             "X-Requested-With": "XMLHttpRequest"
28421         };
28422         
28423         for (var headerName in headers) {
28424             var headerValue = headers[headerName];
28425             if (headerValue) {
28426                 this.xhr.setRequestHeader(headerName, headerValue);
28427             }
28428         }
28429         
28430         var _this = this;
28431         
28432         this.xhr.onload = function()
28433         {
28434             _this.xhrOnLoad(_this.xhr);
28435         }
28436         
28437         this.xhr.onerror = function()
28438         {
28439             _this.xhrOnError(_this.xhr);
28440         }
28441         
28442         var formData = new FormData();
28443
28444         formData.append('returnHTML', 'NO');
28445         
28446         if(crop){
28447             formData.append('crop', crop);
28448         }
28449         
28450         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28451             formData.append(this.paramName, file, file.name);
28452         }
28453         
28454         if(typeof(file.filename) != 'undefined'){
28455             formData.append('filename', file.filename);
28456         }
28457         
28458         if(typeof(file.mimetype) != 'undefined'){
28459             formData.append('mimetype', file.mimetype);
28460         }
28461         
28462         if(this.fireEvent('arrange', this, formData) != false){
28463             this.xhr.send(formData);
28464         };
28465     },
28466     
28467     xhrOnLoad : function(xhr)
28468     {
28469         if(this.loadMask){
28470             this.maskEl.unmask();
28471         }
28472         
28473         if (xhr.readyState !== 4) {
28474             this.fireEvent('exception', this, xhr);
28475             return;
28476         }
28477
28478         var response = Roo.decode(xhr.responseText);
28479         
28480         if(!response.success){
28481             this.fireEvent('exception', this, xhr);
28482             return;
28483         }
28484         
28485         var response = Roo.decode(xhr.responseText);
28486         
28487         this.fireEvent('upload', this, response);
28488         
28489     },
28490     
28491     xhrOnError : function()
28492     {
28493         if(this.loadMask){
28494             this.maskEl.unmask();
28495         }
28496         
28497         Roo.log('xhr on error');
28498         
28499         var response = Roo.decode(xhr.responseText);
28500           
28501         Roo.log(response);
28502         
28503     },
28504     
28505     prepare : function(file)
28506     {   
28507         if(this.loadMask){
28508             this.maskEl.mask(this.loadingText);
28509         }
28510         
28511         this.file = false;
28512         this.exif = {};
28513         
28514         if(typeof(file) === 'string'){
28515             this.loadCanvas(file);
28516             return;
28517         }
28518         
28519         if(!file || !this.urlAPI){
28520             return;
28521         }
28522         
28523         this.file = file;
28524         this.cropType = file.type;
28525         
28526         var _this = this;
28527         
28528         if(this.fireEvent('prepare', this, this.file) != false){
28529             
28530             var reader = new FileReader();
28531             
28532             reader.onload = function (e) {
28533                 if (e.target.error) {
28534                     Roo.log(e.target.error);
28535                     return;
28536                 }
28537                 
28538                 var buffer = e.target.result,
28539                     dataView = new DataView(buffer),
28540                     offset = 2,
28541                     maxOffset = dataView.byteLength - 4,
28542                     markerBytes,
28543                     markerLength;
28544                 
28545                 if (dataView.getUint16(0) === 0xffd8) {
28546                     while (offset < maxOffset) {
28547                         markerBytes = dataView.getUint16(offset);
28548                         
28549                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28550                             markerLength = dataView.getUint16(offset + 2) + 2;
28551                             if (offset + markerLength > dataView.byteLength) {
28552                                 Roo.log('Invalid meta data: Invalid segment size.');
28553                                 break;
28554                             }
28555                             
28556                             if(markerBytes == 0xffe1){
28557                                 _this.parseExifData(
28558                                     dataView,
28559                                     offset,
28560                                     markerLength
28561                                 );
28562                             }
28563                             
28564                             offset += markerLength;
28565                             
28566                             continue;
28567                         }
28568                         
28569                         break;
28570                     }
28571                     
28572                 }
28573                 
28574                 var url = _this.urlAPI.createObjectURL(_this.file);
28575                 
28576                 _this.loadCanvas(url);
28577                 
28578                 return;
28579             }
28580             
28581             reader.readAsArrayBuffer(this.file);
28582             
28583         }
28584         
28585     },
28586     
28587     parseExifData : function(dataView, offset, length)
28588     {
28589         var tiffOffset = offset + 10,
28590             littleEndian,
28591             dirOffset;
28592     
28593         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28594             // No Exif data, might be XMP data instead
28595             return;
28596         }
28597         
28598         // Check for the ASCII code for "Exif" (0x45786966):
28599         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28600             // No Exif data, might be XMP data instead
28601             return;
28602         }
28603         if (tiffOffset + 8 > dataView.byteLength) {
28604             Roo.log('Invalid Exif data: Invalid segment size.');
28605             return;
28606         }
28607         // Check for the two null bytes:
28608         if (dataView.getUint16(offset + 8) !== 0x0000) {
28609             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28610             return;
28611         }
28612         // Check the byte alignment:
28613         switch (dataView.getUint16(tiffOffset)) {
28614         case 0x4949:
28615             littleEndian = true;
28616             break;
28617         case 0x4D4D:
28618             littleEndian = false;
28619             break;
28620         default:
28621             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28622             return;
28623         }
28624         // Check for the TIFF tag marker (0x002A):
28625         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28626             Roo.log('Invalid Exif data: Missing TIFF marker.');
28627             return;
28628         }
28629         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28630         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28631         
28632         this.parseExifTags(
28633             dataView,
28634             tiffOffset,
28635             tiffOffset + dirOffset,
28636             littleEndian
28637         );
28638     },
28639     
28640     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28641     {
28642         var tagsNumber,
28643             dirEndOffset,
28644             i;
28645         if (dirOffset + 6 > dataView.byteLength) {
28646             Roo.log('Invalid Exif data: Invalid directory offset.');
28647             return;
28648         }
28649         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28650         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28651         if (dirEndOffset + 4 > dataView.byteLength) {
28652             Roo.log('Invalid Exif data: Invalid directory size.');
28653             return;
28654         }
28655         for (i = 0; i < tagsNumber; i += 1) {
28656             this.parseExifTag(
28657                 dataView,
28658                 tiffOffset,
28659                 dirOffset + 2 + 12 * i, // tag offset
28660                 littleEndian
28661             );
28662         }
28663         // Return the offset to the next directory:
28664         return dataView.getUint32(dirEndOffset, littleEndian);
28665     },
28666     
28667     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28668     {
28669         var tag = dataView.getUint16(offset, littleEndian);
28670         
28671         this.exif[tag] = this.getExifValue(
28672             dataView,
28673             tiffOffset,
28674             offset,
28675             dataView.getUint16(offset + 2, littleEndian), // tag type
28676             dataView.getUint32(offset + 4, littleEndian), // tag length
28677             littleEndian
28678         );
28679     },
28680     
28681     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28682     {
28683         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28684             tagSize,
28685             dataOffset,
28686             values,
28687             i,
28688             str,
28689             c;
28690     
28691         if (!tagType) {
28692             Roo.log('Invalid Exif data: Invalid tag type.');
28693             return;
28694         }
28695         
28696         tagSize = tagType.size * length;
28697         // Determine if the value is contained in the dataOffset bytes,
28698         // or if the value at the dataOffset is a pointer to the actual data:
28699         dataOffset = tagSize > 4 ?
28700                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28701         if (dataOffset + tagSize > dataView.byteLength) {
28702             Roo.log('Invalid Exif data: Invalid data offset.');
28703             return;
28704         }
28705         if (length === 1) {
28706             return tagType.getValue(dataView, dataOffset, littleEndian);
28707         }
28708         values = [];
28709         for (i = 0; i < length; i += 1) {
28710             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28711         }
28712         
28713         if (tagType.ascii) {
28714             str = '';
28715             // Concatenate the chars:
28716             for (i = 0; i < values.length; i += 1) {
28717                 c = values[i];
28718                 // Ignore the terminating NULL byte(s):
28719                 if (c === '\u0000') {
28720                     break;
28721                 }
28722                 str += c;
28723             }
28724             return str;
28725         }
28726         return values;
28727     }
28728     
28729 });
28730
28731 Roo.apply(Roo.bootstrap.UploadCropbox, {
28732     tags : {
28733         'Orientation': 0x0112
28734     },
28735     
28736     Orientation: {
28737             1: 0, //'top-left',
28738 //            2: 'top-right',
28739             3: 180, //'bottom-right',
28740 //            4: 'bottom-left',
28741 //            5: 'left-top',
28742             6: 90, //'right-top',
28743 //            7: 'right-bottom',
28744             8: 270 //'left-bottom'
28745     },
28746     
28747     exifTagTypes : {
28748         // byte, 8-bit unsigned int:
28749         1: {
28750             getValue: function (dataView, dataOffset) {
28751                 return dataView.getUint8(dataOffset);
28752             },
28753             size: 1
28754         },
28755         // ascii, 8-bit byte:
28756         2: {
28757             getValue: function (dataView, dataOffset) {
28758                 return String.fromCharCode(dataView.getUint8(dataOffset));
28759             },
28760             size: 1,
28761             ascii: true
28762         },
28763         // short, 16 bit int:
28764         3: {
28765             getValue: function (dataView, dataOffset, littleEndian) {
28766                 return dataView.getUint16(dataOffset, littleEndian);
28767             },
28768             size: 2
28769         },
28770         // long, 32 bit int:
28771         4: {
28772             getValue: function (dataView, dataOffset, littleEndian) {
28773                 return dataView.getUint32(dataOffset, littleEndian);
28774             },
28775             size: 4
28776         },
28777         // rational = two long values, first is numerator, second is denominator:
28778         5: {
28779             getValue: function (dataView, dataOffset, littleEndian) {
28780                 return dataView.getUint32(dataOffset, littleEndian) /
28781                     dataView.getUint32(dataOffset + 4, littleEndian);
28782             },
28783             size: 8
28784         },
28785         // slong, 32 bit signed int:
28786         9: {
28787             getValue: function (dataView, dataOffset, littleEndian) {
28788                 return dataView.getInt32(dataOffset, littleEndian);
28789             },
28790             size: 4
28791         },
28792         // srational, two slongs, first is numerator, second is denominator:
28793         10: {
28794             getValue: function (dataView, dataOffset, littleEndian) {
28795                 return dataView.getInt32(dataOffset, littleEndian) /
28796                     dataView.getInt32(dataOffset + 4, littleEndian);
28797             },
28798             size: 8
28799         }
28800     },
28801     
28802     footer : {
28803         STANDARD : [
28804             {
28805                 tag : 'div',
28806                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28807                 action : 'rotate-left',
28808                 cn : [
28809                     {
28810                         tag : 'button',
28811                         cls : 'btn btn-default',
28812                         html : '<i class="fa fa-undo"></i>'
28813                     }
28814                 ]
28815             },
28816             {
28817                 tag : 'div',
28818                 cls : 'btn-group roo-upload-cropbox-picture',
28819                 action : 'picture',
28820                 cn : [
28821                     {
28822                         tag : 'button',
28823                         cls : 'btn btn-default',
28824                         html : '<i class="fa fa-picture-o"></i>'
28825                     }
28826                 ]
28827             },
28828             {
28829                 tag : 'div',
28830                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28831                 action : 'rotate-right',
28832                 cn : [
28833                     {
28834                         tag : 'button',
28835                         cls : 'btn btn-default',
28836                         html : '<i class="fa fa-repeat"></i>'
28837                     }
28838                 ]
28839             }
28840         ],
28841         DOCUMENT : [
28842             {
28843                 tag : 'div',
28844                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28845                 action : 'rotate-left',
28846                 cn : [
28847                     {
28848                         tag : 'button',
28849                         cls : 'btn btn-default',
28850                         html : '<i class="fa fa-undo"></i>'
28851                     }
28852                 ]
28853             },
28854             {
28855                 tag : 'div',
28856                 cls : 'btn-group roo-upload-cropbox-download',
28857                 action : 'download',
28858                 cn : [
28859                     {
28860                         tag : 'button',
28861                         cls : 'btn btn-default',
28862                         html : '<i class="fa fa-download"></i>'
28863                     }
28864                 ]
28865             },
28866             {
28867                 tag : 'div',
28868                 cls : 'btn-group roo-upload-cropbox-crop',
28869                 action : 'crop',
28870                 cn : [
28871                     {
28872                         tag : 'button',
28873                         cls : 'btn btn-default',
28874                         html : '<i class="fa fa-crop"></i>'
28875                     }
28876                 ]
28877             },
28878             {
28879                 tag : 'div',
28880                 cls : 'btn-group roo-upload-cropbox-trash',
28881                 action : 'trash',
28882                 cn : [
28883                     {
28884                         tag : 'button',
28885                         cls : 'btn btn-default',
28886                         html : '<i class="fa fa-trash"></i>'
28887                     }
28888                 ]
28889             },
28890             {
28891                 tag : 'div',
28892                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28893                 action : 'rotate-right',
28894                 cn : [
28895                     {
28896                         tag : 'button',
28897                         cls : 'btn btn-default',
28898                         html : '<i class="fa fa-repeat"></i>'
28899                     }
28900                 ]
28901             }
28902         ],
28903         ROTATOR : [
28904             {
28905                 tag : 'div',
28906                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28907                 action : 'rotate-left',
28908                 cn : [
28909                     {
28910                         tag : 'button',
28911                         cls : 'btn btn-default',
28912                         html : '<i class="fa fa-undo"></i>'
28913                     }
28914                 ]
28915             },
28916             {
28917                 tag : 'div',
28918                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28919                 action : 'rotate-right',
28920                 cn : [
28921                     {
28922                         tag : 'button',
28923                         cls : 'btn btn-default',
28924                         html : '<i class="fa fa-repeat"></i>'
28925                     }
28926                 ]
28927             }
28928         ]
28929     }
28930 });
28931
28932 /*
28933 * Licence: LGPL
28934 */
28935
28936 /**
28937  * @class Roo.bootstrap.DocumentManager
28938  * @extends Roo.bootstrap.Component
28939  * Bootstrap DocumentManager class
28940  * @cfg {String} paramName default 'imageUpload'
28941  * @cfg {String} toolTipName default 'filename'
28942  * @cfg {String} method default POST
28943  * @cfg {String} url action url
28944  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28945  * @cfg {Boolean} multiple multiple upload default true
28946  * @cfg {Number} thumbSize default 300
28947  * @cfg {String} fieldLabel
28948  * @cfg {Number} labelWidth default 4
28949  * @cfg {String} labelAlign (left|top) default left
28950  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28951 * @cfg {Number} labellg set the width of label (1-12)
28952  * @cfg {Number} labelmd set the width of label (1-12)
28953  * @cfg {Number} labelsm set the width of label (1-12)
28954  * @cfg {Number} labelxs set the width of label (1-12)
28955  * 
28956  * @constructor
28957  * Create a new DocumentManager
28958  * @param {Object} config The config object
28959  */
28960
28961 Roo.bootstrap.DocumentManager = function(config){
28962     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28963     
28964     this.files = [];
28965     this.delegates = [];
28966     
28967     this.addEvents({
28968         /**
28969          * @event initial
28970          * Fire when initial the DocumentManager
28971          * @param {Roo.bootstrap.DocumentManager} this
28972          */
28973         "initial" : true,
28974         /**
28975          * @event inspect
28976          * inspect selected file
28977          * @param {Roo.bootstrap.DocumentManager} this
28978          * @param {File} file
28979          */
28980         "inspect" : true,
28981         /**
28982          * @event exception
28983          * Fire when xhr load exception
28984          * @param {Roo.bootstrap.DocumentManager} this
28985          * @param {XMLHttpRequest} xhr
28986          */
28987         "exception" : true,
28988         /**
28989          * @event afterupload
28990          * Fire when xhr load exception
28991          * @param {Roo.bootstrap.DocumentManager} this
28992          * @param {XMLHttpRequest} xhr
28993          */
28994         "afterupload" : true,
28995         /**
28996          * @event prepare
28997          * prepare the form data
28998          * @param {Roo.bootstrap.DocumentManager} this
28999          * @param {Object} formData
29000          */
29001         "prepare" : true,
29002         /**
29003          * @event remove
29004          * Fire when remove the file
29005          * @param {Roo.bootstrap.DocumentManager} this
29006          * @param {Object} file
29007          */
29008         "remove" : true,
29009         /**
29010          * @event refresh
29011          * Fire after refresh the file
29012          * @param {Roo.bootstrap.DocumentManager} this
29013          */
29014         "refresh" : true,
29015         /**
29016          * @event click
29017          * Fire after click the image
29018          * @param {Roo.bootstrap.DocumentManager} this
29019          * @param {Object} file
29020          */
29021         "click" : true,
29022         /**
29023          * @event edit
29024          * Fire when upload a image and editable set to true
29025          * @param {Roo.bootstrap.DocumentManager} this
29026          * @param {Object} file
29027          */
29028         "edit" : true,
29029         /**
29030          * @event beforeselectfile
29031          * Fire before select file
29032          * @param {Roo.bootstrap.DocumentManager} this
29033          */
29034         "beforeselectfile" : true,
29035         /**
29036          * @event process
29037          * Fire before process file
29038          * @param {Roo.bootstrap.DocumentManager} this
29039          * @param {Object} file
29040          */
29041         "process" : true,
29042         /**
29043          * @event previewrendered
29044          * Fire when preview rendered
29045          * @param {Roo.bootstrap.DocumentManager} this
29046          * @param {Object} file
29047          */
29048         "previewrendered" : true,
29049         /**
29050          */
29051         "previewResize" : true
29052         
29053     });
29054 };
29055
29056 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29057     
29058     boxes : 0,
29059     inputName : '',
29060     thumbSize : 300,
29061     multiple : true,
29062     files : false,
29063     method : 'POST',
29064     url : '',
29065     paramName : 'imageUpload',
29066     toolTipName : 'filename',
29067     fieldLabel : '',
29068     labelWidth : 4,
29069     labelAlign : 'left',
29070     editable : true,
29071     delegates : false,
29072     xhr : false, 
29073     
29074     labellg : 0,
29075     labelmd : 0,
29076     labelsm : 0,
29077     labelxs : 0,
29078     
29079     getAutoCreate : function()
29080     {   
29081         var managerWidget = {
29082             tag : 'div',
29083             cls : 'roo-document-manager',
29084             cn : [
29085                 {
29086                     tag : 'input',
29087                     cls : 'roo-document-manager-selector',
29088                     type : 'file'
29089                 },
29090                 {
29091                     tag : 'div',
29092                     cls : 'roo-document-manager-uploader',
29093                     cn : [
29094                         {
29095                             tag : 'div',
29096                             cls : 'roo-document-manager-upload-btn',
29097                             html : '<i class="fa fa-plus"></i>'
29098                         }
29099                     ]
29100                     
29101                 }
29102             ]
29103         };
29104         
29105         var content = [
29106             {
29107                 tag : 'div',
29108                 cls : 'column col-md-12',
29109                 cn : managerWidget
29110             }
29111         ];
29112         
29113         if(this.fieldLabel.length){
29114             
29115             content = [
29116                 {
29117                     tag : 'div',
29118                     cls : 'column col-md-12',
29119                     html : this.fieldLabel
29120                 },
29121                 {
29122                     tag : 'div',
29123                     cls : 'column col-md-12',
29124                     cn : managerWidget
29125                 }
29126             ];
29127
29128             if(this.labelAlign == 'left'){
29129                 content = [
29130                     {
29131                         tag : 'div',
29132                         cls : 'column',
29133                         html : this.fieldLabel
29134                     },
29135                     {
29136                         tag : 'div',
29137                         cls : 'column',
29138                         cn : managerWidget
29139                     }
29140                 ];
29141                 
29142                 if(this.labelWidth > 12){
29143                     content[0].style = "width: " + this.labelWidth + 'px';
29144                 }
29145
29146                 if(this.labelWidth < 13 && this.labelmd == 0){
29147                     this.labelmd = this.labelWidth;
29148                 }
29149
29150                 if(this.labellg > 0){
29151                     content[0].cls += ' col-lg-' + this.labellg;
29152                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29153                 }
29154
29155                 if(this.labelmd > 0){
29156                     content[0].cls += ' col-md-' + this.labelmd;
29157                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29158                 }
29159
29160                 if(this.labelsm > 0){
29161                     content[0].cls += ' col-sm-' + this.labelsm;
29162                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29163                 }
29164
29165                 if(this.labelxs > 0){
29166                     content[0].cls += ' col-xs-' + this.labelxs;
29167                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29168                 }
29169                 
29170             }
29171         }
29172         
29173         var cfg = {
29174             tag : 'div',
29175             cls : 'row clearfix',
29176             cn : content
29177         };
29178         
29179         return cfg;
29180         
29181     },
29182     
29183     initEvents : function()
29184     {
29185         this.managerEl = this.el.select('.roo-document-manager', true).first();
29186         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29187         
29188         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29189         this.selectorEl.hide();
29190         
29191         if(this.multiple){
29192             this.selectorEl.attr('multiple', 'multiple');
29193         }
29194         
29195         this.selectorEl.on('change', this.onFileSelected, this);
29196         
29197         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29198         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29199         
29200         this.uploader.on('click', this.onUploaderClick, this);
29201         
29202         this.renderProgressDialog();
29203         
29204         var _this = this;
29205         
29206         window.addEventListener("resize", function() { _this.refresh(); } );
29207         
29208         this.fireEvent('initial', this);
29209     },
29210     
29211     renderProgressDialog : function()
29212     {
29213         var _this = this;
29214         
29215         this.progressDialog = new Roo.bootstrap.Modal({
29216             cls : 'roo-document-manager-progress-dialog',
29217             allow_close : false,
29218             animate : false,
29219             title : '',
29220             buttons : [
29221                 {
29222                     name  :'cancel',
29223                     weight : 'danger',
29224                     html : 'Cancel'
29225                 }
29226             ], 
29227             listeners : { 
29228                 btnclick : function() {
29229                     _this.uploadCancel();
29230                     this.hide();
29231                 }
29232             }
29233         });
29234          
29235         this.progressDialog.render(Roo.get(document.body));
29236          
29237         this.progress = new Roo.bootstrap.Progress({
29238             cls : 'roo-document-manager-progress',
29239             active : true,
29240             striped : true
29241         });
29242         
29243         this.progress.render(this.progressDialog.getChildContainer());
29244         
29245         this.progressBar = new Roo.bootstrap.ProgressBar({
29246             cls : 'roo-document-manager-progress-bar',
29247             aria_valuenow : 0,
29248             aria_valuemin : 0,
29249             aria_valuemax : 12,
29250             panel : 'success'
29251         });
29252         
29253         this.progressBar.render(this.progress.getChildContainer());
29254     },
29255     
29256     onUploaderClick : function(e)
29257     {
29258         e.preventDefault();
29259      
29260         if(this.fireEvent('beforeselectfile', this) != false){
29261             this.selectorEl.dom.click();
29262         }
29263         
29264     },
29265     
29266     onFileSelected : function(e)
29267     {
29268         e.preventDefault();
29269         
29270         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29271             return;
29272         }
29273         
29274         Roo.each(this.selectorEl.dom.files, function(file){
29275             if(this.fireEvent('inspect', this, file) != false){
29276                 this.files.push(file);
29277             }
29278         }, this);
29279         
29280         this.queue();
29281         
29282     },
29283     
29284     queue : function()
29285     {
29286         this.selectorEl.dom.value = '';
29287         
29288         if(!this.files || !this.files.length){
29289             return;
29290         }
29291         
29292         if(this.boxes > 0 && this.files.length > this.boxes){
29293             this.files = this.files.slice(0, this.boxes);
29294         }
29295         
29296         this.uploader.show();
29297         
29298         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29299             this.uploader.hide();
29300         }
29301         
29302         var _this = this;
29303         
29304         var files = [];
29305         
29306         var docs = [];
29307         
29308         Roo.each(this.files, function(file){
29309             
29310             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29311                 var f = this.renderPreview(file);
29312                 files.push(f);
29313                 return;
29314             }
29315             
29316             if(file.type.indexOf('image') != -1){
29317                 this.delegates.push(
29318                     (function(){
29319                         _this.process(file);
29320                     }).createDelegate(this)
29321                 );
29322         
29323                 return;
29324             }
29325             
29326             docs.push(
29327                 (function(){
29328                     _this.process(file);
29329                 }).createDelegate(this)
29330             );
29331             
29332         }, this);
29333         
29334         this.files = files;
29335         
29336         this.delegates = this.delegates.concat(docs);
29337         
29338         if(!this.delegates.length){
29339             this.refresh();
29340             return;
29341         }
29342         
29343         this.progressBar.aria_valuemax = this.delegates.length;
29344         
29345         this.arrange();
29346         
29347         return;
29348     },
29349     
29350     arrange : function()
29351     {
29352         if(!this.delegates.length){
29353             this.progressDialog.hide();
29354             this.refresh();
29355             return;
29356         }
29357         
29358         var delegate = this.delegates.shift();
29359         
29360         this.progressDialog.show();
29361         
29362         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29363         
29364         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29365         
29366         delegate();
29367     },
29368     
29369     refresh : function()
29370     {
29371         this.uploader.show();
29372         
29373         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29374             this.uploader.hide();
29375         }
29376         
29377         Roo.isTouch ? this.closable(false) : this.closable(true);
29378         
29379         this.fireEvent('refresh', this);
29380     },
29381     
29382     onRemove : function(e, el, o)
29383     {
29384         e.preventDefault();
29385         
29386         this.fireEvent('remove', this, o);
29387         
29388     },
29389     
29390     remove : function(o)
29391     {
29392         var files = [];
29393         
29394         Roo.each(this.files, function(file){
29395             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29396                 files.push(file);
29397                 return;
29398             }
29399
29400             o.target.remove();
29401
29402         }, this);
29403         
29404         this.files = files;
29405         
29406         this.refresh();
29407     },
29408     
29409     clear : function()
29410     {
29411         Roo.each(this.files, function(file){
29412             if(!file.target){
29413                 return;
29414             }
29415             
29416             file.target.remove();
29417
29418         }, this);
29419         
29420         this.files = [];
29421         
29422         this.refresh();
29423     },
29424     
29425     onClick : function(e, el, o)
29426     {
29427         e.preventDefault();
29428         
29429         this.fireEvent('click', this, o);
29430         
29431     },
29432     
29433     closable : function(closable)
29434     {
29435         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29436             
29437             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29438             
29439             if(closable){
29440                 el.show();
29441                 return;
29442             }
29443             
29444             el.hide();
29445             
29446         }, this);
29447     },
29448     
29449     xhrOnLoad : function(xhr)
29450     {
29451         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29452             el.remove();
29453         }, this);
29454         
29455         if (xhr.readyState !== 4) {
29456             this.arrange();
29457             this.fireEvent('exception', this, xhr);
29458             return;
29459         }
29460
29461         var response = Roo.decode(xhr.responseText);
29462         
29463         if(!response.success){
29464             this.arrange();
29465             this.fireEvent('exception', this, xhr);
29466             return;
29467         }
29468         
29469         var file = this.renderPreview(response.data);
29470         
29471         this.files.push(file);
29472         
29473         this.arrange();
29474         
29475         this.fireEvent('afterupload', this, xhr);
29476         
29477     },
29478     
29479     xhrOnError : function(xhr)
29480     {
29481         Roo.log('xhr on error');
29482         
29483         var response = Roo.decode(xhr.responseText);
29484           
29485         Roo.log(response);
29486         
29487         this.arrange();
29488     },
29489     
29490     process : function(file)
29491     {
29492         if(this.fireEvent('process', this, file) !== false){
29493             if(this.editable && file.type.indexOf('image') != -1){
29494                 this.fireEvent('edit', this, file);
29495                 return;
29496             }
29497
29498             this.uploadStart(file, false);
29499
29500             return;
29501         }
29502         
29503     },
29504     
29505     uploadStart : function(file, crop)
29506     {
29507         this.xhr = new XMLHttpRequest();
29508         
29509         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29510             this.arrange();
29511             return;
29512         }
29513         
29514         file.xhr = this.xhr;
29515             
29516         this.managerEl.createChild({
29517             tag : 'div',
29518             cls : 'roo-document-manager-loading',
29519             cn : [
29520                 {
29521                     tag : 'div',
29522                     tooltip : file.name,
29523                     cls : 'roo-document-manager-thumb',
29524                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29525                 }
29526             ]
29527
29528         });
29529
29530         this.xhr.open(this.method, this.url, true);
29531         
29532         var headers = {
29533             "Accept": "application/json",
29534             "Cache-Control": "no-cache",
29535             "X-Requested-With": "XMLHttpRequest"
29536         };
29537         
29538         for (var headerName in headers) {
29539             var headerValue = headers[headerName];
29540             if (headerValue) {
29541                 this.xhr.setRequestHeader(headerName, headerValue);
29542             }
29543         }
29544         
29545         var _this = this;
29546         
29547         this.xhr.onload = function()
29548         {
29549             _this.xhrOnLoad(_this.xhr);
29550         }
29551         
29552         this.xhr.onerror = function()
29553         {
29554             _this.xhrOnError(_this.xhr);
29555         }
29556         
29557         var formData = new FormData();
29558
29559         formData.append('returnHTML', 'NO');
29560         
29561         if(crop){
29562             formData.append('crop', crop);
29563         }
29564         
29565         formData.append(this.paramName, file, file.name);
29566         
29567         var options = {
29568             file : file, 
29569             manually : false
29570         };
29571         
29572         if(this.fireEvent('prepare', this, formData, options) != false){
29573             
29574             if(options.manually){
29575                 return;
29576             }
29577             
29578             this.xhr.send(formData);
29579             return;
29580         };
29581         
29582         this.uploadCancel();
29583     },
29584     
29585     uploadCancel : function()
29586     {
29587         if (this.xhr) {
29588             this.xhr.abort();
29589         }
29590         
29591         this.delegates = [];
29592         
29593         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29594             el.remove();
29595         }, this);
29596         
29597         this.arrange();
29598     },
29599     
29600     renderPreview : function(file)
29601     {
29602         if(typeof(file.target) != 'undefined' && file.target){
29603             return file;
29604         }
29605         
29606         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29607         
29608         var previewEl = this.managerEl.createChild({
29609             tag : 'div',
29610             cls : 'roo-document-manager-preview',
29611             cn : [
29612                 {
29613                     tag : 'div',
29614                     tooltip : file[this.toolTipName],
29615                     cls : 'roo-document-manager-thumb',
29616                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29617                 },
29618                 {
29619                     tag : 'button',
29620                     cls : 'close',
29621                     html : '<i class="fa fa-times-circle"></i>'
29622                 }
29623             ]
29624         });
29625
29626         var close = previewEl.select('button.close', true).first();
29627
29628         close.on('click', this.onRemove, this, file);
29629
29630         file.target = previewEl;
29631
29632         var image = previewEl.select('img', true).first();
29633         
29634         var _this = this;
29635         
29636         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29637         
29638         image.on('click', this.onClick, this, file);
29639         
29640         this.fireEvent('previewrendered', this, file);
29641         
29642         return file;
29643         
29644     },
29645     
29646     onPreviewLoad : function(file, image)
29647     {
29648         if(typeof(file.target) == 'undefined' || !file.target){
29649             return;
29650         }
29651         
29652         var width = image.dom.naturalWidth || image.dom.width;
29653         var height = image.dom.naturalHeight || image.dom.height;
29654         
29655         if(!this.previewResize) {
29656             return;
29657         }
29658         
29659         if(width > height){
29660             file.target.addClass('wide');
29661             return;
29662         }
29663         
29664         file.target.addClass('tall');
29665         return;
29666         
29667     },
29668     
29669     uploadFromSource : function(file, crop)
29670     {
29671         this.xhr = new XMLHttpRequest();
29672         
29673         this.managerEl.createChild({
29674             tag : 'div',
29675             cls : 'roo-document-manager-loading',
29676             cn : [
29677                 {
29678                     tag : 'div',
29679                     tooltip : file.name,
29680                     cls : 'roo-document-manager-thumb',
29681                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29682                 }
29683             ]
29684
29685         });
29686
29687         this.xhr.open(this.method, this.url, true);
29688         
29689         var headers = {
29690             "Accept": "application/json",
29691             "Cache-Control": "no-cache",
29692             "X-Requested-With": "XMLHttpRequest"
29693         };
29694         
29695         for (var headerName in headers) {
29696             var headerValue = headers[headerName];
29697             if (headerValue) {
29698                 this.xhr.setRequestHeader(headerName, headerValue);
29699             }
29700         }
29701         
29702         var _this = this;
29703         
29704         this.xhr.onload = function()
29705         {
29706             _this.xhrOnLoad(_this.xhr);
29707         }
29708         
29709         this.xhr.onerror = function()
29710         {
29711             _this.xhrOnError(_this.xhr);
29712         }
29713         
29714         var formData = new FormData();
29715
29716         formData.append('returnHTML', 'NO');
29717         
29718         formData.append('crop', crop);
29719         
29720         if(typeof(file.filename) != 'undefined'){
29721             formData.append('filename', file.filename);
29722         }
29723         
29724         if(typeof(file.mimetype) != 'undefined'){
29725             formData.append('mimetype', file.mimetype);
29726         }
29727         
29728         Roo.log(formData);
29729         
29730         if(this.fireEvent('prepare', this, formData) != false){
29731             this.xhr.send(formData);
29732         };
29733     }
29734 });
29735
29736 /*
29737 * Licence: LGPL
29738 */
29739
29740 /**
29741  * @class Roo.bootstrap.DocumentViewer
29742  * @extends Roo.bootstrap.Component
29743  * Bootstrap DocumentViewer class
29744  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29745  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29746  * 
29747  * @constructor
29748  * Create a new DocumentViewer
29749  * @param {Object} config The config object
29750  */
29751
29752 Roo.bootstrap.DocumentViewer = function(config){
29753     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29754     
29755     this.addEvents({
29756         /**
29757          * @event initial
29758          * Fire after initEvent
29759          * @param {Roo.bootstrap.DocumentViewer} this
29760          */
29761         "initial" : true,
29762         /**
29763          * @event click
29764          * Fire after click
29765          * @param {Roo.bootstrap.DocumentViewer} this
29766          */
29767         "click" : true,
29768         /**
29769          * @event download
29770          * Fire after download button
29771          * @param {Roo.bootstrap.DocumentViewer} this
29772          */
29773         "download" : true,
29774         /**
29775          * @event trash
29776          * Fire after trash button
29777          * @param {Roo.bootstrap.DocumentViewer} this
29778          */
29779         "trash" : true
29780         
29781     });
29782 };
29783
29784 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29785     
29786     showDownload : true,
29787     
29788     showTrash : true,
29789     
29790     getAutoCreate : function()
29791     {
29792         var cfg = {
29793             tag : 'div',
29794             cls : 'roo-document-viewer',
29795             cn : [
29796                 {
29797                     tag : 'div',
29798                     cls : 'roo-document-viewer-body',
29799                     cn : [
29800                         {
29801                             tag : 'div',
29802                             cls : 'roo-document-viewer-thumb',
29803                             cn : [
29804                                 {
29805                                     tag : 'img',
29806                                     cls : 'roo-document-viewer-image'
29807                                 }
29808                             ]
29809                         }
29810                     ]
29811                 },
29812                 {
29813                     tag : 'div',
29814                     cls : 'roo-document-viewer-footer',
29815                     cn : {
29816                         tag : 'div',
29817                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29818                         cn : [
29819                             {
29820                                 tag : 'div',
29821                                 cls : 'btn-group roo-document-viewer-download',
29822                                 cn : [
29823                                     {
29824                                         tag : 'button',
29825                                         cls : 'btn btn-default',
29826                                         html : '<i class="fa fa-download"></i>'
29827                                     }
29828                                 ]
29829                             },
29830                             {
29831                                 tag : 'div',
29832                                 cls : 'btn-group roo-document-viewer-trash',
29833                                 cn : [
29834                                     {
29835                                         tag : 'button',
29836                                         cls : 'btn btn-default',
29837                                         html : '<i class="fa fa-trash"></i>'
29838                                     }
29839                                 ]
29840                             }
29841                         ]
29842                     }
29843                 }
29844             ]
29845         };
29846         
29847         return cfg;
29848     },
29849     
29850     initEvents : function()
29851     {
29852         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29853         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29854         
29855         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29856         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29857         
29858         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29859         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29860         
29861         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29862         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29863         
29864         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29865         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29866         
29867         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29868         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29869         
29870         this.bodyEl.on('click', this.onClick, this);
29871         this.downloadBtn.on('click', this.onDownload, this);
29872         this.trashBtn.on('click', this.onTrash, this);
29873         
29874         this.downloadBtn.hide();
29875         this.trashBtn.hide();
29876         
29877         if(this.showDownload){
29878             this.downloadBtn.show();
29879         }
29880         
29881         if(this.showTrash){
29882             this.trashBtn.show();
29883         }
29884         
29885         if(!this.showDownload && !this.showTrash) {
29886             this.footerEl.hide();
29887         }
29888         
29889     },
29890     
29891     initial : function()
29892     {
29893         this.fireEvent('initial', this);
29894         
29895     },
29896     
29897     onClick : function(e)
29898     {
29899         e.preventDefault();
29900         
29901         this.fireEvent('click', this);
29902     },
29903     
29904     onDownload : function(e)
29905     {
29906         e.preventDefault();
29907         
29908         this.fireEvent('download', this);
29909     },
29910     
29911     onTrash : function(e)
29912     {
29913         e.preventDefault();
29914         
29915         this.fireEvent('trash', this);
29916     }
29917     
29918 });
29919 /*
29920  * - LGPL
29921  *
29922  * nav progress bar
29923  * 
29924  */
29925
29926 /**
29927  * @class Roo.bootstrap.NavProgressBar
29928  * @extends Roo.bootstrap.Component
29929  * Bootstrap NavProgressBar class
29930  * 
29931  * @constructor
29932  * Create a new nav progress bar
29933  * @param {Object} config The config object
29934  */
29935
29936 Roo.bootstrap.NavProgressBar = function(config){
29937     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29938
29939     this.bullets = this.bullets || [];
29940    
29941 //    Roo.bootstrap.NavProgressBar.register(this);
29942      this.addEvents({
29943         /**
29944              * @event changed
29945              * Fires when the active item changes
29946              * @param {Roo.bootstrap.NavProgressBar} this
29947              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29948              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29949          */
29950         'changed': true
29951      });
29952     
29953 };
29954
29955 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29956     
29957     bullets : [],
29958     barItems : [],
29959     
29960     getAutoCreate : function()
29961     {
29962         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29963         
29964         cfg = {
29965             tag : 'div',
29966             cls : 'roo-navigation-bar-group',
29967             cn : [
29968                 {
29969                     tag : 'div',
29970                     cls : 'roo-navigation-top-bar'
29971                 },
29972                 {
29973                     tag : 'div',
29974                     cls : 'roo-navigation-bullets-bar',
29975                     cn : [
29976                         {
29977                             tag : 'ul',
29978                             cls : 'roo-navigation-bar'
29979                         }
29980                     ]
29981                 },
29982                 
29983                 {
29984                     tag : 'div',
29985                     cls : 'roo-navigation-bottom-bar'
29986                 }
29987             ]
29988             
29989         };
29990         
29991         return cfg;
29992         
29993     },
29994     
29995     initEvents: function() 
29996     {
29997         
29998     },
29999     
30000     onRender : function(ct, position) 
30001     {
30002         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30003         
30004         if(this.bullets.length){
30005             Roo.each(this.bullets, function(b){
30006                this.addItem(b);
30007             }, this);
30008         }
30009         
30010         this.format();
30011         
30012     },
30013     
30014     addItem : function(cfg)
30015     {
30016         var item = new Roo.bootstrap.NavProgressItem(cfg);
30017         
30018         item.parentId = this.id;
30019         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30020         
30021         if(cfg.html){
30022             var top = new Roo.bootstrap.Element({
30023                 tag : 'div',
30024                 cls : 'roo-navigation-bar-text'
30025             });
30026             
30027             var bottom = new Roo.bootstrap.Element({
30028                 tag : 'div',
30029                 cls : 'roo-navigation-bar-text'
30030             });
30031             
30032             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30033             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30034             
30035             var topText = new Roo.bootstrap.Element({
30036                 tag : 'span',
30037                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30038             });
30039             
30040             var bottomText = new Roo.bootstrap.Element({
30041                 tag : 'span',
30042                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30043             });
30044             
30045             topText.onRender(top.el, null);
30046             bottomText.onRender(bottom.el, null);
30047             
30048             item.topEl = top;
30049             item.bottomEl = bottom;
30050         }
30051         
30052         this.barItems.push(item);
30053         
30054         return item;
30055     },
30056     
30057     getActive : function()
30058     {
30059         var active = false;
30060         
30061         Roo.each(this.barItems, function(v){
30062             
30063             if (!v.isActive()) {
30064                 return;
30065             }
30066             
30067             active = v;
30068             return false;
30069             
30070         });
30071         
30072         return active;
30073     },
30074     
30075     setActiveItem : function(item)
30076     {
30077         var prev = false;
30078         
30079         Roo.each(this.barItems, function(v){
30080             if (v.rid == item.rid) {
30081                 return ;
30082             }
30083             
30084             if (v.isActive()) {
30085                 v.setActive(false);
30086                 prev = v;
30087             }
30088         });
30089
30090         item.setActive(true);
30091         
30092         this.fireEvent('changed', this, item, prev);
30093     },
30094     
30095     getBarItem: function(rid)
30096     {
30097         var ret = false;
30098         
30099         Roo.each(this.barItems, function(e) {
30100             if (e.rid != rid) {
30101                 return;
30102             }
30103             
30104             ret =  e;
30105             return false;
30106         });
30107         
30108         return ret;
30109     },
30110     
30111     indexOfItem : function(item)
30112     {
30113         var index = false;
30114         
30115         Roo.each(this.barItems, function(v, i){
30116             
30117             if (v.rid != item.rid) {
30118                 return;
30119             }
30120             
30121             index = i;
30122             return false
30123         });
30124         
30125         return index;
30126     },
30127     
30128     setActiveNext : function()
30129     {
30130         var i = this.indexOfItem(this.getActive());
30131         
30132         if (i > this.barItems.length) {
30133             return;
30134         }
30135         
30136         this.setActiveItem(this.barItems[i+1]);
30137     },
30138     
30139     setActivePrev : function()
30140     {
30141         var i = this.indexOfItem(this.getActive());
30142         
30143         if (i  < 1) {
30144             return;
30145         }
30146         
30147         this.setActiveItem(this.barItems[i-1]);
30148     },
30149     
30150     format : function()
30151     {
30152         if(!this.barItems.length){
30153             return;
30154         }
30155      
30156         var width = 100 / this.barItems.length;
30157         
30158         Roo.each(this.barItems, function(i){
30159             i.el.setStyle('width', width + '%');
30160             i.topEl.el.setStyle('width', width + '%');
30161             i.bottomEl.el.setStyle('width', width + '%');
30162         }, this);
30163         
30164     }
30165     
30166 });
30167 /*
30168  * - LGPL
30169  *
30170  * Nav Progress Item
30171  * 
30172  */
30173
30174 /**
30175  * @class Roo.bootstrap.NavProgressItem
30176  * @extends Roo.bootstrap.Component
30177  * Bootstrap NavProgressItem class
30178  * @cfg {String} rid the reference id
30179  * @cfg {Boolean} active (true|false) Is item active default false
30180  * @cfg {Boolean} disabled (true|false) Is item active default false
30181  * @cfg {String} html
30182  * @cfg {String} position (top|bottom) text position default bottom
30183  * @cfg {String} icon show icon instead of number
30184  * 
30185  * @constructor
30186  * Create a new NavProgressItem
30187  * @param {Object} config The config object
30188  */
30189 Roo.bootstrap.NavProgressItem = function(config){
30190     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30191     this.addEvents({
30192         // raw events
30193         /**
30194          * @event click
30195          * The raw click event for the entire grid.
30196          * @param {Roo.bootstrap.NavProgressItem} this
30197          * @param {Roo.EventObject} e
30198          */
30199         "click" : true
30200     });
30201    
30202 };
30203
30204 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30205     
30206     rid : '',
30207     active : false,
30208     disabled : false,
30209     html : '',
30210     position : 'bottom',
30211     icon : false,
30212     
30213     getAutoCreate : function()
30214     {
30215         var iconCls = 'roo-navigation-bar-item-icon';
30216         
30217         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30218         
30219         var cfg = {
30220             tag: 'li',
30221             cls: 'roo-navigation-bar-item',
30222             cn : [
30223                 {
30224                     tag : 'i',
30225                     cls : iconCls
30226                 }
30227             ]
30228         };
30229         
30230         if(this.active){
30231             cfg.cls += ' active';
30232         }
30233         if(this.disabled){
30234             cfg.cls += ' disabled';
30235         }
30236         
30237         return cfg;
30238     },
30239     
30240     disable : function()
30241     {
30242         this.setDisabled(true);
30243     },
30244     
30245     enable : function()
30246     {
30247         this.setDisabled(false);
30248     },
30249     
30250     initEvents: function() 
30251     {
30252         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30253         
30254         this.iconEl.on('click', this.onClick, this);
30255     },
30256     
30257     onClick : function(e)
30258     {
30259         e.preventDefault();
30260         
30261         if(this.disabled){
30262             return;
30263         }
30264         
30265         if(this.fireEvent('click', this, e) === false){
30266             return;
30267         };
30268         
30269         this.parent().setActiveItem(this);
30270     },
30271     
30272     isActive: function () 
30273     {
30274         return this.active;
30275     },
30276     
30277     setActive : function(state)
30278     {
30279         if(this.active == state){
30280             return;
30281         }
30282         
30283         this.active = state;
30284         
30285         if (state) {
30286             this.el.addClass('active');
30287             return;
30288         }
30289         
30290         this.el.removeClass('active');
30291         
30292         return;
30293     },
30294     
30295     setDisabled : function(state)
30296     {
30297         if(this.disabled == state){
30298             return;
30299         }
30300         
30301         this.disabled = state;
30302         
30303         if (state) {
30304             this.el.addClass('disabled');
30305             return;
30306         }
30307         
30308         this.el.removeClass('disabled');
30309     },
30310     
30311     tooltipEl : function()
30312     {
30313         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30314     }
30315 });
30316  
30317
30318  /*
30319  * - LGPL
30320  *
30321  * FieldLabel
30322  * 
30323  */
30324
30325 /**
30326  * @class Roo.bootstrap.FieldLabel
30327  * @extends Roo.bootstrap.Component
30328  * Bootstrap FieldLabel class
30329  * @cfg {String} html contents of the element
30330  * @cfg {String} tag tag of the element default label
30331  * @cfg {String} cls class of the element
30332  * @cfg {String} target label target 
30333  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30334  * @cfg {String} invalidClass default "text-warning"
30335  * @cfg {String} validClass default "text-success"
30336  * @cfg {String} iconTooltip default "This field is required"
30337  * @cfg {String} indicatorpos (left|right) default left
30338  * 
30339  * @constructor
30340  * Create a new FieldLabel
30341  * @param {Object} config The config object
30342  */
30343
30344 Roo.bootstrap.FieldLabel = function(config){
30345     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30346     
30347     this.addEvents({
30348             /**
30349              * @event invalid
30350              * Fires after the field has been marked as invalid.
30351              * @param {Roo.form.FieldLabel} this
30352              * @param {String} msg The validation message
30353              */
30354             invalid : true,
30355             /**
30356              * @event valid
30357              * Fires after the field has been validated with no errors.
30358              * @param {Roo.form.FieldLabel} this
30359              */
30360             valid : true
30361         });
30362 };
30363
30364 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30365     
30366     tag: 'label',
30367     cls: '',
30368     html: '',
30369     target: '',
30370     allowBlank : true,
30371     invalidClass : 'has-warning',
30372     validClass : 'has-success',
30373     iconTooltip : 'This field is required',
30374     indicatorpos : 'left',
30375     
30376     getAutoCreate : function(){
30377         
30378         var cls = "";
30379         if (!this.allowBlank) {
30380             cls  = "visible";
30381         }
30382         
30383         var cfg = {
30384             tag : this.tag,
30385             cls : 'roo-bootstrap-field-label ' + this.cls,
30386             for : this.target,
30387             cn : [
30388                 {
30389                     tag : 'i',
30390                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30391                     tooltip : this.iconTooltip
30392                 },
30393                 {
30394                     tag : 'span',
30395                     html : this.html
30396                 }
30397             ] 
30398         };
30399         
30400         if(this.indicatorpos == 'right'){
30401             var cfg = {
30402                 tag : this.tag,
30403                 cls : 'roo-bootstrap-field-label ' + this.cls,
30404                 for : this.target,
30405                 cn : [
30406                     {
30407                         tag : 'span',
30408                         html : this.html
30409                     },
30410                     {
30411                         tag : 'i',
30412                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30413                         tooltip : this.iconTooltip
30414                     }
30415                 ] 
30416             };
30417         }
30418         
30419         return cfg;
30420     },
30421     
30422     initEvents: function() 
30423     {
30424         Roo.bootstrap.Element.superclass.initEvents.call(this);
30425         
30426         this.indicator = this.indicatorEl();
30427         
30428         if(this.indicator){
30429             this.indicator.removeClass('visible');
30430             this.indicator.addClass('invisible');
30431         }
30432         
30433         Roo.bootstrap.FieldLabel.register(this);
30434     },
30435     
30436     indicatorEl : function()
30437     {
30438         var indicator = this.el.select('i.roo-required-indicator',true).first();
30439         
30440         if(!indicator){
30441             return false;
30442         }
30443         
30444         return indicator;
30445         
30446     },
30447     
30448     /**
30449      * Mark this field as valid
30450      */
30451     markValid : function()
30452     {
30453         if(this.indicator){
30454             this.indicator.removeClass('visible');
30455             this.indicator.addClass('invisible');
30456         }
30457         
30458         this.el.removeClass(this.invalidClass);
30459         
30460         this.el.addClass(this.validClass);
30461         
30462         this.fireEvent('valid', this);
30463     },
30464     
30465     /**
30466      * Mark this field as invalid
30467      * @param {String} msg The validation message
30468      */
30469     markInvalid : function(msg)
30470     {
30471         if(this.indicator){
30472             this.indicator.removeClass('invisible');
30473             this.indicator.addClass('visible');
30474         }
30475         
30476         this.el.removeClass(this.validClass);
30477         
30478         this.el.addClass(this.invalidClass);
30479         
30480         this.fireEvent('invalid', this, msg);
30481     }
30482     
30483    
30484 });
30485
30486 Roo.apply(Roo.bootstrap.FieldLabel, {
30487     
30488     groups: {},
30489     
30490      /**
30491     * register a FieldLabel Group
30492     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30493     */
30494     register : function(label)
30495     {
30496         if(this.groups.hasOwnProperty(label.target)){
30497             return;
30498         }
30499      
30500         this.groups[label.target] = label;
30501         
30502     },
30503     /**
30504     * fetch a FieldLabel Group based on the target
30505     * @param {string} target
30506     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30507     */
30508     get: function(target) {
30509         if (typeof(this.groups[target]) == 'undefined') {
30510             return false;
30511         }
30512         
30513         return this.groups[target] ;
30514     }
30515 });
30516
30517  
30518
30519  /*
30520  * - LGPL
30521  *
30522  * page DateSplitField.
30523  * 
30524  */
30525
30526
30527 /**
30528  * @class Roo.bootstrap.DateSplitField
30529  * @extends Roo.bootstrap.Component
30530  * Bootstrap DateSplitField class
30531  * @cfg {string} fieldLabel - the label associated
30532  * @cfg {Number} labelWidth set the width of label (0-12)
30533  * @cfg {String} labelAlign (top|left)
30534  * @cfg {Boolean} dayAllowBlank (true|false) default false
30535  * @cfg {Boolean} monthAllowBlank (true|false) default false
30536  * @cfg {Boolean} yearAllowBlank (true|false) default false
30537  * @cfg {string} dayPlaceholder 
30538  * @cfg {string} monthPlaceholder
30539  * @cfg {string} yearPlaceholder
30540  * @cfg {string} dayFormat default 'd'
30541  * @cfg {string} monthFormat default 'm'
30542  * @cfg {string} yearFormat default 'Y'
30543  * @cfg {Number} labellg set the width of label (1-12)
30544  * @cfg {Number} labelmd set the width of label (1-12)
30545  * @cfg {Number} labelsm set the width of label (1-12)
30546  * @cfg {Number} labelxs set the width of label (1-12)
30547
30548  *     
30549  * @constructor
30550  * Create a new DateSplitField
30551  * @param {Object} config The config object
30552  */
30553
30554 Roo.bootstrap.DateSplitField = function(config){
30555     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30556     
30557     this.addEvents({
30558         // raw events
30559          /**
30560          * @event years
30561          * getting the data of years
30562          * @param {Roo.bootstrap.DateSplitField} this
30563          * @param {Object} years
30564          */
30565         "years" : true,
30566         /**
30567          * @event days
30568          * getting the data of days
30569          * @param {Roo.bootstrap.DateSplitField} this
30570          * @param {Object} days
30571          */
30572         "days" : true,
30573         /**
30574          * @event invalid
30575          * Fires after the field has been marked as invalid.
30576          * @param {Roo.form.Field} this
30577          * @param {String} msg The validation message
30578          */
30579         invalid : true,
30580        /**
30581          * @event valid
30582          * Fires after the field has been validated with no errors.
30583          * @param {Roo.form.Field} this
30584          */
30585         valid : true
30586     });
30587 };
30588
30589 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30590     
30591     fieldLabel : '',
30592     labelAlign : 'top',
30593     labelWidth : 3,
30594     dayAllowBlank : false,
30595     monthAllowBlank : false,
30596     yearAllowBlank : false,
30597     dayPlaceholder : '',
30598     monthPlaceholder : '',
30599     yearPlaceholder : '',
30600     dayFormat : 'd',
30601     monthFormat : 'm',
30602     yearFormat : 'Y',
30603     isFormField : true,
30604     labellg : 0,
30605     labelmd : 0,
30606     labelsm : 0,
30607     labelxs : 0,
30608     
30609     getAutoCreate : function()
30610     {
30611         var cfg = {
30612             tag : 'div',
30613             cls : 'row roo-date-split-field-group',
30614             cn : [
30615                 {
30616                     tag : 'input',
30617                     type : 'hidden',
30618                     cls : 'form-hidden-field roo-date-split-field-group-value',
30619                     name : this.name
30620                 }
30621             ]
30622         };
30623         
30624         var labelCls = 'col-md-12';
30625         var contentCls = 'col-md-4';
30626         
30627         if(this.fieldLabel){
30628             
30629             var label = {
30630                 tag : 'div',
30631                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30632                 cn : [
30633                     {
30634                         tag : 'label',
30635                         html : this.fieldLabel
30636                     }
30637                 ]
30638             };
30639             
30640             if(this.labelAlign == 'left'){
30641             
30642                 if(this.labelWidth > 12){
30643                     label.style = "width: " + this.labelWidth + 'px';
30644                 }
30645
30646                 if(this.labelWidth < 13 && this.labelmd == 0){
30647                     this.labelmd = this.labelWidth;
30648                 }
30649
30650                 if(this.labellg > 0){
30651                     labelCls = ' col-lg-' + this.labellg;
30652                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30653                 }
30654
30655                 if(this.labelmd > 0){
30656                     labelCls = ' col-md-' + this.labelmd;
30657                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30658                 }
30659
30660                 if(this.labelsm > 0){
30661                     labelCls = ' col-sm-' + this.labelsm;
30662                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30663                 }
30664
30665                 if(this.labelxs > 0){
30666                     labelCls = ' col-xs-' + this.labelxs;
30667                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30668                 }
30669             }
30670             
30671             label.cls += ' ' + labelCls;
30672             
30673             cfg.cn.push(label);
30674         }
30675         
30676         Roo.each(['day', 'month', 'year'], function(t){
30677             cfg.cn.push({
30678                 tag : 'div',
30679                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30680             });
30681         }, this);
30682         
30683         return cfg;
30684     },
30685     
30686     inputEl: function ()
30687     {
30688         return this.el.select('.roo-date-split-field-group-value', true).first();
30689     },
30690     
30691     onRender : function(ct, position) 
30692     {
30693         var _this = this;
30694         
30695         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30696         
30697         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30698         
30699         this.dayField = new Roo.bootstrap.ComboBox({
30700             allowBlank : this.dayAllowBlank,
30701             alwaysQuery : true,
30702             displayField : 'value',
30703             editable : false,
30704             fieldLabel : '',
30705             forceSelection : true,
30706             mode : 'local',
30707             placeholder : this.dayPlaceholder,
30708             selectOnFocus : true,
30709             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30710             triggerAction : 'all',
30711             typeAhead : true,
30712             valueField : 'value',
30713             store : new Roo.data.SimpleStore({
30714                 data : (function() {    
30715                     var days = [];
30716                     _this.fireEvent('days', _this, days);
30717                     return days;
30718                 })(),
30719                 fields : [ 'value' ]
30720             }),
30721             listeners : {
30722                 select : function (_self, record, index)
30723                 {
30724                     _this.setValue(_this.getValue());
30725                 }
30726             }
30727         });
30728
30729         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30730         
30731         this.monthField = new Roo.bootstrap.MonthField({
30732             after : '<i class=\"fa fa-calendar\"></i>',
30733             allowBlank : this.monthAllowBlank,
30734             placeholder : this.monthPlaceholder,
30735             readOnly : true,
30736             listeners : {
30737                 render : function (_self)
30738                 {
30739                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30740                         e.preventDefault();
30741                         _self.focus();
30742                     });
30743                 },
30744                 select : function (_self, oldvalue, newvalue)
30745                 {
30746                     _this.setValue(_this.getValue());
30747                 }
30748             }
30749         });
30750         
30751         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30752         
30753         this.yearField = new Roo.bootstrap.ComboBox({
30754             allowBlank : this.yearAllowBlank,
30755             alwaysQuery : true,
30756             displayField : 'value',
30757             editable : false,
30758             fieldLabel : '',
30759             forceSelection : true,
30760             mode : 'local',
30761             placeholder : this.yearPlaceholder,
30762             selectOnFocus : true,
30763             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30764             triggerAction : 'all',
30765             typeAhead : true,
30766             valueField : 'value',
30767             store : new Roo.data.SimpleStore({
30768                 data : (function() {
30769                     var years = [];
30770                     _this.fireEvent('years', _this, years);
30771                     return years;
30772                 })(),
30773                 fields : [ 'value' ]
30774             }),
30775             listeners : {
30776                 select : function (_self, record, index)
30777                 {
30778                     _this.setValue(_this.getValue());
30779                 }
30780             }
30781         });
30782
30783         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30784     },
30785     
30786     setValue : function(v, format)
30787     {
30788         this.inputEl.dom.value = v;
30789         
30790         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30791         
30792         var d = Date.parseDate(v, f);
30793         
30794         if(!d){
30795             this.validate();
30796             return;
30797         }
30798         
30799         this.setDay(d.format(this.dayFormat));
30800         this.setMonth(d.format(this.monthFormat));
30801         this.setYear(d.format(this.yearFormat));
30802         
30803         this.validate();
30804         
30805         return;
30806     },
30807     
30808     setDay : function(v)
30809     {
30810         this.dayField.setValue(v);
30811         this.inputEl.dom.value = this.getValue();
30812         this.validate();
30813         return;
30814     },
30815     
30816     setMonth : function(v)
30817     {
30818         this.monthField.setValue(v, true);
30819         this.inputEl.dom.value = this.getValue();
30820         this.validate();
30821         return;
30822     },
30823     
30824     setYear : function(v)
30825     {
30826         this.yearField.setValue(v);
30827         this.inputEl.dom.value = this.getValue();
30828         this.validate();
30829         return;
30830     },
30831     
30832     getDay : function()
30833     {
30834         return this.dayField.getValue();
30835     },
30836     
30837     getMonth : function()
30838     {
30839         return this.monthField.getValue();
30840     },
30841     
30842     getYear : function()
30843     {
30844         return this.yearField.getValue();
30845     },
30846     
30847     getValue : function()
30848     {
30849         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30850         
30851         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30852         
30853         return date;
30854     },
30855     
30856     reset : function()
30857     {
30858         this.setDay('');
30859         this.setMonth('');
30860         this.setYear('');
30861         this.inputEl.dom.value = '';
30862         this.validate();
30863         return;
30864     },
30865     
30866     validate : function()
30867     {
30868         var d = this.dayField.validate();
30869         var m = this.monthField.validate();
30870         var y = this.yearField.validate();
30871         
30872         var valid = true;
30873         
30874         if(
30875                 (!this.dayAllowBlank && !d) ||
30876                 (!this.monthAllowBlank && !m) ||
30877                 (!this.yearAllowBlank && !y)
30878         ){
30879             valid = false;
30880         }
30881         
30882         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30883             return valid;
30884         }
30885         
30886         if(valid){
30887             this.markValid();
30888             return valid;
30889         }
30890         
30891         this.markInvalid();
30892         
30893         return valid;
30894     },
30895     
30896     markValid : function()
30897     {
30898         
30899         var label = this.el.select('label', true).first();
30900         var icon = this.el.select('i.fa-star', true).first();
30901
30902         if(label && icon){
30903             icon.remove();
30904         }
30905         
30906         this.fireEvent('valid', this);
30907     },
30908     
30909      /**
30910      * Mark this field as invalid
30911      * @param {String} msg The validation message
30912      */
30913     markInvalid : function(msg)
30914     {
30915         
30916         var label = this.el.select('label', true).first();
30917         var icon = this.el.select('i.fa-star', true).first();
30918
30919         if(label && !icon){
30920             this.el.select('.roo-date-split-field-label', true).createChild({
30921                 tag : 'i',
30922                 cls : 'text-danger fa fa-lg fa-star',
30923                 tooltip : 'This field is required',
30924                 style : 'margin-right:5px;'
30925             }, label, true);
30926         }
30927         
30928         this.fireEvent('invalid', this, msg);
30929     },
30930     
30931     clearInvalid : function()
30932     {
30933         var label = this.el.select('label', true).first();
30934         var icon = this.el.select('i.fa-star', true).first();
30935
30936         if(label && icon){
30937             icon.remove();
30938         }
30939         
30940         this.fireEvent('valid', this);
30941     },
30942     
30943     getName: function()
30944     {
30945         return this.name;
30946     }
30947     
30948 });
30949
30950  /**
30951  *
30952  * This is based on 
30953  * http://masonry.desandro.com
30954  *
30955  * The idea is to render all the bricks based on vertical width...
30956  *
30957  * The original code extends 'outlayer' - we might need to use that....
30958  * 
30959  */
30960
30961
30962 /**
30963  * @class Roo.bootstrap.LayoutMasonry
30964  * @extends Roo.bootstrap.Component
30965  * Bootstrap Layout Masonry class
30966  * 
30967  * @constructor
30968  * Create a new Element
30969  * @param {Object} config The config object
30970  */
30971
30972 Roo.bootstrap.LayoutMasonry = function(config){
30973     
30974     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30975     
30976     this.bricks = [];
30977     
30978     Roo.bootstrap.LayoutMasonry.register(this);
30979     
30980     this.addEvents({
30981         // raw events
30982         /**
30983          * @event layout
30984          * Fire after layout the items
30985          * @param {Roo.bootstrap.LayoutMasonry} this
30986          * @param {Roo.EventObject} e
30987          */
30988         "layout" : true
30989     });
30990     
30991 };
30992
30993 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30994     
30995     /**
30996      * @cfg {Boolean} isLayoutInstant = no animation?
30997      */   
30998     isLayoutInstant : false, // needed?
30999    
31000     /**
31001      * @cfg {Number} boxWidth  width of the columns
31002      */   
31003     boxWidth : 450,
31004     
31005       /**
31006      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31007      */   
31008     boxHeight : 0,
31009     
31010     /**
31011      * @cfg {Number} padWidth padding below box..
31012      */   
31013     padWidth : 10, 
31014     
31015     /**
31016      * @cfg {Number} gutter gutter width..
31017      */   
31018     gutter : 10,
31019     
31020      /**
31021      * @cfg {Number} maxCols maximum number of columns
31022      */   
31023     
31024     maxCols: 0,
31025     
31026     /**
31027      * @cfg {Boolean} isAutoInitial defalut true
31028      */   
31029     isAutoInitial : true, 
31030     
31031     containerWidth: 0,
31032     
31033     /**
31034      * @cfg {Boolean} isHorizontal defalut false
31035      */   
31036     isHorizontal : false, 
31037
31038     currentSize : null,
31039     
31040     tag: 'div',
31041     
31042     cls: '',
31043     
31044     bricks: null, //CompositeElement
31045     
31046     cols : 1,
31047     
31048     _isLayoutInited : false,
31049     
31050 //    isAlternative : false, // only use for vertical layout...
31051     
31052     /**
31053      * @cfg {Number} alternativePadWidth padding below box..
31054      */   
31055     alternativePadWidth : 50,
31056     
31057     selectedBrick : [],
31058     
31059     getAutoCreate : function(){
31060         
31061         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31062         
31063         var cfg = {
31064             tag: this.tag,
31065             cls: 'blog-masonary-wrapper ' + this.cls,
31066             cn : {
31067                 cls : 'mas-boxes masonary'
31068             }
31069         };
31070         
31071         return cfg;
31072     },
31073     
31074     getChildContainer: function( )
31075     {
31076         if (this.boxesEl) {
31077             return this.boxesEl;
31078         }
31079         
31080         this.boxesEl = this.el.select('.mas-boxes').first();
31081         
31082         return this.boxesEl;
31083     },
31084     
31085     
31086     initEvents : function()
31087     {
31088         var _this = this;
31089         
31090         if(this.isAutoInitial){
31091             Roo.log('hook children rendered');
31092             this.on('childrenrendered', function() {
31093                 Roo.log('children rendered');
31094                 _this.initial();
31095             } ,this);
31096         }
31097     },
31098     
31099     initial : function()
31100     {
31101         this.selectedBrick = [];
31102         
31103         this.currentSize = this.el.getBox(true);
31104         
31105         Roo.EventManager.onWindowResize(this.resize, this); 
31106
31107         if(!this.isAutoInitial){
31108             this.layout();
31109             return;
31110         }
31111         
31112         this.layout();
31113         
31114         return;
31115         //this.layout.defer(500,this);
31116         
31117     },
31118     
31119     resize : function()
31120     {
31121         var cs = this.el.getBox(true);
31122         
31123         if (
31124                 this.currentSize.width == cs.width && 
31125                 this.currentSize.x == cs.x && 
31126                 this.currentSize.height == cs.height && 
31127                 this.currentSize.y == cs.y 
31128         ) {
31129             Roo.log("no change in with or X or Y");
31130             return;
31131         }
31132         
31133         this.currentSize = cs;
31134         
31135         this.layout();
31136         
31137     },
31138     
31139     layout : function()
31140     {   
31141         this._resetLayout();
31142         
31143         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31144         
31145         this.layoutItems( isInstant );
31146       
31147         this._isLayoutInited = true;
31148         
31149         this.fireEvent('layout', this);
31150         
31151     },
31152     
31153     _resetLayout : function()
31154     {
31155         if(this.isHorizontal){
31156             this.horizontalMeasureColumns();
31157             return;
31158         }
31159         
31160         this.verticalMeasureColumns();
31161         
31162     },
31163     
31164     verticalMeasureColumns : function()
31165     {
31166         this.getContainerWidth();
31167         
31168 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31169 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31170 //            return;
31171 //        }
31172         
31173         var boxWidth = this.boxWidth + this.padWidth;
31174         
31175         if(this.containerWidth < this.boxWidth){
31176             boxWidth = this.containerWidth
31177         }
31178         
31179         var containerWidth = this.containerWidth;
31180         
31181         var cols = Math.floor(containerWidth / boxWidth);
31182         
31183         this.cols = Math.max( cols, 1 );
31184         
31185         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31186         
31187         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31188         
31189         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31190         
31191         this.colWidth = boxWidth + avail - this.padWidth;
31192         
31193         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31194         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31195     },
31196     
31197     horizontalMeasureColumns : function()
31198     {
31199         this.getContainerWidth();
31200         
31201         var boxWidth = this.boxWidth;
31202         
31203         if(this.containerWidth < boxWidth){
31204             boxWidth = this.containerWidth;
31205         }
31206         
31207         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31208         
31209         this.el.setHeight(boxWidth);
31210         
31211     },
31212     
31213     getContainerWidth : function()
31214     {
31215         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31216     },
31217     
31218     layoutItems : function( isInstant )
31219     {
31220         Roo.log(this.bricks);
31221         
31222         var items = Roo.apply([], this.bricks);
31223         
31224         if(this.isHorizontal){
31225             this._horizontalLayoutItems( items , isInstant );
31226             return;
31227         }
31228         
31229 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31230 //            this._verticalAlternativeLayoutItems( items , isInstant );
31231 //            return;
31232 //        }
31233         
31234         this._verticalLayoutItems( items , isInstant );
31235         
31236     },
31237     
31238     _verticalLayoutItems : function ( items , isInstant)
31239     {
31240         if ( !items || !items.length ) {
31241             return;
31242         }
31243         
31244         var standard = [
31245             ['xs', 'xs', 'xs', 'tall'],
31246             ['xs', 'xs', 'tall'],
31247             ['xs', 'xs', 'sm'],
31248             ['xs', 'xs', 'xs'],
31249             ['xs', 'tall'],
31250             ['xs', 'sm'],
31251             ['xs', 'xs'],
31252             ['xs'],
31253             
31254             ['sm', 'xs', 'xs'],
31255             ['sm', 'xs'],
31256             ['sm'],
31257             
31258             ['tall', 'xs', 'xs', 'xs'],
31259             ['tall', 'xs', 'xs'],
31260             ['tall', 'xs'],
31261             ['tall']
31262             
31263         ];
31264         
31265         var queue = [];
31266         
31267         var boxes = [];
31268         
31269         var box = [];
31270         
31271         Roo.each(items, function(item, k){
31272             
31273             switch (item.size) {
31274                 // these layouts take up a full box,
31275                 case 'md' :
31276                 case 'md-left' :
31277                 case 'md-right' :
31278                 case 'wide' :
31279                     
31280                     if(box.length){
31281                         boxes.push(box);
31282                         box = [];
31283                     }
31284                     
31285                     boxes.push([item]);
31286                     
31287                     break;
31288                     
31289                 case 'xs' :
31290                 case 'sm' :
31291                 case 'tall' :
31292                     
31293                     box.push(item);
31294                     
31295                     break;
31296                 default :
31297                     break;
31298                     
31299             }
31300             
31301         }, this);
31302         
31303         if(box.length){
31304             boxes.push(box);
31305             box = [];
31306         }
31307         
31308         var filterPattern = function(box, length)
31309         {
31310             if(!box.length){
31311                 return;
31312             }
31313             
31314             var match = false;
31315             
31316             var pattern = box.slice(0, length);
31317             
31318             var format = [];
31319             
31320             Roo.each(pattern, function(i){
31321                 format.push(i.size);
31322             }, this);
31323             
31324             Roo.each(standard, function(s){
31325                 
31326                 if(String(s) != String(format)){
31327                     return;
31328                 }
31329                 
31330                 match = true;
31331                 return false;
31332                 
31333             }, this);
31334             
31335             if(!match && length == 1){
31336                 return;
31337             }
31338             
31339             if(!match){
31340                 filterPattern(box, length - 1);
31341                 return;
31342             }
31343                 
31344             queue.push(pattern);
31345
31346             box = box.slice(length, box.length);
31347
31348             filterPattern(box, 4);
31349
31350             return;
31351             
31352         }
31353         
31354         Roo.each(boxes, function(box, k){
31355             
31356             if(!box.length){
31357                 return;
31358             }
31359             
31360             if(box.length == 1){
31361                 queue.push(box);
31362                 return;
31363             }
31364             
31365             filterPattern(box, 4);
31366             
31367         }, this);
31368         
31369         this._processVerticalLayoutQueue( queue, isInstant );
31370         
31371     },
31372     
31373 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31374 //    {
31375 //        if ( !items || !items.length ) {
31376 //            return;
31377 //        }
31378 //
31379 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31380 //        
31381 //    },
31382     
31383     _horizontalLayoutItems : function ( items , isInstant)
31384     {
31385         if ( !items || !items.length || items.length < 3) {
31386             return;
31387         }
31388         
31389         items.reverse();
31390         
31391         var eItems = items.slice(0, 3);
31392         
31393         items = items.slice(3, items.length);
31394         
31395         var standard = [
31396             ['xs', 'xs', 'xs', 'wide'],
31397             ['xs', 'xs', 'wide'],
31398             ['xs', 'xs', 'sm'],
31399             ['xs', 'xs', 'xs'],
31400             ['xs', 'wide'],
31401             ['xs', 'sm'],
31402             ['xs', 'xs'],
31403             ['xs'],
31404             
31405             ['sm', 'xs', 'xs'],
31406             ['sm', 'xs'],
31407             ['sm'],
31408             
31409             ['wide', 'xs', 'xs', 'xs'],
31410             ['wide', 'xs', 'xs'],
31411             ['wide', 'xs'],
31412             ['wide'],
31413             
31414             ['wide-thin']
31415         ];
31416         
31417         var queue = [];
31418         
31419         var boxes = [];
31420         
31421         var box = [];
31422         
31423         Roo.each(items, function(item, k){
31424             
31425             switch (item.size) {
31426                 case 'md' :
31427                 case 'md-left' :
31428                 case 'md-right' :
31429                 case 'tall' :
31430                     
31431                     if(box.length){
31432                         boxes.push(box);
31433                         box = [];
31434                     }
31435                     
31436                     boxes.push([item]);
31437                     
31438                     break;
31439                     
31440                 case 'xs' :
31441                 case 'sm' :
31442                 case 'wide' :
31443                 case 'wide-thin' :
31444                     
31445                     box.push(item);
31446                     
31447                     break;
31448                 default :
31449                     break;
31450                     
31451             }
31452             
31453         }, this);
31454         
31455         if(box.length){
31456             boxes.push(box);
31457             box = [];
31458         }
31459         
31460         var filterPattern = function(box, length)
31461         {
31462             if(!box.length){
31463                 return;
31464             }
31465             
31466             var match = false;
31467             
31468             var pattern = box.slice(0, length);
31469             
31470             var format = [];
31471             
31472             Roo.each(pattern, function(i){
31473                 format.push(i.size);
31474             }, this);
31475             
31476             Roo.each(standard, function(s){
31477                 
31478                 if(String(s) != String(format)){
31479                     return;
31480                 }
31481                 
31482                 match = true;
31483                 return false;
31484                 
31485             }, this);
31486             
31487             if(!match && length == 1){
31488                 return;
31489             }
31490             
31491             if(!match){
31492                 filterPattern(box, length - 1);
31493                 return;
31494             }
31495                 
31496             queue.push(pattern);
31497
31498             box = box.slice(length, box.length);
31499
31500             filterPattern(box, 4);
31501
31502             return;
31503             
31504         }
31505         
31506         Roo.each(boxes, function(box, k){
31507             
31508             if(!box.length){
31509                 return;
31510             }
31511             
31512             if(box.length == 1){
31513                 queue.push(box);
31514                 return;
31515             }
31516             
31517             filterPattern(box, 4);
31518             
31519         }, this);
31520         
31521         
31522         var prune = [];
31523         
31524         var pos = this.el.getBox(true);
31525         
31526         var minX = pos.x;
31527         
31528         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31529         
31530         var hit_end = false;
31531         
31532         Roo.each(queue, function(box){
31533             
31534             if(hit_end){
31535                 
31536                 Roo.each(box, function(b){
31537                 
31538                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31539                     b.el.hide();
31540
31541                 }, this);
31542
31543                 return;
31544             }
31545             
31546             var mx = 0;
31547             
31548             Roo.each(box, function(b){
31549                 
31550                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31551                 b.el.show();
31552
31553                 mx = Math.max(mx, b.x);
31554                 
31555             }, this);
31556             
31557             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31558             
31559             if(maxX < minX){
31560                 
31561                 Roo.each(box, function(b){
31562                 
31563                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31564                     b.el.hide();
31565                     
31566                 }, this);
31567                 
31568                 hit_end = true;
31569                 
31570                 return;
31571             }
31572             
31573             prune.push(box);
31574             
31575         }, this);
31576         
31577         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31578     },
31579     
31580     /** Sets position of item in DOM
31581     * @param {Element} item
31582     * @param {Number} x - horizontal position
31583     * @param {Number} y - vertical position
31584     * @param {Boolean} isInstant - disables transitions
31585     */
31586     _processVerticalLayoutQueue : function( queue, isInstant )
31587     {
31588         var pos = this.el.getBox(true);
31589         var x = pos.x;
31590         var y = pos.y;
31591         var maxY = [];
31592         
31593         for (var i = 0; i < this.cols; i++){
31594             maxY[i] = pos.y;
31595         }
31596         
31597         Roo.each(queue, function(box, k){
31598             
31599             var col = k % this.cols;
31600             
31601             Roo.each(box, function(b,kk){
31602                 
31603                 b.el.position('absolute');
31604                 
31605                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31606                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31607                 
31608                 if(b.size == 'md-left' || b.size == 'md-right'){
31609                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31610                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31611                 }
31612                 
31613                 b.el.setWidth(width);
31614                 b.el.setHeight(height);
31615                 // iframe?
31616                 b.el.select('iframe',true).setSize(width,height);
31617                 
31618             }, this);
31619             
31620             for (var i = 0; i < this.cols; i++){
31621                 
31622                 if(maxY[i] < maxY[col]){
31623                     col = i;
31624                     continue;
31625                 }
31626                 
31627                 col = Math.min(col, i);
31628                 
31629             }
31630             
31631             x = pos.x + col * (this.colWidth + this.padWidth);
31632             
31633             y = maxY[col];
31634             
31635             var positions = [];
31636             
31637             switch (box.length){
31638                 case 1 :
31639                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31640                     break;
31641                 case 2 :
31642                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31643                     break;
31644                 case 3 :
31645                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31646                     break;
31647                 case 4 :
31648                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31649                     break;
31650                 default :
31651                     break;
31652             }
31653             
31654             Roo.each(box, function(b,kk){
31655                 
31656                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31657                 
31658                 var sz = b.el.getSize();
31659                 
31660                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31661                 
31662             }, this);
31663             
31664         }, this);
31665         
31666         var mY = 0;
31667         
31668         for (var i = 0; i < this.cols; i++){
31669             mY = Math.max(mY, maxY[i]);
31670         }
31671         
31672         this.el.setHeight(mY - pos.y);
31673         
31674     },
31675     
31676 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31677 //    {
31678 //        var pos = this.el.getBox(true);
31679 //        var x = pos.x;
31680 //        var y = pos.y;
31681 //        var maxX = pos.right;
31682 //        
31683 //        var maxHeight = 0;
31684 //        
31685 //        Roo.each(items, function(item, k){
31686 //            
31687 //            var c = k % 2;
31688 //            
31689 //            item.el.position('absolute');
31690 //                
31691 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31692 //
31693 //            item.el.setWidth(width);
31694 //
31695 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31696 //
31697 //            item.el.setHeight(height);
31698 //            
31699 //            if(c == 0){
31700 //                item.el.setXY([x, y], isInstant ? false : true);
31701 //            } else {
31702 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31703 //            }
31704 //            
31705 //            y = y + height + this.alternativePadWidth;
31706 //            
31707 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31708 //            
31709 //        }, this);
31710 //        
31711 //        this.el.setHeight(maxHeight);
31712 //        
31713 //    },
31714     
31715     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31716     {
31717         var pos = this.el.getBox(true);
31718         
31719         var minX = pos.x;
31720         var minY = pos.y;
31721         
31722         var maxX = pos.right;
31723         
31724         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31725         
31726         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31727         
31728         Roo.each(queue, function(box, k){
31729             
31730             Roo.each(box, function(b, kk){
31731                 
31732                 b.el.position('absolute');
31733                 
31734                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31735                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31736                 
31737                 if(b.size == 'md-left' || b.size == 'md-right'){
31738                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31739                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31740                 }
31741                 
31742                 b.el.setWidth(width);
31743                 b.el.setHeight(height);
31744                 
31745             }, this);
31746             
31747             if(!box.length){
31748                 return;
31749             }
31750             
31751             var positions = [];
31752             
31753             switch (box.length){
31754                 case 1 :
31755                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31756                     break;
31757                 case 2 :
31758                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31759                     break;
31760                 case 3 :
31761                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31762                     break;
31763                 case 4 :
31764                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31765                     break;
31766                 default :
31767                     break;
31768             }
31769             
31770             Roo.each(box, function(b,kk){
31771                 
31772                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31773                 
31774                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31775                 
31776             }, this);
31777             
31778         }, this);
31779         
31780     },
31781     
31782     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31783     {
31784         Roo.each(eItems, function(b,k){
31785             
31786             b.size = (k == 0) ? 'sm' : 'xs';
31787             b.x = (k == 0) ? 2 : 1;
31788             b.y = (k == 0) ? 2 : 1;
31789             
31790             b.el.position('absolute');
31791             
31792             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31793                 
31794             b.el.setWidth(width);
31795             
31796             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31797             
31798             b.el.setHeight(height);
31799             
31800         }, this);
31801
31802         var positions = [];
31803         
31804         positions.push({
31805             x : maxX - this.unitWidth * 2 - this.gutter,
31806             y : minY
31807         });
31808         
31809         positions.push({
31810             x : maxX - this.unitWidth,
31811             y : minY + (this.unitWidth + this.gutter) * 2
31812         });
31813         
31814         positions.push({
31815             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31816             y : minY
31817         });
31818         
31819         Roo.each(eItems, function(b,k){
31820             
31821             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31822
31823         }, this);
31824         
31825     },
31826     
31827     getVerticalOneBoxColPositions : function(x, y, box)
31828     {
31829         var pos = [];
31830         
31831         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31832         
31833         if(box[0].size == 'md-left'){
31834             rand = 0;
31835         }
31836         
31837         if(box[0].size == 'md-right'){
31838             rand = 1;
31839         }
31840         
31841         pos.push({
31842             x : x + (this.unitWidth + this.gutter) * rand,
31843             y : y
31844         });
31845         
31846         return pos;
31847     },
31848     
31849     getVerticalTwoBoxColPositions : function(x, y, box)
31850     {
31851         var pos = [];
31852         
31853         if(box[0].size == 'xs'){
31854             
31855             pos.push({
31856                 x : x,
31857                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31858             });
31859
31860             pos.push({
31861                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31862                 y : y
31863             });
31864             
31865             return pos;
31866             
31867         }
31868         
31869         pos.push({
31870             x : x,
31871             y : y
31872         });
31873
31874         pos.push({
31875             x : x + (this.unitWidth + this.gutter) * 2,
31876             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31877         });
31878         
31879         return pos;
31880         
31881     },
31882     
31883     getVerticalThreeBoxColPositions : function(x, y, box)
31884     {
31885         var pos = [];
31886         
31887         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31888             
31889             pos.push({
31890                 x : x,
31891                 y : y
31892             });
31893
31894             pos.push({
31895                 x : x + (this.unitWidth + this.gutter) * 1,
31896                 y : y
31897             });
31898             
31899             pos.push({
31900                 x : x + (this.unitWidth + this.gutter) * 2,
31901                 y : y
31902             });
31903             
31904             return pos;
31905             
31906         }
31907         
31908         if(box[0].size == 'xs' && box[1].size == 'xs'){
31909             
31910             pos.push({
31911                 x : x,
31912                 y : y
31913             });
31914
31915             pos.push({
31916                 x : x,
31917                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31918             });
31919             
31920             pos.push({
31921                 x : x + (this.unitWidth + this.gutter) * 1,
31922                 y : y
31923             });
31924             
31925             return pos;
31926             
31927         }
31928         
31929         pos.push({
31930             x : x,
31931             y : y
31932         });
31933
31934         pos.push({
31935             x : x + (this.unitWidth + this.gutter) * 2,
31936             y : y
31937         });
31938
31939         pos.push({
31940             x : x + (this.unitWidth + this.gutter) * 2,
31941             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31942         });
31943             
31944         return pos;
31945         
31946     },
31947     
31948     getVerticalFourBoxColPositions : function(x, y, box)
31949     {
31950         var pos = [];
31951         
31952         if(box[0].size == 'xs'){
31953             
31954             pos.push({
31955                 x : x,
31956                 y : y
31957             });
31958
31959             pos.push({
31960                 x : x,
31961                 y : y + (this.unitHeight + this.gutter) * 1
31962             });
31963             
31964             pos.push({
31965                 x : x,
31966                 y : y + (this.unitHeight + this.gutter) * 2
31967             });
31968             
31969             pos.push({
31970                 x : x + (this.unitWidth + this.gutter) * 1,
31971                 y : y
31972             });
31973             
31974             return pos;
31975             
31976         }
31977         
31978         pos.push({
31979             x : x,
31980             y : y
31981         });
31982
31983         pos.push({
31984             x : x + (this.unitWidth + this.gutter) * 2,
31985             y : y
31986         });
31987
31988         pos.push({
31989             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31990             y : y + (this.unitHeight + this.gutter) * 1
31991         });
31992
31993         pos.push({
31994             x : x + (this.unitWidth + this.gutter) * 2,
31995             y : y + (this.unitWidth + this.gutter) * 2
31996         });
31997
31998         return pos;
31999         
32000     },
32001     
32002     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32003     {
32004         var pos = [];
32005         
32006         if(box[0].size == 'md-left'){
32007             pos.push({
32008                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32009                 y : minY
32010             });
32011             
32012             return pos;
32013         }
32014         
32015         if(box[0].size == 'md-right'){
32016             pos.push({
32017                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32018                 y : minY + (this.unitWidth + this.gutter) * 1
32019             });
32020             
32021             return pos;
32022         }
32023         
32024         var rand = Math.floor(Math.random() * (4 - box[0].y));
32025         
32026         pos.push({
32027             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32028             y : minY + (this.unitWidth + this.gutter) * rand
32029         });
32030         
32031         return pos;
32032         
32033     },
32034     
32035     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32036     {
32037         var pos = [];
32038         
32039         if(box[0].size == 'xs'){
32040             
32041             pos.push({
32042                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32043                 y : minY
32044             });
32045
32046             pos.push({
32047                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32048                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32049             });
32050             
32051             return pos;
32052             
32053         }
32054         
32055         pos.push({
32056             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32057             y : minY
32058         });
32059
32060         pos.push({
32061             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32062             y : minY + (this.unitWidth + this.gutter) * 2
32063         });
32064         
32065         return pos;
32066         
32067     },
32068     
32069     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32070     {
32071         var pos = [];
32072         
32073         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32074             
32075             pos.push({
32076                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32077                 y : minY
32078             });
32079
32080             pos.push({
32081                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32082                 y : minY + (this.unitWidth + this.gutter) * 1
32083             });
32084             
32085             pos.push({
32086                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32087                 y : minY + (this.unitWidth + this.gutter) * 2
32088             });
32089             
32090             return pos;
32091             
32092         }
32093         
32094         if(box[0].size == 'xs' && box[1].size == 'xs'){
32095             
32096             pos.push({
32097                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32098                 y : minY
32099             });
32100
32101             pos.push({
32102                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32103                 y : minY
32104             });
32105             
32106             pos.push({
32107                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32108                 y : minY + (this.unitWidth + this.gutter) * 1
32109             });
32110             
32111             return pos;
32112             
32113         }
32114         
32115         pos.push({
32116             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32117             y : minY
32118         });
32119
32120         pos.push({
32121             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32122             y : minY + (this.unitWidth + this.gutter) * 2
32123         });
32124
32125         pos.push({
32126             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32127             y : minY + (this.unitWidth + this.gutter) * 2
32128         });
32129             
32130         return pos;
32131         
32132     },
32133     
32134     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32135     {
32136         var pos = [];
32137         
32138         if(box[0].size == 'xs'){
32139             
32140             pos.push({
32141                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32142                 y : minY
32143             });
32144
32145             pos.push({
32146                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32147                 y : minY
32148             });
32149             
32150             pos.push({
32151                 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),
32152                 y : minY
32153             });
32154             
32155             pos.push({
32156                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32157                 y : minY + (this.unitWidth + this.gutter) * 1
32158             });
32159             
32160             return pos;
32161             
32162         }
32163         
32164         pos.push({
32165             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32166             y : minY
32167         });
32168         
32169         pos.push({
32170             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32171             y : minY + (this.unitWidth + this.gutter) * 2
32172         });
32173         
32174         pos.push({
32175             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32176             y : minY + (this.unitWidth + this.gutter) * 2
32177         });
32178         
32179         pos.push({
32180             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),
32181             y : minY + (this.unitWidth + this.gutter) * 2
32182         });
32183
32184         return pos;
32185         
32186     },
32187     
32188     /**
32189     * remove a Masonry Brick
32190     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32191     */
32192     removeBrick : function(brick_id)
32193     {
32194         if (!brick_id) {
32195             return;
32196         }
32197         
32198         for (var i = 0; i<this.bricks.length; i++) {
32199             if (this.bricks[i].id == brick_id) {
32200                 this.bricks.splice(i,1);
32201                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32202                 this.initial();
32203             }
32204         }
32205     },
32206     
32207     /**
32208     * adds a Masonry Brick
32209     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32210     */
32211     addBrick : function(cfg)
32212     {
32213         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32214         //this.register(cn);
32215         cn.parentId = this.id;
32216         cn.render(this.el);
32217         return cn;
32218     },
32219     
32220     /**
32221     * register a Masonry Brick
32222     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32223     */
32224     
32225     register : function(brick)
32226     {
32227         this.bricks.push(brick);
32228         brick.masonryId = this.id;
32229     },
32230     
32231     /**
32232     * clear all the Masonry Brick
32233     */
32234     clearAll : function()
32235     {
32236         this.bricks = [];
32237         //this.getChildContainer().dom.innerHTML = "";
32238         this.el.dom.innerHTML = '';
32239     },
32240     
32241     getSelected : function()
32242     {
32243         if (!this.selectedBrick) {
32244             return false;
32245         }
32246         
32247         return this.selectedBrick;
32248     }
32249 });
32250
32251 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32252     
32253     groups: {},
32254      /**
32255     * register a Masonry Layout
32256     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32257     */
32258     
32259     register : function(layout)
32260     {
32261         this.groups[layout.id] = layout;
32262     },
32263     /**
32264     * fetch a  Masonry Layout based on the masonry layout ID
32265     * @param {string} the masonry layout to add
32266     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32267     */
32268     
32269     get: function(layout_id) {
32270         if (typeof(this.groups[layout_id]) == 'undefined') {
32271             return false;
32272         }
32273         return this.groups[layout_id] ;
32274     }
32275     
32276     
32277     
32278 });
32279
32280  
32281
32282  /**
32283  *
32284  * This is based on 
32285  * http://masonry.desandro.com
32286  *
32287  * The idea is to render all the bricks based on vertical width...
32288  *
32289  * The original code extends 'outlayer' - we might need to use that....
32290  * 
32291  */
32292
32293
32294 /**
32295  * @class Roo.bootstrap.LayoutMasonryAuto
32296  * @extends Roo.bootstrap.Component
32297  * Bootstrap Layout Masonry class
32298  * 
32299  * @constructor
32300  * Create a new Element
32301  * @param {Object} config The config object
32302  */
32303
32304 Roo.bootstrap.LayoutMasonryAuto = function(config){
32305     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32306 };
32307
32308 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32309     
32310       /**
32311      * @cfg {Boolean} isFitWidth  - resize the width..
32312      */   
32313     isFitWidth : false,  // options..
32314     /**
32315      * @cfg {Boolean} isOriginLeft = left align?
32316      */   
32317     isOriginLeft : true,
32318     /**
32319      * @cfg {Boolean} isOriginTop = top align?
32320      */   
32321     isOriginTop : false,
32322     /**
32323      * @cfg {Boolean} isLayoutInstant = no animation?
32324      */   
32325     isLayoutInstant : false, // needed?
32326     /**
32327      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32328      */   
32329     isResizingContainer : true,
32330     /**
32331      * @cfg {Number} columnWidth  width of the columns 
32332      */   
32333     
32334     columnWidth : 0,
32335     
32336     /**
32337      * @cfg {Number} maxCols maximum number of columns
32338      */   
32339     
32340     maxCols: 0,
32341     /**
32342      * @cfg {Number} padHeight padding below box..
32343      */   
32344     
32345     padHeight : 10, 
32346     
32347     /**
32348      * @cfg {Boolean} isAutoInitial defalut true
32349      */   
32350     
32351     isAutoInitial : true, 
32352     
32353     // private?
32354     gutter : 0,
32355     
32356     containerWidth: 0,
32357     initialColumnWidth : 0,
32358     currentSize : null,
32359     
32360     colYs : null, // array.
32361     maxY : 0,
32362     padWidth: 10,
32363     
32364     
32365     tag: 'div',
32366     cls: '',
32367     bricks: null, //CompositeElement
32368     cols : 0, // array?
32369     // element : null, // wrapped now this.el
32370     _isLayoutInited : null, 
32371     
32372     
32373     getAutoCreate : function(){
32374         
32375         var cfg = {
32376             tag: this.tag,
32377             cls: 'blog-masonary-wrapper ' + this.cls,
32378             cn : {
32379                 cls : 'mas-boxes masonary'
32380             }
32381         };
32382         
32383         return cfg;
32384     },
32385     
32386     getChildContainer: function( )
32387     {
32388         if (this.boxesEl) {
32389             return this.boxesEl;
32390         }
32391         
32392         this.boxesEl = this.el.select('.mas-boxes').first();
32393         
32394         return this.boxesEl;
32395     },
32396     
32397     
32398     initEvents : function()
32399     {
32400         var _this = this;
32401         
32402         if(this.isAutoInitial){
32403             Roo.log('hook children rendered');
32404             this.on('childrenrendered', function() {
32405                 Roo.log('children rendered');
32406                 _this.initial();
32407             } ,this);
32408         }
32409         
32410     },
32411     
32412     initial : function()
32413     {
32414         this.reloadItems();
32415
32416         this.currentSize = this.el.getBox(true);
32417
32418         /// was window resize... - let's see if this works..
32419         Roo.EventManager.onWindowResize(this.resize, this); 
32420
32421         if(!this.isAutoInitial){
32422             this.layout();
32423             return;
32424         }
32425         
32426         this.layout.defer(500,this);
32427     },
32428     
32429     reloadItems: function()
32430     {
32431         this.bricks = this.el.select('.masonry-brick', true);
32432         
32433         this.bricks.each(function(b) {
32434             //Roo.log(b.getSize());
32435             if (!b.attr('originalwidth')) {
32436                 b.attr('originalwidth',  b.getSize().width);
32437             }
32438             
32439         });
32440         
32441         Roo.log(this.bricks.elements.length);
32442     },
32443     
32444     resize : function()
32445     {
32446         Roo.log('resize');
32447         var cs = this.el.getBox(true);
32448         
32449         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32450             Roo.log("no change in with or X");
32451             return;
32452         }
32453         this.currentSize = cs;
32454         this.layout();
32455     },
32456     
32457     layout : function()
32458     {
32459          Roo.log('layout');
32460         this._resetLayout();
32461         //this._manageStamps();
32462       
32463         // don't animate first layout
32464         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32465         this.layoutItems( isInstant );
32466       
32467         // flag for initalized
32468         this._isLayoutInited = true;
32469     },
32470     
32471     layoutItems : function( isInstant )
32472     {
32473         //var items = this._getItemsForLayout( this.items );
32474         // original code supports filtering layout items.. we just ignore it..
32475         
32476         this._layoutItems( this.bricks , isInstant );
32477       
32478         this._postLayout();
32479     },
32480     _layoutItems : function ( items , isInstant)
32481     {
32482        //this.fireEvent( 'layout', this, items );
32483     
32484
32485         if ( !items || !items.elements.length ) {
32486           // no items, emit event with empty array
32487             return;
32488         }
32489
32490         var queue = [];
32491         items.each(function(item) {
32492             Roo.log("layout item");
32493             Roo.log(item);
32494             // get x/y object from method
32495             var position = this._getItemLayoutPosition( item );
32496             // enqueue
32497             position.item = item;
32498             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32499             queue.push( position );
32500         }, this);
32501       
32502         this._processLayoutQueue( queue );
32503     },
32504     /** Sets position of item in DOM
32505     * @param {Element} item
32506     * @param {Number} x - horizontal position
32507     * @param {Number} y - vertical position
32508     * @param {Boolean} isInstant - disables transitions
32509     */
32510     _processLayoutQueue : function( queue )
32511     {
32512         for ( var i=0, len = queue.length; i < len; i++ ) {
32513             var obj = queue[i];
32514             obj.item.position('absolute');
32515             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32516         }
32517     },
32518       
32519     
32520     /**
32521     * Any logic you want to do after each layout,
32522     * i.e. size the container
32523     */
32524     _postLayout : function()
32525     {
32526         this.resizeContainer();
32527     },
32528     
32529     resizeContainer : function()
32530     {
32531         if ( !this.isResizingContainer ) {
32532             return;
32533         }
32534         var size = this._getContainerSize();
32535         if ( size ) {
32536             this.el.setSize(size.width,size.height);
32537             this.boxesEl.setSize(size.width,size.height);
32538         }
32539     },
32540     
32541     
32542     
32543     _resetLayout : function()
32544     {
32545         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32546         this.colWidth = this.el.getWidth();
32547         //this.gutter = this.el.getWidth(); 
32548         
32549         this.measureColumns();
32550
32551         // reset column Y
32552         var i = this.cols;
32553         this.colYs = [];
32554         while (i--) {
32555             this.colYs.push( 0 );
32556         }
32557     
32558         this.maxY = 0;
32559     },
32560
32561     measureColumns : function()
32562     {
32563         this.getContainerWidth();
32564       // if columnWidth is 0, default to outerWidth of first item
32565         if ( !this.columnWidth ) {
32566             var firstItem = this.bricks.first();
32567             Roo.log(firstItem);
32568             this.columnWidth  = this.containerWidth;
32569             if (firstItem && firstItem.attr('originalwidth') ) {
32570                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32571             }
32572             // columnWidth fall back to item of first element
32573             Roo.log("set column width?");
32574                         this.initialColumnWidth = this.columnWidth  ;
32575
32576             // if first elem has no width, default to size of container
32577             
32578         }
32579         
32580         
32581         if (this.initialColumnWidth) {
32582             this.columnWidth = this.initialColumnWidth;
32583         }
32584         
32585         
32586             
32587         // column width is fixed at the top - however if container width get's smaller we should
32588         // reduce it...
32589         
32590         // this bit calcs how man columns..
32591             
32592         var columnWidth = this.columnWidth += this.gutter;
32593       
32594         // calculate columns
32595         var containerWidth = this.containerWidth + this.gutter;
32596         
32597         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32598         // fix rounding errors, typically with gutters
32599         var excess = columnWidth - containerWidth % columnWidth;
32600         
32601         
32602         // if overshoot is less than a pixel, round up, otherwise floor it
32603         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32604         cols = Math[ mathMethod ]( cols );
32605         this.cols = Math.max( cols, 1 );
32606         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32607         
32608          // padding positioning..
32609         var totalColWidth = this.cols * this.columnWidth;
32610         var padavail = this.containerWidth - totalColWidth;
32611         // so for 2 columns - we need 3 'pads'
32612         
32613         var padNeeded = (1+this.cols) * this.padWidth;
32614         
32615         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32616         
32617         this.columnWidth += padExtra
32618         //this.padWidth = Math.floor(padavail /  ( this.cols));
32619         
32620         // adjust colum width so that padding is fixed??
32621         
32622         // we have 3 columns ... total = width * 3
32623         // we have X left over... that should be used by 
32624         
32625         //if (this.expandC) {
32626             
32627         //}
32628         
32629         
32630         
32631     },
32632     
32633     getContainerWidth : function()
32634     {
32635        /* // container is parent if fit width
32636         var container = this.isFitWidth ? this.element.parentNode : this.element;
32637         // check that this.size and size are there
32638         // IE8 triggers resize on body size change, so they might not be
32639         
32640         var size = getSize( container );  //FIXME
32641         this.containerWidth = size && size.innerWidth; //FIXME
32642         */
32643          
32644         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32645         
32646     },
32647     
32648     _getItemLayoutPosition : function( item )  // what is item?
32649     {
32650         // we resize the item to our columnWidth..
32651       
32652         item.setWidth(this.columnWidth);
32653         item.autoBoxAdjust  = false;
32654         
32655         var sz = item.getSize();
32656  
32657         // how many columns does this brick span
32658         var remainder = this.containerWidth % this.columnWidth;
32659         
32660         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32661         // round if off by 1 pixel, otherwise use ceil
32662         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32663         colSpan = Math.min( colSpan, this.cols );
32664         
32665         // normally this should be '1' as we dont' currently allow multi width columns..
32666         
32667         var colGroup = this._getColGroup( colSpan );
32668         // get the minimum Y value from the columns
32669         var minimumY = Math.min.apply( Math, colGroup );
32670         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32671         
32672         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32673          
32674         // position the brick
32675         var position = {
32676             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32677             y: this.currentSize.y + minimumY + this.padHeight
32678         };
32679         
32680         Roo.log(position);
32681         // apply setHeight to necessary columns
32682         var setHeight = minimumY + sz.height + this.padHeight;
32683         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32684         
32685         var setSpan = this.cols + 1 - colGroup.length;
32686         for ( var i = 0; i < setSpan; i++ ) {
32687           this.colYs[ shortColIndex + i ] = setHeight ;
32688         }
32689       
32690         return position;
32691     },
32692     
32693     /**
32694      * @param {Number} colSpan - number of columns the element spans
32695      * @returns {Array} colGroup
32696      */
32697     _getColGroup : function( colSpan )
32698     {
32699         if ( colSpan < 2 ) {
32700           // if brick spans only one column, use all the column Ys
32701           return this.colYs;
32702         }
32703       
32704         var colGroup = [];
32705         // how many different places could this brick fit horizontally
32706         var groupCount = this.cols + 1 - colSpan;
32707         // for each group potential horizontal position
32708         for ( var i = 0; i < groupCount; i++ ) {
32709           // make an array of colY values for that one group
32710           var groupColYs = this.colYs.slice( i, i + colSpan );
32711           // and get the max value of the array
32712           colGroup[i] = Math.max.apply( Math, groupColYs );
32713         }
32714         return colGroup;
32715     },
32716     /*
32717     _manageStamp : function( stamp )
32718     {
32719         var stampSize =  stamp.getSize();
32720         var offset = stamp.getBox();
32721         // get the columns that this stamp affects
32722         var firstX = this.isOriginLeft ? offset.x : offset.right;
32723         var lastX = firstX + stampSize.width;
32724         var firstCol = Math.floor( firstX / this.columnWidth );
32725         firstCol = Math.max( 0, firstCol );
32726         
32727         var lastCol = Math.floor( lastX / this.columnWidth );
32728         // lastCol should not go over if multiple of columnWidth #425
32729         lastCol -= lastX % this.columnWidth ? 0 : 1;
32730         lastCol = Math.min( this.cols - 1, lastCol );
32731         
32732         // set colYs to bottom of the stamp
32733         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32734             stampSize.height;
32735             
32736         for ( var i = firstCol; i <= lastCol; i++ ) {
32737           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32738         }
32739     },
32740     */
32741     
32742     _getContainerSize : function()
32743     {
32744         this.maxY = Math.max.apply( Math, this.colYs );
32745         var size = {
32746             height: this.maxY
32747         };
32748       
32749         if ( this.isFitWidth ) {
32750             size.width = this._getContainerFitWidth();
32751         }
32752       
32753         return size;
32754     },
32755     
32756     _getContainerFitWidth : function()
32757     {
32758         var unusedCols = 0;
32759         // count unused columns
32760         var i = this.cols;
32761         while ( --i ) {
32762           if ( this.colYs[i] !== 0 ) {
32763             break;
32764           }
32765           unusedCols++;
32766         }
32767         // fit container to columns that have been used
32768         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32769     },
32770     
32771     needsResizeLayout : function()
32772     {
32773         var previousWidth = this.containerWidth;
32774         this.getContainerWidth();
32775         return previousWidth !== this.containerWidth;
32776     }
32777  
32778 });
32779
32780  
32781
32782  /*
32783  * - LGPL
32784  *
32785  * element
32786  * 
32787  */
32788
32789 /**
32790  * @class Roo.bootstrap.MasonryBrick
32791  * @extends Roo.bootstrap.Component
32792  * Bootstrap MasonryBrick class
32793  * 
32794  * @constructor
32795  * Create a new MasonryBrick
32796  * @param {Object} config The config object
32797  */
32798
32799 Roo.bootstrap.MasonryBrick = function(config){
32800     
32801     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32802     
32803     Roo.bootstrap.MasonryBrick.register(this);
32804     
32805     this.addEvents({
32806         // raw events
32807         /**
32808          * @event click
32809          * When a MasonryBrick is clcik
32810          * @param {Roo.bootstrap.MasonryBrick} this
32811          * @param {Roo.EventObject} e
32812          */
32813         "click" : true
32814     });
32815 };
32816
32817 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32818     
32819     /**
32820      * @cfg {String} title
32821      */   
32822     title : '',
32823     /**
32824      * @cfg {String} html
32825      */   
32826     html : '',
32827     /**
32828      * @cfg {String} bgimage
32829      */   
32830     bgimage : '',
32831     /**
32832      * @cfg {String} videourl
32833      */   
32834     videourl : '',
32835     /**
32836      * @cfg {String} cls
32837      */   
32838     cls : '',
32839     /**
32840      * @cfg {String} href
32841      */   
32842     href : '',
32843     /**
32844      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32845      */   
32846     size : 'xs',
32847     
32848     /**
32849      * @cfg {String} placetitle (center|bottom)
32850      */   
32851     placetitle : '',
32852     
32853     /**
32854      * @cfg {Boolean} isFitContainer defalut true
32855      */   
32856     isFitContainer : true, 
32857     
32858     /**
32859      * @cfg {Boolean} preventDefault defalut false
32860      */   
32861     preventDefault : false, 
32862     
32863     /**
32864      * @cfg {Boolean} inverse defalut false
32865      */   
32866     maskInverse : false, 
32867     
32868     getAutoCreate : function()
32869     {
32870         if(!this.isFitContainer){
32871             return this.getSplitAutoCreate();
32872         }
32873         
32874         var cls = 'masonry-brick masonry-brick-full';
32875         
32876         if(this.href.length){
32877             cls += ' masonry-brick-link';
32878         }
32879         
32880         if(this.bgimage.length){
32881             cls += ' masonry-brick-image';
32882         }
32883         
32884         if(this.maskInverse){
32885             cls += ' mask-inverse';
32886         }
32887         
32888         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32889             cls += ' enable-mask';
32890         }
32891         
32892         if(this.size){
32893             cls += ' masonry-' + this.size + '-brick';
32894         }
32895         
32896         if(this.placetitle.length){
32897             
32898             switch (this.placetitle) {
32899                 case 'center' :
32900                     cls += ' masonry-center-title';
32901                     break;
32902                 case 'bottom' :
32903                     cls += ' masonry-bottom-title';
32904                     break;
32905                 default:
32906                     break;
32907             }
32908             
32909         } else {
32910             if(!this.html.length && !this.bgimage.length){
32911                 cls += ' masonry-center-title';
32912             }
32913
32914             if(!this.html.length && this.bgimage.length){
32915                 cls += ' masonry-bottom-title';
32916             }
32917         }
32918         
32919         if(this.cls){
32920             cls += ' ' + this.cls;
32921         }
32922         
32923         var cfg = {
32924             tag: (this.href.length) ? 'a' : 'div',
32925             cls: cls,
32926             cn: [
32927                 {
32928                     tag: 'div',
32929                     cls: 'masonry-brick-mask'
32930                 },
32931                 {
32932                     tag: 'div',
32933                     cls: 'masonry-brick-paragraph',
32934                     cn: []
32935                 }
32936             ]
32937         };
32938         
32939         if(this.href.length){
32940             cfg.href = this.href;
32941         }
32942         
32943         var cn = cfg.cn[1].cn;
32944         
32945         if(this.title.length){
32946             cn.push({
32947                 tag: 'h4',
32948                 cls: 'masonry-brick-title',
32949                 html: this.title
32950             });
32951         }
32952         
32953         if(this.html.length){
32954             cn.push({
32955                 tag: 'p',
32956                 cls: 'masonry-brick-text',
32957                 html: this.html
32958             });
32959         }
32960         
32961         if (!this.title.length && !this.html.length) {
32962             cfg.cn[1].cls += ' hide';
32963         }
32964         
32965         if(this.bgimage.length){
32966             cfg.cn.push({
32967                 tag: 'img',
32968                 cls: 'masonry-brick-image-view',
32969                 src: this.bgimage
32970             });
32971         }
32972         
32973         if(this.videourl.length){
32974             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32975             // youtube support only?
32976             cfg.cn.push({
32977                 tag: 'iframe',
32978                 cls: 'masonry-brick-image-view',
32979                 src: vurl,
32980                 frameborder : 0,
32981                 allowfullscreen : true
32982             });
32983         }
32984         
32985         return cfg;
32986         
32987     },
32988     
32989     getSplitAutoCreate : function()
32990     {
32991         var cls = 'masonry-brick masonry-brick-split';
32992         
32993         if(this.href.length){
32994             cls += ' masonry-brick-link';
32995         }
32996         
32997         if(this.bgimage.length){
32998             cls += ' masonry-brick-image';
32999         }
33000         
33001         if(this.size){
33002             cls += ' masonry-' + this.size + '-brick';
33003         }
33004         
33005         switch (this.placetitle) {
33006             case 'center' :
33007                 cls += ' masonry-center-title';
33008                 break;
33009             case 'bottom' :
33010                 cls += ' masonry-bottom-title';
33011                 break;
33012             default:
33013                 if(!this.bgimage.length){
33014                     cls += ' masonry-center-title';
33015                 }
33016
33017                 if(this.bgimage.length){
33018                     cls += ' masonry-bottom-title';
33019                 }
33020                 break;
33021         }
33022         
33023         if(this.cls){
33024             cls += ' ' + this.cls;
33025         }
33026         
33027         var cfg = {
33028             tag: (this.href.length) ? 'a' : 'div',
33029             cls: cls,
33030             cn: [
33031                 {
33032                     tag: 'div',
33033                     cls: 'masonry-brick-split-head',
33034                     cn: [
33035                         {
33036                             tag: 'div',
33037                             cls: 'masonry-brick-paragraph',
33038                             cn: []
33039                         }
33040                     ]
33041                 },
33042                 {
33043                     tag: 'div',
33044                     cls: 'masonry-brick-split-body',
33045                     cn: []
33046                 }
33047             ]
33048         };
33049         
33050         if(this.href.length){
33051             cfg.href = this.href;
33052         }
33053         
33054         if(this.title.length){
33055             cfg.cn[0].cn[0].cn.push({
33056                 tag: 'h4',
33057                 cls: 'masonry-brick-title',
33058                 html: this.title
33059             });
33060         }
33061         
33062         if(this.html.length){
33063             cfg.cn[1].cn.push({
33064                 tag: 'p',
33065                 cls: 'masonry-brick-text',
33066                 html: this.html
33067             });
33068         }
33069
33070         if(this.bgimage.length){
33071             cfg.cn[0].cn.push({
33072                 tag: 'img',
33073                 cls: 'masonry-brick-image-view',
33074                 src: this.bgimage
33075             });
33076         }
33077         
33078         if(this.videourl.length){
33079             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33080             // youtube support only?
33081             cfg.cn[0].cn.cn.push({
33082                 tag: 'iframe',
33083                 cls: 'masonry-brick-image-view',
33084                 src: vurl,
33085                 frameborder : 0,
33086                 allowfullscreen : true
33087             });
33088         }
33089         
33090         return cfg;
33091     },
33092     
33093     initEvents: function() 
33094     {
33095         switch (this.size) {
33096             case 'xs' :
33097                 this.x = 1;
33098                 this.y = 1;
33099                 break;
33100             case 'sm' :
33101                 this.x = 2;
33102                 this.y = 2;
33103                 break;
33104             case 'md' :
33105             case 'md-left' :
33106             case 'md-right' :
33107                 this.x = 3;
33108                 this.y = 3;
33109                 break;
33110             case 'tall' :
33111                 this.x = 2;
33112                 this.y = 3;
33113                 break;
33114             case 'wide' :
33115                 this.x = 3;
33116                 this.y = 2;
33117                 break;
33118             case 'wide-thin' :
33119                 this.x = 3;
33120                 this.y = 1;
33121                 break;
33122                         
33123             default :
33124                 break;
33125         }
33126         
33127         if(Roo.isTouch){
33128             this.el.on('touchstart', this.onTouchStart, this);
33129             this.el.on('touchmove', this.onTouchMove, this);
33130             this.el.on('touchend', this.onTouchEnd, this);
33131             this.el.on('contextmenu', this.onContextMenu, this);
33132         } else {
33133             this.el.on('mouseenter'  ,this.enter, this);
33134             this.el.on('mouseleave', this.leave, this);
33135             this.el.on('click', this.onClick, this);
33136         }
33137         
33138         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33139             this.parent().bricks.push(this);   
33140         }
33141         
33142     },
33143     
33144     onClick: function(e, el)
33145     {
33146         var time = this.endTimer - this.startTimer;
33147         // Roo.log(e.preventDefault());
33148         if(Roo.isTouch){
33149             if(time > 1000){
33150                 e.preventDefault();
33151                 return;
33152             }
33153         }
33154         
33155         if(!this.preventDefault){
33156             return;
33157         }
33158         
33159         e.preventDefault();
33160         
33161         if (this.activeClass != '') {
33162             this.selectBrick();
33163         }
33164         
33165         this.fireEvent('click', this, e);
33166     },
33167     
33168     enter: function(e, el)
33169     {
33170         e.preventDefault();
33171         
33172         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33173             return;
33174         }
33175         
33176         if(this.bgimage.length && this.html.length){
33177             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33178         }
33179     },
33180     
33181     leave: function(e, el)
33182     {
33183         e.preventDefault();
33184         
33185         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33186             return;
33187         }
33188         
33189         if(this.bgimage.length && this.html.length){
33190             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33191         }
33192     },
33193     
33194     onTouchStart: function(e, el)
33195     {
33196 //        e.preventDefault();
33197         
33198         this.touchmoved = false;
33199         
33200         if(!this.isFitContainer){
33201             return;
33202         }
33203         
33204         if(!this.bgimage.length || !this.html.length){
33205             return;
33206         }
33207         
33208         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33209         
33210         this.timer = new Date().getTime();
33211         
33212     },
33213     
33214     onTouchMove: function(e, el)
33215     {
33216         this.touchmoved = true;
33217     },
33218     
33219     onContextMenu : function(e,el)
33220     {
33221         e.preventDefault();
33222         e.stopPropagation();
33223         return false;
33224     },
33225     
33226     onTouchEnd: function(e, el)
33227     {
33228 //        e.preventDefault();
33229         
33230         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33231         
33232             this.leave(e,el);
33233             
33234             return;
33235         }
33236         
33237         if(!this.bgimage.length || !this.html.length){
33238             
33239             if(this.href.length){
33240                 window.location.href = this.href;
33241             }
33242             
33243             return;
33244         }
33245         
33246         if(!this.isFitContainer){
33247             return;
33248         }
33249         
33250         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33251         
33252         window.location.href = this.href;
33253     },
33254     
33255     //selection on single brick only
33256     selectBrick : function() {
33257         
33258         if (!this.parentId) {
33259             return;
33260         }
33261         
33262         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33263         var index = m.selectedBrick.indexOf(this.id);
33264         
33265         if ( index > -1) {
33266             m.selectedBrick.splice(index,1);
33267             this.el.removeClass(this.activeClass);
33268             return;
33269         }
33270         
33271         for(var i = 0; i < m.selectedBrick.length; i++) {
33272             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33273             b.el.removeClass(b.activeClass);
33274         }
33275         
33276         m.selectedBrick = [];
33277         
33278         m.selectedBrick.push(this.id);
33279         this.el.addClass(this.activeClass);
33280         return;
33281     },
33282     
33283     isSelected : function(){
33284         return this.el.hasClass(this.activeClass);
33285         
33286     }
33287 });
33288
33289 Roo.apply(Roo.bootstrap.MasonryBrick, {
33290     
33291     //groups: {},
33292     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33293      /**
33294     * register a Masonry Brick
33295     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33296     */
33297     
33298     register : function(brick)
33299     {
33300         //this.groups[brick.id] = brick;
33301         this.groups.add(brick.id, brick);
33302     },
33303     /**
33304     * fetch a  masonry brick based on the masonry brick ID
33305     * @param {string} the masonry brick to add
33306     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33307     */
33308     
33309     get: function(brick_id) 
33310     {
33311         // if (typeof(this.groups[brick_id]) == 'undefined') {
33312         //     return false;
33313         // }
33314         // return this.groups[brick_id] ;
33315         
33316         if(this.groups.key(brick_id)) {
33317             return this.groups.key(brick_id);
33318         }
33319         
33320         return false;
33321     }
33322     
33323     
33324     
33325 });
33326
33327  /*
33328  * - LGPL
33329  *
33330  * element
33331  * 
33332  */
33333
33334 /**
33335  * @class Roo.bootstrap.Brick
33336  * @extends Roo.bootstrap.Component
33337  * Bootstrap Brick class
33338  * 
33339  * @constructor
33340  * Create a new Brick
33341  * @param {Object} config The config object
33342  */
33343
33344 Roo.bootstrap.Brick = function(config){
33345     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33346     
33347     this.addEvents({
33348         // raw events
33349         /**
33350          * @event click
33351          * When a Brick is click
33352          * @param {Roo.bootstrap.Brick} this
33353          * @param {Roo.EventObject} e
33354          */
33355         "click" : true
33356     });
33357 };
33358
33359 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33360     
33361     /**
33362      * @cfg {String} title
33363      */   
33364     title : '',
33365     /**
33366      * @cfg {String} html
33367      */   
33368     html : '',
33369     /**
33370      * @cfg {String} bgimage
33371      */   
33372     bgimage : '',
33373     /**
33374      * @cfg {String} cls
33375      */   
33376     cls : '',
33377     /**
33378      * @cfg {String} href
33379      */   
33380     href : '',
33381     /**
33382      * @cfg {String} video
33383      */   
33384     video : '',
33385     /**
33386      * @cfg {Boolean} square
33387      */   
33388     square : true,
33389     
33390     getAutoCreate : function()
33391     {
33392         var cls = 'roo-brick';
33393         
33394         if(this.href.length){
33395             cls += ' roo-brick-link';
33396         }
33397         
33398         if(this.bgimage.length){
33399             cls += ' roo-brick-image';
33400         }
33401         
33402         if(!this.html.length && !this.bgimage.length){
33403             cls += ' roo-brick-center-title';
33404         }
33405         
33406         if(!this.html.length && this.bgimage.length){
33407             cls += ' roo-brick-bottom-title';
33408         }
33409         
33410         if(this.cls){
33411             cls += ' ' + this.cls;
33412         }
33413         
33414         var cfg = {
33415             tag: (this.href.length) ? 'a' : 'div',
33416             cls: cls,
33417             cn: [
33418                 {
33419                     tag: 'div',
33420                     cls: 'roo-brick-paragraph',
33421                     cn: []
33422                 }
33423             ]
33424         };
33425         
33426         if(this.href.length){
33427             cfg.href = this.href;
33428         }
33429         
33430         var cn = cfg.cn[0].cn;
33431         
33432         if(this.title.length){
33433             cn.push({
33434                 tag: 'h4',
33435                 cls: 'roo-brick-title',
33436                 html: this.title
33437             });
33438         }
33439         
33440         if(this.html.length){
33441             cn.push({
33442                 tag: 'p',
33443                 cls: 'roo-brick-text',
33444                 html: this.html
33445             });
33446         } else {
33447             cn.cls += ' hide';
33448         }
33449         
33450         if(this.bgimage.length){
33451             cfg.cn.push({
33452                 tag: 'img',
33453                 cls: 'roo-brick-image-view',
33454                 src: this.bgimage
33455             });
33456         }
33457         
33458         return cfg;
33459     },
33460     
33461     initEvents: function() 
33462     {
33463         if(this.title.length || this.html.length){
33464             this.el.on('mouseenter'  ,this.enter, this);
33465             this.el.on('mouseleave', this.leave, this);
33466         }
33467         
33468         Roo.EventManager.onWindowResize(this.resize, this); 
33469         
33470         if(this.bgimage.length){
33471             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33472             this.imageEl.on('load', this.onImageLoad, this);
33473             return;
33474         }
33475         
33476         this.resize();
33477     },
33478     
33479     onImageLoad : function()
33480     {
33481         this.resize();
33482     },
33483     
33484     resize : function()
33485     {
33486         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33487         
33488         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33489         
33490         if(this.bgimage.length){
33491             var image = this.el.select('.roo-brick-image-view', true).first();
33492             
33493             image.setWidth(paragraph.getWidth());
33494             
33495             if(this.square){
33496                 image.setHeight(paragraph.getWidth());
33497             }
33498             
33499             this.el.setHeight(image.getHeight());
33500             paragraph.setHeight(image.getHeight());
33501             
33502         }
33503         
33504     },
33505     
33506     enter: function(e, el)
33507     {
33508         e.preventDefault();
33509         
33510         if(this.bgimage.length){
33511             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33512             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33513         }
33514     },
33515     
33516     leave: function(e, el)
33517     {
33518         e.preventDefault();
33519         
33520         if(this.bgimage.length){
33521             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33522             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33523         }
33524     }
33525     
33526 });
33527
33528  
33529
33530  /*
33531  * - LGPL
33532  *
33533  * Number field 
33534  */
33535
33536 /**
33537  * @class Roo.bootstrap.NumberField
33538  * @extends Roo.bootstrap.Input
33539  * Bootstrap NumberField class
33540  * 
33541  * 
33542  * 
33543  * 
33544  * @constructor
33545  * Create a new NumberField
33546  * @param {Object} config The config object
33547  */
33548
33549 Roo.bootstrap.NumberField = function(config){
33550     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33551 };
33552
33553 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33554     
33555     /**
33556      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33557      */
33558     allowDecimals : true,
33559     /**
33560      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33561      */
33562     decimalSeparator : ".",
33563     /**
33564      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33565      */
33566     decimalPrecision : 2,
33567     /**
33568      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33569      */
33570     allowNegative : true,
33571     
33572     /**
33573      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33574      */
33575     allowZero: true,
33576     /**
33577      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33578      */
33579     minValue : Number.NEGATIVE_INFINITY,
33580     /**
33581      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33582      */
33583     maxValue : Number.MAX_VALUE,
33584     /**
33585      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33586      */
33587     minText : "The minimum value for this field is {0}",
33588     /**
33589      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33590      */
33591     maxText : "The maximum value for this field is {0}",
33592     /**
33593      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33594      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33595      */
33596     nanText : "{0} is not a valid number",
33597     /**
33598      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33599      */
33600     thousandsDelimiter : false,
33601     /**
33602      * @cfg {String} valueAlign alignment of value
33603      */
33604     valueAlign : "left",
33605
33606     getAutoCreate : function()
33607     {
33608         var hiddenInput = {
33609             tag: 'input',
33610             type: 'hidden',
33611             id: Roo.id(),
33612             cls: 'hidden-number-input'
33613         };
33614         
33615         if (this.name) {
33616             hiddenInput.name = this.name;
33617         }
33618         
33619         this.name = '';
33620         
33621         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33622         
33623         this.name = hiddenInput.name;
33624         
33625         if(cfg.cn.length > 0) {
33626             cfg.cn.push(hiddenInput);
33627         }
33628         
33629         return cfg;
33630     },
33631
33632     // private
33633     initEvents : function()
33634     {   
33635         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33636         
33637         var allowed = "0123456789";
33638         
33639         if(this.allowDecimals){
33640             allowed += this.decimalSeparator;
33641         }
33642         
33643         if(this.allowNegative){
33644             allowed += "-";
33645         }
33646         
33647         if(this.thousandsDelimiter) {
33648             allowed += ",";
33649         }
33650         
33651         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33652         
33653         var keyPress = function(e){
33654             
33655             var k = e.getKey();
33656             
33657             var c = e.getCharCode();
33658             
33659             if(
33660                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33661                     allowed.indexOf(String.fromCharCode(c)) === -1
33662             ){
33663                 e.stopEvent();
33664                 return;
33665             }
33666             
33667             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33668                 return;
33669             }
33670             
33671             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33672                 e.stopEvent();
33673             }
33674         };
33675         
33676         this.el.on("keypress", keyPress, this);
33677     },
33678     
33679     validateValue : function(value)
33680     {
33681         
33682         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33683             return false;
33684         }
33685         
33686         var num = this.parseValue(value);
33687         
33688         if(isNaN(num)){
33689             this.markInvalid(String.format(this.nanText, value));
33690             return false;
33691         }
33692         
33693         if(num < this.minValue){
33694             this.markInvalid(String.format(this.minText, this.minValue));
33695             return false;
33696         }
33697         
33698         if(num > this.maxValue){
33699             this.markInvalid(String.format(this.maxText, this.maxValue));
33700             return false;
33701         }
33702         
33703         return true;
33704     },
33705
33706     getValue : function()
33707     {
33708         var v = this.hiddenEl().getValue();
33709         
33710         return this.fixPrecision(this.parseValue(v));
33711     },
33712
33713     parseValue : function(value)
33714     {
33715         if(this.thousandsDelimiter) {
33716             value += "";
33717             r = new RegExp(",", "g");
33718             value = value.replace(r, "");
33719         }
33720         
33721         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33722         return isNaN(value) ? '' : value;
33723     },
33724
33725     fixPrecision : function(value)
33726     {
33727         if(this.thousandsDelimiter) {
33728             value += "";
33729             r = new RegExp(",", "g");
33730             value = value.replace(r, "");
33731         }
33732         
33733         var nan = isNaN(value);
33734         
33735         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33736             return nan ? '' : value;
33737         }
33738         return parseFloat(value).toFixed(this.decimalPrecision);
33739     },
33740
33741     setValue : function(v)
33742     {
33743         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33744         
33745         this.value = v;
33746         
33747         if(this.rendered){
33748             
33749             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33750             
33751             this.inputEl().dom.value = (v == '') ? '' :
33752                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33753             
33754             if(!this.allowZero && v === '0') {
33755                 this.hiddenEl().dom.value = '';
33756                 this.inputEl().dom.value = '';
33757             }
33758             
33759             this.validate();
33760         }
33761     },
33762
33763     decimalPrecisionFcn : function(v)
33764     {
33765         return Math.floor(v);
33766     },
33767
33768     beforeBlur : function()
33769     {
33770         var v = this.parseValue(this.getRawValue());
33771         
33772         if(v || v === 0 || v === ''){
33773             this.setValue(v);
33774         }
33775     },
33776     
33777     hiddenEl : function()
33778     {
33779         return this.el.select('input.hidden-number-input',true).first();
33780     }
33781     
33782 });
33783
33784  
33785
33786 /*
33787 * Licence: LGPL
33788 */
33789
33790 /**
33791  * @class Roo.bootstrap.DocumentSlider
33792  * @extends Roo.bootstrap.Component
33793  * Bootstrap DocumentSlider class
33794  * 
33795  * @constructor
33796  * Create a new DocumentViewer
33797  * @param {Object} config The config object
33798  */
33799
33800 Roo.bootstrap.DocumentSlider = function(config){
33801     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33802     
33803     this.files = [];
33804     
33805     this.addEvents({
33806         /**
33807          * @event initial
33808          * Fire after initEvent
33809          * @param {Roo.bootstrap.DocumentSlider} this
33810          */
33811         "initial" : true,
33812         /**
33813          * @event update
33814          * Fire after update
33815          * @param {Roo.bootstrap.DocumentSlider} this
33816          */
33817         "update" : true,
33818         /**
33819          * @event click
33820          * Fire after click
33821          * @param {Roo.bootstrap.DocumentSlider} this
33822          */
33823         "click" : true
33824     });
33825 };
33826
33827 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33828     
33829     files : false,
33830     
33831     indicator : 0,
33832     
33833     getAutoCreate : function()
33834     {
33835         var cfg = {
33836             tag : 'div',
33837             cls : 'roo-document-slider',
33838             cn : [
33839                 {
33840                     tag : 'div',
33841                     cls : 'roo-document-slider-header',
33842                     cn : [
33843                         {
33844                             tag : 'div',
33845                             cls : 'roo-document-slider-header-title'
33846                         }
33847                     ]
33848                 },
33849                 {
33850                     tag : 'div',
33851                     cls : 'roo-document-slider-body',
33852                     cn : [
33853                         {
33854                             tag : 'div',
33855                             cls : 'roo-document-slider-prev',
33856                             cn : [
33857                                 {
33858                                     tag : 'i',
33859                                     cls : 'fa fa-chevron-left'
33860                                 }
33861                             ]
33862                         },
33863                         {
33864                             tag : 'div',
33865                             cls : 'roo-document-slider-thumb',
33866                             cn : [
33867                                 {
33868                                     tag : 'img',
33869                                     cls : 'roo-document-slider-image'
33870                                 }
33871                             ]
33872                         },
33873                         {
33874                             tag : 'div',
33875                             cls : 'roo-document-slider-next',
33876                             cn : [
33877                                 {
33878                                     tag : 'i',
33879                                     cls : 'fa fa-chevron-right'
33880                                 }
33881                             ]
33882                         }
33883                     ]
33884                 }
33885             ]
33886         };
33887         
33888         return cfg;
33889     },
33890     
33891     initEvents : function()
33892     {
33893         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33894         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33895         
33896         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33897         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33898         
33899         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33900         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33901         
33902         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33903         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33904         
33905         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33906         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33907         
33908         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33909         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33910         
33911         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33912         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33913         
33914         this.thumbEl.on('click', this.onClick, this);
33915         
33916         this.prevIndicator.on('click', this.prev, this);
33917         
33918         this.nextIndicator.on('click', this.next, this);
33919         
33920     },
33921     
33922     initial : function()
33923     {
33924         if(this.files.length){
33925             this.indicator = 1;
33926             this.update()
33927         }
33928         
33929         this.fireEvent('initial', this);
33930     },
33931     
33932     update : function()
33933     {
33934         this.imageEl.attr('src', this.files[this.indicator - 1]);
33935         
33936         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33937         
33938         this.prevIndicator.show();
33939         
33940         if(this.indicator == 1){
33941             this.prevIndicator.hide();
33942         }
33943         
33944         this.nextIndicator.show();
33945         
33946         if(this.indicator == this.files.length){
33947             this.nextIndicator.hide();
33948         }
33949         
33950         this.thumbEl.scrollTo('top');
33951         
33952         this.fireEvent('update', this);
33953     },
33954     
33955     onClick : function(e)
33956     {
33957         e.preventDefault();
33958         
33959         this.fireEvent('click', this);
33960     },
33961     
33962     prev : function(e)
33963     {
33964         e.preventDefault();
33965         
33966         this.indicator = Math.max(1, this.indicator - 1);
33967         
33968         this.update();
33969     },
33970     
33971     next : function(e)
33972     {
33973         e.preventDefault();
33974         
33975         this.indicator = Math.min(this.files.length, this.indicator + 1);
33976         
33977         this.update();
33978     }
33979 });
33980 /*
33981  * - LGPL
33982  *
33983  * RadioSet
33984  *
33985  *
33986  */
33987
33988 /**
33989  * @class Roo.bootstrap.RadioSet
33990  * @extends Roo.bootstrap.Input
33991  * Bootstrap RadioSet class
33992  * @cfg {String} indicatorpos (left|right) default left
33993  * @cfg {Boolean} inline (true|false) inline the element (default true)
33994  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33995  * @constructor
33996  * Create a new RadioSet
33997  * @param {Object} config The config object
33998  */
33999
34000 Roo.bootstrap.RadioSet = function(config){
34001     
34002     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34003     
34004     this.radioes = [];
34005     
34006     Roo.bootstrap.RadioSet.register(this);
34007     
34008     this.addEvents({
34009         /**
34010         * @event check
34011         * Fires when the element is checked or unchecked.
34012         * @param {Roo.bootstrap.RadioSet} this This radio
34013         * @param {Roo.bootstrap.Radio} item The checked item
34014         */
34015        check : true,
34016        /**
34017         * @event click
34018         * Fires when the element is click.
34019         * @param {Roo.bootstrap.RadioSet} this This radio set
34020         * @param {Roo.bootstrap.Radio} item The checked item
34021         * @param {Roo.EventObject} e The event object
34022         */
34023        click : true
34024     });
34025     
34026 };
34027
34028 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34029
34030     radioes : false,
34031     
34032     inline : true,
34033     
34034     weight : '',
34035     
34036     indicatorpos : 'left',
34037     
34038     getAutoCreate : function()
34039     {
34040         var label = {
34041             tag : 'label',
34042             cls : 'roo-radio-set-label',
34043             cn : [
34044                 {
34045                     tag : 'span',
34046                     html : this.fieldLabel
34047                 }
34048             ]
34049         };
34050         
34051         if(this.indicatorpos == 'left'){
34052             label.cn.unshift({
34053                 tag : 'i',
34054                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34055                 tooltip : 'This field is required'
34056             });
34057         } else {
34058             label.cn.push({
34059                 tag : 'i',
34060                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34061                 tooltip : 'This field is required'
34062             });
34063         }
34064         
34065         var items = {
34066             tag : 'div',
34067             cls : 'roo-radio-set-items'
34068         };
34069         
34070         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34071         
34072         if (align === 'left' && this.fieldLabel.length) {
34073             
34074             items = {
34075                 cls : "roo-radio-set-right", 
34076                 cn: [
34077                     items
34078                 ]
34079             };
34080             
34081             if(this.labelWidth > 12){
34082                 label.style = "width: " + this.labelWidth + 'px';
34083             }
34084             
34085             if(this.labelWidth < 13 && this.labelmd == 0){
34086                 this.labelmd = this.labelWidth;
34087             }
34088             
34089             if(this.labellg > 0){
34090                 label.cls += ' col-lg-' + this.labellg;
34091                 items.cls += ' col-lg-' + (12 - this.labellg);
34092             }
34093             
34094             if(this.labelmd > 0){
34095                 label.cls += ' col-md-' + this.labelmd;
34096                 items.cls += ' col-md-' + (12 - this.labelmd);
34097             }
34098             
34099             if(this.labelsm > 0){
34100                 label.cls += ' col-sm-' + this.labelsm;
34101                 items.cls += ' col-sm-' + (12 - this.labelsm);
34102             }
34103             
34104             if(this.labelxs > 0){
34105                 label.cls += ' col-xs-' + this.labelxs;
34106                 items.cls += ' col-xs-' + (12 - this.labelxs);
34107             }
34108         }
34109         
34110         var cfg = {
34111             tag : 'div',
34112             cls : 'roo-radio-set',
34113             cn : [
34114                 {
34115                     tag : 'input',
34116                     cls : 'roo-radio-set-input',
34117                     type : 'hidden',
34118                     name : this.name,
34119                     value : this.value ? this.value :  ''
34120                 },
34121                 label,
34122                 items
34123             ]
34124         };
34125         
34126         if(this.weight.length){
34127             cfg.cls += ' roo-radio-' + this.weight;
34128         }
34129         
34130         if(this.inline) {
34131             cfg.cls += ' roo-radio-set-inline';
34132         }
34133         
34134         var settings=this;
34135         ['xs','sm','md','lg'].map(function(size){
34136             if (settings[size]) {
34137                 cfg.cls += ' col-' + size + '-' + settings[size];
34138             }
34139         });
34140         
34141         return cfg;
34142         
34143     },
34144
34145     initEvents : function()
34146     {
34147         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34148         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34149         
34150         if(!this.fieldLabel.length){
34151             this.labelEl.hide();
34152         }
34153         
34154         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34155         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34156         
34157         this.indicator = this.indicatorEl();
34158         
34159         if(this.indicator){
34160             this.indicator.addClass('invisible');
34161         }
34162         
34163         this.originalValue = this.getValue();
34164         
34165     },
34166     
34167     inputEl: function ()
34168     {
34169         return this.el.select('.roo-radio-set-input', true).first();
34170     },
34171     
34172     getChildContainer : function()
34173     {
34174         return this.itemsEl;
34175     },
34176     
34177     register : function(item)
34178     {
34179         this.radioes.push(item);
34180         
34181     },
34182     
34183     validate : function()
34184     {   
34185         if(this.getVisibilityEl().hasClass('hidden')){
34186             return true;
34187         }
34188         
34189         var valid = false;
34190         
34191         Roo.each(this.radioes, function(i){
34192             if(!i.checked){
34193                 return;
34194             }
34195             
34196             valid = true;
34197             return false;
34198         });
34199         
34200         if(this.allowBlank) {
34201             return true;
34202         }
34203         
34204         if(this.disabled || valid){
34205             this.markValid();
34206             return true;
34207         }
34208         
34209         this.markInvalid();
34210         return false;
34211         
34212     },
34213     
34214     markValid : function()
34215     {
34216         if(this.labelEl.isVisible(true)){
34217             this.indicatorEl().removeClass('visible');
34218             this.indicatorEl().addClass('invisible');
34219         }
34220         
34221         this.el.removeClass([this.invalidClass, this.validClass]);
34222         this.el.addClass(this.validClass);
34223         
34224         this.fireEvent('valid', this);
34225     },
34226     
34227     markInvalid : function(msg)
34228     {
34229         if(this.allowBlank || this.disabled){
34230             return;
34231         }
34232         
34233         if(this.labelEl.isVisible(true)){
34234             this.indicatorEl().removeClass('invisible');
34235             this.indicatorEl().addClass('visible');
34236         }
34237         
34238         this.el.removeClass([this.invalidClass, this.validClass]);
34239         this.el.addClass(this.invalidClass);
34240         
34241         this.fireEvent('invalid', this, msg);
34242         
34243     },
34244     
34245     setValue : function(v, suppressEvent)
34246     {   
34247         if(this.value === v){
34248             return;
34249         }
34250         
34251         this.value = v;
34252         
34253         if(this.rendered){
34254             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34255         }
34256         
34257         Roo.each(this.radioes, function(i){
34258             i.checked = false;
34259             i.el.removeClass('checked');
34260         });
34261         
34262         Roo.each(this.radioes, function(i){
34263             
34264             if(i.value === v || i.value.toString() === v.toString()){
34265                 i.checked = true;
34266                 i.el.addClass('checked');
34267                 
34268                 if(suppressEvent !== true){
34269                     this.fireEvent('check', this, i);
34270                 }
34271                 
34272                 return false;
34273             }
34274             
34275         }, this);
34276         
34277         this.validate();
34278     },
34279     
34280     clearInvalid : function(){
34281         
34282         if(!this.el || this.preventMark){
34283             return;
34284         }
34285         
34286         this.el.removeClass([this.invalidClass]);
34287         
34288         this.fireEvent('valid', this);
34289     }
34290     
34291 });
34292
34293 Roo.apply(Roo.bootstrap.RadioSet, {
34294     
34295     groups: {},
34296     
34297     register : function(set)
34298     {
34299         this.groups[set.name] = set;
34300     },
34301     
34302     get: function(name) 
34303     {
34304         if (typeof(this.groups[name]) == 'undefined') {
34305             return false;
34306         }
34307         
34308         return this.groups[name] ;
34309     }
34310     
34311 });
34312 /*
34313  * Based on:
34314  * Ext JS Library 1.1.1
34315  * Copyright(c) 2006-2007, Ext JS, LLC.
34316  *
34317  * Originally Released Under LGPL - original licence link has changed is not relivant.
34318  *
34319  * Fork - LGPL
34320  * <script type="text/javascript">
34321  */
34322
34323
34324 /**
34325  * @class Roo.bootstrap.SplitBar
34326  * @extends Roo.util.Observable
34327  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34328  * <br><br>
34329  * Usage:
34330  * <pre><code>
34331 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34332                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34333 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34334 split.minSize = 100;
34335 split.maxSize = 600;
34336 split.animate = true;
34337 split.on('moved', splitterMoved);
34338 </code></pre>
34339  * @constructor
34340  * Create a new SplitBar
34341  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34342  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34343  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34344  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34345                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34346                         position of the SplitBar).
34347  */
34348 Roo.bootstrap.SplitBar = function(cfg){
34349     
34350     /** @private */
34351     
34352     //{
34353     //  dragElement : elm
34354     //  resizingElement: el,
34355         // optional..
34356     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34357     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34358         // existingProxy ???
34359     //}
34360     
34361     this.el = Roo.get(cfg.dragElement, true);
34362     this.el.dom.unselectable = "on";
34363     /** @private */
34364     this.resizingEl = Roo.get(cfg.resizingElement, true);
34365
34366     /**
34367      * @private
34368      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34369      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34370      * @type Number
34371      */
34372     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34373     
34374     /**
34375      * The minimum size of the resizing element. (Defaults to 0)
34376      * @type Number
34377      */
34378     this.minSize = 0;
34379     
34380     /**
34381      * The maximum size of the resizing element. (Defaults to 2000)
34382      * @type Number
34383      */
34384     this.maxSize = 2000;
34385     
34386     /**
34387      * Whether to animate the transition to the new size
34388      * @type Boolean
34389      */
34390     this.animate = false;
34391     
34392     /**
34393      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34394      * @type Boolean
34395      */
34396     this.useShim = false;
34397     
34398     /** @private */
34399     this.shim = null;
34400     
34401     if(!cfg.existingProxy){
34402         /** @private */
34403         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34404     }else{
34405         this.proxy = Roo.get(cfg.existingProxy).dom;
34406     }
34407     /** @private */
34408     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34409     
34410     /** @private */
34411     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34412     
34413     /** @private */
34414     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34415     
34416     /** @private */
34417     this.dragSpecs = {};
34418     
34419     /**
34420      * @private The adapter to use to positon and resize elements
34421      */
34422     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34423     this.adapter.init(this);
34424     
34425     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34426         /** @private */
34427         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34428         this.el.addClass("roo-splitbar-h");
34429     }else{
34430         /** @private */
34431         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34432         this.el.addClass("roo-splitbar-v");
34433     }
34434     
34435     this.addEvents({
34436         /**
34437          * @event resize
34438          * Fires when the splitter is moved (alias for {@link #event-moved})
34439          * @param {Roo.bootstrap.SplitBar} this
34440          * @param {Number} newSize the new width or height
34441          */
34442         "resize" : true,
34443         /**
34444          * @event moved
34445          * Fires when the splitter is moved
34446          * @param {Roo.bootstrap.SplitBar} this
34447          * @param {Number} newSize the new width or height
34448          */
34449         "moved" : true,
34450         /**
34451          * @event beforeresize
34452          * Fires before the splitter is dragged
34453          * @param {Roo.bootstrap.SplitBar} this
34454          */
34455         "beforeresize" : true,
34456
34457         "beforeapply" : true
34458     });
34459
34460     Roo.util.Observable.call(this);
34461 };
34462
34463 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34464     onStartProxyDrag : function(x, y){
34465         this.fireEvent("beforeresize", this);
34466         if(!this.overlay){
34467             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34468             o.unselectable();
34469             o.enableDisplayMode("block");
34470             // all splitbars share the same overlay
34471             Roo.bootstrap.SplitBar.prototype.overlay = o;
34472         }
34473         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34474         this.overlay.show();
34475         Roo.get(this.proxy).setDisplayed("block");
34476         var size = this.adapter.getElementSize(this);
34477         this.activeMinSize = this.getMinimumSize();;
34478         this.activeMaxSize = this.getMaximumSize();;
34479         var c1 = size - this.activeMinSize;
34480         var c2 = Math.max(this.activeMaxSize - size, 0);
34481         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34482             this.dd.resetConstraints();
34483             this.dd.setXConstraint(
34484                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34485                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34486             );
34487             this.dd.setYConstraint(0, 0);
34488         }else{
34489             this.dd.resetConstraints();
34490             this.dd.setXConstraint(0, 0);
34491             this.dd.setYConstraint(
34492                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34493                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34494             );
34495          }
34496         this.dragSpecs.startSize = size;
34497         this.dragSpecs.startPoint = [x, y];
34498         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34499     },
34500     
34501     /** 
34502      * @private Called after the drag operation by the DDProxy
34503      */
34504     onEndProxyDrag : function(e){
34505         Roo.get(this.proxy).setDisplayed(false);
34506         var endPoint = Roo.lib.Event.getXY(e);
34507         if(this.overlay){
34508             this.overlay.hide();
34509         }
34510         var newSize;
34511         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34512             newSize = this.dragSpecs.startSize + 
34513                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34514                     endPoint[0] - this.dragSpecs.startPoint[0] :
34515                     this.dragSpecs.startPoint[0] - endPoint[0]
34516                 );
34517         }else{
34518             newSize = this.dragSpecs.startSize + 
34519                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34520                     endPoint[1] - this.dragSpecs.startPoint[1] :
34521                     this.dragSpecs.startPoint[1] - endPoint[1]
34522                 );
34523         }
34524         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34525         if(newSize != this.dragSpecs.startSize){
34526             if(this.fireEvent('beforeapply', this, newSize) !== false){
34527                 this.adapter.setElementSize(this, newSize);
34528                 this.fireEvent("moved", this, newSize);
34529                 this.fireEvent("resize", this, newSize);
34530             }
34531         }
34532     },
34533     
34534     /**
34535      * Get the adapter this SplitBar uses
34536      * @return The adapter object
34537      */
34538     getAdapter : function(){
34539         return this.adapter;
34540     },
34541     
34542     /**
34543      * Set the adapter this SplitBar uses
34544      * @param {Object} adapter A SplitBar adapter object
34545      */
34546     setAdapter : function(adapter){
34547         this.adapter = adapter;
34548         this.adapter.init(this);
34549     },
34550     
34551     /**
34552      * Gets the minimum size for the resizing element
34553      * @return {Number} The minimum size
34554      */
34555     getMinimumSize : function(){
34556         return this.minSize;
34557     },
34558     
34559     /**
34560      * Sets the minimum size for the resizing element
34561      * @param {Number} minSize The minimum size
34562      */
34563     setMinimumSize : function(minSize){
34564         this.minSize = minSize;
34565     },
34566     
34567     /**
34568      * Gets the maximum size for the resizing element
34569      * @return {Number} The maximum size
34570      */
34571     getMaximumSize : function(){
34572         return this.maxSize;
34573     },
34574     
34575     /**
34576      * Sets the maximum size for the resizing element
34577      * @param {Number} maxSize The maximum size
34578      */
34579     setMaximumSize : function(maxSize){
34580         this.maxSize = maxSize;
34581     },
34582     
34583     /**
34584      * Sets the initialize size for the resizing element
34585      * @param {Number} size The initial size
34586      */
34587     setCurrentSize : function(size){
34588         var oldAnimate = this.animate;
34589         this.animate = false;
34590         this.adapter.setElementSize(this, size);
34591         this.animate = oldAnimate;
34592     },
34593     
34594     /**
34595      * Destroy this splitbar. 
34596      * @param {Boolean} removeEl True to remove the element
34597      */
34598     destroy : function(removeEl){
34599         if(this.shim){
34600             this.shim.remove();
34601         }
34602         this.dd.unreg();
34603         this.proxy.parentNode.removeChild(this.proxy);
34604         if(removeEl){
34605             this.el.remove();
34606         }
34607     }
34608 });
34609
34610 /**
34611  * @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.
34612  */
34613 Roo.bootstrap.SplitBar.createProxy = function(dir){
34614     var proxy = new Roo.Element(document.createElement("div"));
34615     proxy.unselectable();
34616     var cls = 'roo-splitbar-proxy';
34617     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34618     document.body.appendChild(proxy.dom);
34619     return proxy.dom;
34620 };
34621
34622 /** 
34623  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34624  * Default Adapter. It assumes the splitter and resizing element are not positioned
34625  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34626  */
34627 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34628 };
34629
34630 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34631     // do nothing for now
34632     init : function(s){
34633     
34634     },
34635     /**
34636      * Called before drag operations to get the current size of the resizing element. 
34637      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34638      */
34639      getElementSize : function(s){
34640         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34641             return s.resizingEl.getWidth();
34642         }else{
34643             return s.resizingEl.getHeight();
34644         }
34645     },
34646     
34647     /**
34648      * Called after drag operations to set the size of the resizing element.
34649      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34650      * @param {Number} newSize The new size to set
34651      * @param {Function} onComplete A function to be invoked when resizing is complete
34652      */
34653     setElementSize : function(s, newSize, onComplete){
34654         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34655             if(!s.animate){
34656                 s.resizingEl.setWidth(newSize);
34657                 if(onComplete){
34658                     onComplete(s, newSize);
34659                 }
34660             }else{
34661                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34662             }
34663         }else{
34664             
34665             if(!s.animate){
34666                 s.resizingEl.setHeight(newSize);
34667                 if(onComplete){
34668                     onComplete(s, newSize);
34669                 }
34670             }else{
34671                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34672             }
34673         }
34674     }
34675 };
34676
34677 /** 
34678  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34679  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34680  * Adapter that  moves the splitter element to align with the resized sizing element. 
34681  * Used with an absolute positioned SplitBar.
34682  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34683  * document.body, make sure you assign an id to the body element.
34684  */
34685 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34686     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34687     this.container = Roo.get(container);
34688 };
34689
34690 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34691     init : function(s){
34692         this.basic.init(s);
34693     },
34694     
34695     getElementSize : function(s){
34696         return this.basic.getElementSize(s);
34697     },
34698     
34699     setElementSize : function(s, newSize, onComplete){
34700         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34701     },
34702     
34703     moveSplitter : function(s){
34704         var yes = Roo.bootstrap.SplitBar;
34705         switch(s.placement){
34706             case yes.LEFT:
34707                 s.el.setX(s.resizingEl.getRight());
34708                 break;
34709             case yes.RIGHT:
34710                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34711                 break;
34712             case yes.TOP:
34713                 s.el.setY(s.resizingEl.getBottom());
34714                 break;
34715             case yes.BOTTOM:
34716                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34717                 break;
34718         }
34719     }
34720 };
34721
34722 /**
34723  * Orientation constant - Create a vertical SplitBar
34724  * @static
34725  * @type Number
34726  */
34727 Roo.bootstrap.SplitBar.VERTICAL = 1;
34728
34729 /**
34730  * Orientation constant - Create a horizontal SplitBar
34731  * @static
34732  * @type Number
34733  */
34734 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34735
34736 /**
34737  * Placement constant - The resizing element is to the left of the splitter element
34738  * @static
34739  * @type Number
34740  */
34741 Roo.bootstrap.SplitBar.LEFT = 1;
34742
34743 /**
34744  * Placement constant - The resizing element is to the right of the splitter element
34745  * @static
34746  * @type Number
34747  */
34748 Roo.bootstrap.SplitBar.RIGHT = 2;
34749
34750 /**
34751  * Placement constant - The resizing element is positioned above the splitter element
34752  * @static
34753  * @type Number
34754  */
34755 Roo.bootstrap.SplitBar.TOP = 3;
34756
34757 /**
34758  * Placement constant - The resizing element is positioned under splitter element
34759  * @static
34760  * @type Number
34761  */
34762 Roo.bootstrap.SplitBar.BOTTOM = 4;
34763 Roo.namespace("Roo.bootstrap.layout");/*
34764  * Based on:
34765  * Ext JS Library 1.1.1
34766  * Copyright(c) 2006-2007, Ext JS, LLC.
34767  *
34768  * Originally Released Under LGPL - original licence link has changed is not relivant.
34769  *
34770  * Fork - LGPL
34771  * <script type="text/javascript">
34772  */
34773
34774 /**
34775  * @class Roo.bootstrap.layout.Manager
34776  * @extends Roo.bootstrap.Component
34777  * Base class for layout managers.
34778  */
34779 Roo.bootstrap.layout.Manager = function(config)
34780 {
34781     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34782
34783
34784
34785
34786
34787     /** false to disable window resize monitoring @type Boolean */
34788     this.monitorWindowResize = true;
34789     this.regions = {};
34790     this.addEvents({
34791         /**
34792          * @event layout
34793          * Fires when a layout is performed.
34794          * @param {Roo.LayoutManager} this
34795          */
34796         "layout" : true,
34797         /**
34798          * @event regionresized
34799          * Fires when the user resizes a region.
34800          * @param {Roo.LayoutRegion} region The resized region
34801          * @param {Number} newSize The new size (width for east/west, height for north/south)
34802          */
34803         "regionresized" : true,
34804         /**
34805          * @event regioncollapsed
34806          * Fires when a region is collapsed.
34807          * @param {Roo.LayoutRegion} region The collapsed region
34808          */
34809         "regioncollapsed" : true,
34810         /**
34811          * @event regionexpanded
34812          * Fires when a region is expanded.
34813          * @param {Roo.LayoutRegion} region The expanded region
34814          */
34815         "regionexpanded" : true
34816     });
34817     this.updating = false;
34818
34819     if (config.el) {
34820         this.el = Roo.get(config.el);
34821         this.initEvents();
34822     }
34823
34824 };
34825
34826 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34827
34828
34829     regions : null,
34830
34831     monitorWindowResize : true,
34832
34833
34834     updating : false,
34835
34836
34837     onRender : function(ct, position)
34838     {
34839         if(!this.el){
34840             this.el = Roo.get(ct);
34841             this.initEvents();
34842         }
34843         //this.fireEvent('render',this);
34844     },
34845
34846
34847     initEvents: function()
34848     {
34849
34850
34851         // ie scrollbar fix
34852         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34853             document.body.scroll = "no";
34854         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34855             this.el.position('relative');
34856         }
34857         this.id = this.el.id;
34858         this.el.addClass("roo-layout-container");
34859         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34860         if(this.el.dom != document.body ) {
34861             this.el.on('resize', this.layout,this);
34862             this.el.on('show', this.layout,this);
34863         }
34864
34865     },
34866
34867     /**
34868      * Returns true if this layout is currently being updated
34869      * @return {Boolean}
34870      */
34871     isUpdating : function(){
34872         return this.updating;
34873     },
34874
34875     /**
34876      * Suspend the LayoutManager from doing auto-layouts while
34877      * making multiple add or remove calls
34878      */
34879     beginUpdate : function(){
34880         this.updating = true;
34881     },
34882
34883     /**
34884      * Restore auto-layouts and optionally disable the manager from performing a layout
34885      * @param {Boolean} noLayout true to disable a layout update
34886      */
34887     endUpdate : function(noLayout){
34888         this.updating = false;
34889         if(!noLayout){
34890             this.layout();
34891         }
34892     },
34893
34894     layout: function(){
34895         // abstract...
34896     },
34897
34898     onRegionResized : function(region, newSize){
34899         this.fireEvent("regionresized", region, newSize);
34900         this.layout();
34901     },
34902
34903     onRegionCollapsed : function(region){
34904         this.fireEvent("regioncollapsed", region);
34905     },
34906
34907     onRegionExpanded : function(region){
34908         this.fireEvent("regionexpanded", region);
34909     },
34910
34911     /**
34912      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34913      * performs box-model adjustments.
34914      * @return {Object} The size as an object {width: (the width), height: (the height)}
34915      */
34916     getViewSize : function()
34917     {
34918         var size;
34919         if(this.el.dom != document.body){
34920             size = this.el.getSize();
34921         }else{
34922             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34923         }
34924         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34925         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34926         return size;
34927     },
34928
34929     /**
34930      * Returns the Element this layout is bound to.
34931      * @return {Roo.Element}
34932      */
34933     getEl : function(){
34934         return this.el;
34935     },
34936
34937     /**
34938      * Returns the specified region.
34939      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34940      * @return {Roo.LayoutRegion}
34941      */
34942     getRegion : function(target){
34943         return this.regions[target.toLowerCase()];
34944     },
34945
34946     onWindowResize : function(){
34947         if(this.monitorWindowResize){
34948             this.layout();
34949         }
34950     }
34951 });
34952 /*
34953  * Based on:
34954  * Ext JS Library 1.1.1
34955  * Copyright(c) 2006-2007, Ext JS, LLC.
34956  *
34957  * Originally Released Under LGPL - original licence link has changed is not relivant.
34958  *
34959  * Fork - LGPL
34960  * <script type="text/javascript">
34961  */
34962 /**
34963  * @class Roo.bootstrap.layout.Border
34964  * @extends Roo.bootstrap.layout.Manager
34965  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34966  * please see: examples/bootstrap/nested.html<br><br>
34967  
34968 <b>The container the layout is rendered into can be either the body element or any other element.
34969 If it is not the body element, the container needs to either be an absolute positioned element,
34970 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34971 the container size if it is not the body element.</b>
34972
34973 * @constructor
34974 * Create a new Border
34975 * @param {Object} config Configuration options
34976  */
34977 Roo.bootstrap.layout.Border = function(config){
34978     config = config || {};
34979     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34980     
34981     
34982     
34983     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34984         if(config[region]){
34985             config[region].region = region;
34986             this.addRegion(config[region]);
34987         }
34988     },this);
34989     
34990 };
34991
34992 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34993
34994 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34995     /**
34996      * Creates and adds a new region if it doesn't already exist.
34997      * @param {String} target The target region key (north, south, east, west or center).
34998      * @param {Object} config The regions config object
34999      * @return {BorderLayoutRegion} The new region
35000      */
35001     addRegion : function(config)
35002     {
35003         if(!this.regions[config.region]){
35004             var r = this.factory(config);
35005             this.bindRegion(r);
35006         }
35007         return this.regions[config.region];
35008     },
35009
35010     // private (kinda)
35011     bindRegion : function(r){
35012         this.regions[r.config.region] = r;
35013         
35014         r.on("visibilitychange",    this.layout, this);
35015         r.on("paneladded",          this.layout, this);
35016         r.on("panelremoved",        this.layout, this);
35017         r.on("invalidated",         this.layout, this);
35018         r.on("resized",             this.onRegionResized, this);
35019         r.on("collapsed",           this.onRegionCollapsed, this);
35020         r.on("expanded",            this.onRegionExpanded, this);
35021     },
35022
35023     /**
35024      * Performs a layout update.
35025      */
35026     layout : function()
35027     {
35028         if(this.updating) {
35029             return;
35030         }
35031         
35032         // render all the rebions if they have not been done alreayd?
35033         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35034             if(this.regions[region] && !this.regions[region].bodyEl){
35035                 this.regions[region].onRender(this.el)
35036             }
35037         },this);
35038         
35039         var size = this.getViewSize();
35040         var w = size.width;
35041         var h = size.height;
35042         var centerW = w;
35043         var centerH = h;
35044         var centerY = 0;
35045         var centerX = 0;
35046         //var x = 0, y = 0;
35047
35048         var rs = this.regions;
35049         var north = rs["north"];
35050         var south = rs["south"]; 
35051         var west = rs["west"];
35052         var east = rs["east"];
35053         var center = rs["center"];
35054         //if(this.hideOnLayout){ // not supported anymore
35055             //c.el.setStyle("display", "none");
35056         //}
35057         if(north && north.isVisible()){
35058             var b = north.getBox();
35059             var m = north.getMargins();
35060             b.width = w - (m.left+m.right);
35061             b.x = m.left;
35062             b.y = m.top;
35063             centerY = b.height + b.y + m.bottom;
35064             centerH -= centerY;
35065             north.updateBox(this.safeBox(b));
35066         }
35067         if(south && south.isVisible()){
35068             var b = south.getBox();
35069             var m = south.getMargins();
35070             b.width = w - (m.left+m.right);
35071             b.x = m.left;
35072             var totalHeight = (b.height + m.top + m.bottom);
35073             b.y = h - totalHeight + m.top;
35074             centerH -= totalHeight;
35075             south.updateBox(this.safeBox(b));
35076         }
35077         if(west && west.isVisible()){
35078             var b = west.getBox();
35079             var m = west.getMargins();
35080             b.height = centerH - (m.top+m.bottom);
35081             b.x = m.left;
35082             b.y = centerY + m.top;
35083             var totalWidth = (b.width + m.left + m.right);
35084             centerX += totalWidth;
35085             centerW -= totalWidth;
35086             west.updateBox(this.safeBox(b));
35087         }
35088         if(east && east.isVisible()){
35089             var b = east.getBox();
35090             var m = east.getMargins();
35091             b.height = centerH - (m.top+m.bottom);
35092             var totalWidth = (b.width + m.left + m.right);
35093             b.x = w - totalWidth + m.left;
35094             b.y = centerY + m.top;
35095             centerW -= totalWidth;
35096             east.updateBox(this.safeBox(b));
35097         }
35098         if(center){
35099             var m = center.getMargins();
35100             var centerBox = {
35101                 x: centerX + m.left,
35102                 y: centerY + m.top,
35103                 width: centerW - (m.left+m.right),
35104                 height: centerH - (m.top+m.bottom)
35105             };
35106             //if(this.hideOnLayout){
35107                 //center.el.setStyle("display", "block");
35108             //}
35109             center.updateBox(this.safeBox(centerBox));
35110         }
35111         this.el.repaint();
35112         this.fireEvent("layout", this);
35113     },
35114
35115     // private
35116     safeBox : function(box){
35117         box.width = Math.max(0, box.width);
35118         box.height = Math.max(0, box.height);
35119         return box;
35120     },
35121
35122     /**
35123      * Adds a ContentPanel (or subclass) to this layout.
35124      * @param {String} target The target region key (north, south, east, west or center).
35125      * @param {Roo.ContentPanel} panel The panel to add
35126      * @return {Roo.ContentPanel} The added panel
35127      */
35128     add : function(target, panel){
35129          
35130         target = target.toLowerCase();
35131         return this.regions[target].add(panel);
35132     },
35133
35134     /**
35135      * Remove a ContentPanel (or subclass) to this layout.
35136      * @param {String} target The target region key (north, south, east, west or center).
35137      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35138      * @return {Roo.ContentPanel} The removed panel
35139      */
35140     remove : function(target, panel){
35141         target = target.toLowerCase();
35142         return this.regions[target].remove(panel);
35143     },
35144
35145     /**
35146      * Searches all regions for a panel with the specified id
35147      * @param {String} panelId
35148      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35149      */
35150     findPanel : function(panelId){
35151         var rs = this.regions;
35152         for(var target in rs){
35153             if(typeof rs[target] != "function"){
35154                 var p = rs[target].getPanel(panelId);
35155                 if(p){
35156                     return p;
35157                 }
35158             }
35159         }
35160         return null;
35161     },
35162
35163     /**
35164      * Searches all regions for a panel with the specified id and activates (shows) it.
35165      * @param {String/ContentPanel} panelId The panels id or the panel itself
35166      * @return {Roo.ContentPanel} The shown panel or null
35167      */
35168     showPanel : function(panelId) {
35169       var rs = this.regions;
35170       for(var target in rs){
35171          var r = rs[target];
35172          if(typeof r != "function"){
35173             if(r.hasPanel(panelId)){
35174                return r.showPanel(panelId);
35175             }
35176          }
35177       }
35178       return null;
35179    },
35180
35181    /**
35182      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35183      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35184      */
35185    /*
35186     restoreState : function(provider){
35187         if(!provider){
35188             provider = Roo.state.Manager;
35189         }
35190         var sm = new Roo.LayoutStateManager();
35191         sm.init(this, provider);
35192     },
35193 */
35194  
35195  
35196     /**
35197      * Adds a xtype elements to the layout.
35198      * <pre><code>
35199
35200 layout.addxtype({
35201        xtype : 'ContentPanel',
35202        region: 'west',
35203        items: [ .... ]
35204    }
35205 );
35206
35207 layout.addxtype({
35208         xtype : 'NestedLayoutPanel',
35209         region: 'west',
35210         layout: {
35211            center: { },
35212            west: { }   
35213         },
35214         items : [ ... list of content panels or nested layout panels.. ]
35215    }
35216 );
35217 </code></pre>
35218      * @param {Object} cfg Xtype definition of item to add.
35219      */
35220     addxtype : function(cfg)
35221     {
35222         // basically accepts a pannel...
35223         // can accept a layout region..!?!?
35224         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35225         
35226         
35227         // theory?  children can only be panels??
35228         
35229         //if (!cfg.xtype.match(/Panel$/)) {
35230         //    return false;
35231         //}
35232         var ret = false;
35233         
35234         if (typeof(cfg.region) == 'undefined') {
35235             Roo.log("Failed to add Panel, region was not set");
35236             Roo.log(cfg);
35237             return false;
35238         }
35239         var region = cfg.region;
35240         delete cfg.region;
35241         
35242           
35243         var xitems = [];
35244         if (cfg.items) {
35245             xitems = cfg.items;
35246             delete cfg.items;
35247         }
35248         var nb = false;
35249         
35250         switch(cfg.xtype) 
35251         {
35252             case 'Content':  // ContentPanel (el, cfg)
35253             case 'Scroll':  // ContentPanel (el, cfg)
35254             case 'View': 
35255                 cfg.autoCreate = true;
35256                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35257                 //} else {
35258                 //    var el = this.el.createChild();
35259                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35260                 //}
35261                 
35262                 this.add(region, ret);
35263                 break;
35264             
35265             /*
35266             case 'TreePanel': // our new panel!
35267                 cfg.el = this.el.createChild();
35268                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35269                 this.add(region, ret);
35270                 break;
35271             */
35272             
35273             case 'Nest': 
35274                 // create a new Layout (which is  a Border Layout...
35275                 
35276                 var clayout = cfg.layout;
35277                 clayout.el  = this.el.createChild();
35278                 clayout.items   = clayout.items  || [];
35279                 
35280                 delete cfg.layout;
35281                 
35282                 // replace this exitems with the clayout ones..
35283                 xitems = clayout.items;
35284                  
35285                 // force background off if it's in center...
35286                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35287                     cfg.background = false;
35288                 }
35289                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35290                 
35291                 
35292                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35293                 //console.log('adding nested layout panel '  + cfg.toSource());
35294                 this.add(region, ret);
35295                 nb = {}; /// find first...
35296                 break;
35297             
35298             case 'Grid':
35299                 
35300                 // needs grid and region
35301                 
35302                 //var el = this.getRegion(region).el.createChild();
35303                 /*
35304                  *var el = this.el.createChild();
35305                 // create the grid first...
35306                 cfg.grid.container = el;
35307                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35308                 */
35309                 
35310                 if (region == 'center' && this.active ) {
35311                     cfg.background = false;
35312                 }
35313                 
35314                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35315                 
35316                 this.add(region, ret);
35317                 /*
35318                 if (cfg.background) {
35319                     // render grid on panel activation (if panel background)
35320                     ret.on('activate', function(gp) {
35321                         if (!gp.grid.rendered) {
35322                     //        gp.grid.render(el);
35323                         }
35324                     });
35325                 } else {
35326                   //  cfg.grid.render(el);
35327                 }
35328                 */
35329                 break;
35330            
35331            
35332             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35333                 // it was the old xcomponent building that caused this before.
35334                 // espeically if border is the top element in the tree.
35335                 ret = this;
35336                 break; 
35337                 
35338                     
35339                 
35340                 
35341                 
35342             default:
35343                 /*
35344                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35345                     
35346                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35347                     this.add(region, ret);
35348                 } else {
35349                 */
35350                     Roo.log(cfg);
35351                     throw "Can not add '" + cfg.xtype + "' to Border";
35352                     return null;
35353              
35354                                 
35355              
35356         }
35357         this.beginUpdate();
35358         // add children..
35359         var region = '';
35360         var abn = {};
35361         Roo.each(xitems, function(i)  {
35362             region = nb && i.region ? i.region : false;
35363             
35364             var add = ret.addxtype(i);
35365            
35366             if (region) {
35367                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35368                 if (!i.background) {
35369                     abn[region] = nb[region] ;
35370                 }
35371             }
35372             
35373         });
35374         this.endUpdate();
35375
35376         // make the last non-background panel active..
35377         //if (nb) { Roo.log(abn); }
35378         if (nb) {
35379             
35380             for(var r in abn) {
35381                 region = this.getRegion(r);
35382                 if (region) {
35383                     // tried using nb[r], but it does not work..
35384                      
35385                     region.showPanel(abn[r]);
35386                    
35387                 }
35388             }
35389         }
35390         return ret;
35391         
35392     },
35393     
35394     
35395 // private
35396     factory : function(cfg)
35397     {
35398         
35399         var validRegions = Roo.bootstrap.layout.Border.regions;
35400
35401         var target = cfg.region;
35402         cfg.mgr = this;
35403         
35404         var r = Roo.bootstrap.layout;
35405         Roo.log(target);
35406         switch(target){
35407             case "north":
35408                 return new r.North(cfg);
35409             case "south":
35410                 return new r.South(cfg);
35411             case "east":
35412                 return new r.East(cfg);
35413             case "west":
35414                 return new r.West(cfg);
35415             case "center":
35416                 return new r.Center(cfg);
35417         }
35418         throw 'Layout region "'+target+'" not supported.';
35419     }
35420     
35421     
35422 });
35423  /*
35424  * Based on:
35425  * Ext JS Library 1.1.1
35426  * Copyright(c) 2006-2007, Ext JS, LLC.
35427  *
35428  * Originally Released Under LGPL - original licence link has changed is not relivant.
35429  *
35430  * Fork - LGPL
35431  * <script type="text/javascript">
35432  */
35433  
35434 /**
35435  * @class Roo.bootstrap.layout.Basic
35436  * @extends Roo.util.Observable
35437  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35438  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35439  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35440  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35441  * @cfg {string}   region  the region that it inhabits..
35442  * @cfg {bool}   skipConfig skip config?
35443  * 
35444
35445  */
35446 Roo.bootstrap.layout.Basic = function(config){
35447     
35448     this.mgr = config.mgr;
35449     
35450     this.position = config.region;
35451     
35452     var skipConfig = config.skipConfig;
35453     
35454     this.events = {
35455         /**
35456          * @scope Roo.BasicLayoutRegion
35457          */
35458         
35459         /**
35460          * @event beforeremove
35461          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35462          * @param {Roo.LayoutRegion} this
35463          * @param {Roo.ContentPanel} panel The panel
35464          * @param {Object} e The cancel event object
35465          */
35466         "beforeremove" : true,
35467         /**
35468          * @event invalidated
35469          * Fires when the layout for this region is changed.
35470          * @param {Roo.LayoutRegion} this
35471          */
35472         "invalidated" : true,
35473         /**
35474          * @event visibilitychange
35475          * Fires when this region is shown or hidden 
35476          * @param {Roo.LayoutRegion} this
35477          * @param {Boolean} visibility true or false
35478          */
35479         "visibilitychange" : true,
35480         /**
35481          * @event paneladded
35482          * Fires when a panel is added. 
35483          * @param {Roo.LayoutRegion} this
35484          * @param {Roo.ContentPanel} panel The panel
35485          */
35486         "paneladded" : true,
35487         /**
35488          * @event panelremoved
35489          * Fires when a panel is removed. 
35490          * @param {Roo.LayoutRegion} this
35491          * @param {Roo.ContentPanel} panel The panel
35492          */
35493         "panelremoved" : true,
35494         /**
35495          * @event beforecollapse
35496          * Fires when this region before collapse.
35497          * @param {Roo.LayoutRegion} this
35498          */
35499         "beforecollapse" : true,
35500         /**
35501          * @event collapsed
35502          * Fires when this region is collapsed.
35503          * @param {Roo.LayoutRegion} this
35504          */
35505         "collapsed" : true,
35506         /**
35507          * @event expanded
35508          * Fires when this region is expanded.
35509          * @param {Roo.LayoutRegion} this
35510          */
35511         "expanded" : true,
35512         /**
35513          * @event slideshow
35514          * Fires when this region is slid into view.
35515          * @param {Roo.LayoutRegion} this
35516          */
35517         "slideshow" : true,
35518         /**
35519          * @event slidehide
35520          * Fires when this region slides out of view. 
35521          * @param {Roo.LayoutRegion} this
35522          */
35523         "slidehide" : true,
35524         /**
35525          * @event panelactivated
35526          * Fires when a panel is activated. 
35527          * @param {Roo.LayoutRegion} this
35528          * @param {Roo.ContentPanel} panel The activated panel
35529          */
35530         "panelactivated" : true,
35531         /**
35532          * @event resized
35533          * Fires when the user resizes this region. 
35534          * @param {Roo.LayoutRegion} this
35535          * @param {Number} newSize The new size (width for east/west, height for north/south)
35536          */
35537         "resized" : true
35538     };
35539     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35540     this.panels = new Roo.util.MixedCollection();
35541     this.panels.getKey = this.getPanelId.createDelegate(this);
35542     this.box = null;
35543     this.activePanel = null;
35544     // ensure listeners are added...
35545     
35546     if (config.listeners || config.events) {
35547         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35548             listeners : config.listeners || {},
35549             events : config.events || {}
35550         });
35551     }
35552     
35553     if(skipConfig !== true){
35554         this.applyConfig(config);
35555     }
35556 };
35557
35558 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35559 {
35560     getPanelId : function(p){
35561         return p.getId();
35562     },
35563     
35564     applyConfig : function(config){
35565         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35566         this.config = config;
35567         
35568     },
35569     
35570     /**
35571      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35572      * the width, for horizontal (north, south) the height.
35573      * @param {Number} newSize The new width or height
35574      */
35575     resizeTo : function(newSize){
35576         var el = this.el ? this.el :
35577                  (this.activePanel ? this.activePanel.getEl() : null);
35578         if(el){
35579             switch(this.position){
35580                 case "east":
35581                 case "west":
35582                     el.setWidth(newSize);
35583                     this.fireEvent("resized", this, newSize);
35584                 break;
35585                 case "north":
35586                 case "south":
35587                     el.setHeight(newSize);
35588                     this.fireEvent("resized", this, newSize);
35589                 break;                
35590             }
35591         }
35592     },
35593     
35594     getBox : function(){
35595         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35596     },
35597     
35598     getMargins : function(){
35599         return this.margins;
35600     },
35601     
35602     updateBox : function(box){
35603         this.box = box;
35604         var el = this.activePanel.getEl();
35605         el.dom.style.left = box.x + "px";
35606         el.dom.style.top = box.y + "px";
35607         this.activePanel.setSize(box.width, box.height);
35608     },
35609     
35610     /**
35611      * Returns the container element for this region.
35612      * @return {Roo.Element}
35613      */
35614     getEl : function(){
35615         return this.activePanel;
35616     },
35617     
35618     /**
35619      * Returns true if this region is currently visible.
35620      * @return {Boolean}
35621      */
35622     isVisible : function(){
35623         return this.activePanel ? true : false;
35624     },
35625     
35626     setActivePanel : function(panel){
35627         panel = this.getPanel(panel);
35628         if(this.activePanel && this.activePanel != panel){
35629             this.activePanel.setActiveState(false);
35630             this.activePanel.getEl().setLeftTop(-10000,-10000);
35631         }
35632         this.activePanel = panel;
35633         panel.setActiveState(true);
35634         if(this.box){
35635             panel.setSize(this.box.width, this.box.height);
35636         }
35637         this.fireEvent("panelactivated", this, panel);
35638         this.fireEvent("invalidated");
35639     },
35640     
35641     /**
35642      * Show the specified panel.
35643      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35644      * @return {Roo.ContentPanel} The shown panel or null
35645      */
35646     showPanel : function(panel){
35647         panel = this.getPanel(panel);
35648         if(panel){
35649             this.setActivePanel(panel);
35650         }
35651         return panel;
35652     },
35653     
35654     /**
35655      * Get the active panel for this region.
35656      * @return {Roo.ContentPanel} The active panel or null
35657      */
35658     getActivePanel : function(){
35659         return this.activePanel;
35660     },
35661     
35662     /**
35663      * Add the passed ContentPanel(s)
35664      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35665      * @return {Roo.ContentPanel} The panel added (if only one was added)
35666      */
35667     add : function(panel){
35668         if(arguments.length > 1){
35669             for(var i = 0, len = arguments.length; i < len; i++) {
35670                 this.add(arguments[i]);
35671             }
35672             return null;
35673         }
35674         if(this.hasPanel(panel)){
35675             this.showPanel(panel);
35676             return panel;
35677         }
35678         var el = panel.getEl();
35679         if(el.dom.parentNode != this.mgr.el.dom){
35680             this.mgr.el.dom.appendChild(el.dom);
35681         }
35682         if(panel.setRegion){
35683             panel.setRegion(this);
35684         }
35685         this.panels.add(panel);
35686         el.setStyle("position", "absolute");
35687         if(!panel.background){
35688             this.setActivePanel(panel);
35689             if(this.config.initialSize && this.panels.getCount()==1){
35690                 this.resizeTo(this.config.initialSize);
35691             }
35692         }
35693         this.fireEvent("paneladded", this, panel);
35694         return panel;
35695     },
35696     
35697     /**
35698      * Returns true if the panel is in this region.
35699      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35700      * @return {Boolean}
35701      */
35702     hasPanel : function(panel){
35703         if(typeof panel == "object"){ // must be panel obj
35704             panel = panel.getId();
35705         }
35706         return this.getPanel(panel) ? true : false;
35707     },
35708     
35709     /**
35710      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35711      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35712      * @param {Boolean} preservePanel Overrides the config preservePanel option
35713      * @return {Roo.ContentPanel} The panel that was removed
35714      */
35715     remove : function(panel, preservePanel){
35716         panel = this.getPanel(panel);
35717         if(!panel){
35718             return null;
35719         }
35720         var e = {};
35721         this.fireEvent("beforeremove", this, panel, e);
35722         if(e.cancel === true){
35723             return null;
35724         }
35725         var panelId = panel.getId();
35726         this.panels.removeKey(panelId);
35727         return panel;
35728     },
35729     
35730     /**
35731      * Returns the panel specified or null if it's not in this region.
35732      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35733      * @return {Roo.ContentPanel}
35734      */
35735     getPanel : function(id){
35736         if(typeof id == "object"){ // must be panel obj
35737             return id;
35738         }
35739         return this.panels.get(id);
35740     },
35741     
35742     /**
35743      * Returns this regions position (north/south/east/west/center).
35744      * @return {String} 
35745      */
35746     getPosition: function(){
35747         return this.position;    
35748     }
35749 });/*
35750  * Based on:
35751  * Ext JS Library 1.1.1
35752  * Copyright(c) 2006-2007, Ext JS, LLC.
35753  *
35754  * Originally Released Under LGPL - original licence link has changed is not relivant.
35755  *
35756  * Fork - LGPL
35757  * <script type="text/javascript">
35758  */
35759  
35760 /**
35761  * @class Roo.bootstrap.layout.Region
35762  * @extends Roo.bootstrap.layout.Basic
35763  * This class represents a region in a layout manager.
35764  
35765  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35766  * @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})
35767  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35768  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35769  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35770  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35771  * @cfg {String}    title           The title for the region (overrides panel titles)
35772  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35773  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35774  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35775  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35776  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35777  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35778  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35779  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35780  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35781  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35782
35783  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35784  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35785  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35786  * @cfg {Number}    width           For East/West panels
35787  * @cfg {Number}    height          For North/South panels
35788  * @cfg {Boolean}   split           To show the splitter
35789  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35790  * 
35791  * @cfg {string}   cls             Extra CSS classes to add to region
35792  * 
35793  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35794  * @cfg {string}   region  the region that it inhabits..
35795  *
35796
35797  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35798  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35799
35800  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35801  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35802  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35803  */
35804 Roo.bootstrap.layout.Region = function(config)
35805 {
35806     this.applyConfig(config);
35807
35808     var mgr = config.mgr;
35809     var pos = config.region;
35810     config.skipConfig = true;
35811     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35812     
35813     if (mgr.el) {
35814         this.onRender(mgr.el);   
35815     }
35816      
35817     this.visible = true;
35818     this.collapsed = false;
35819     this.unrendered_panels = [];
35820 };
35821
35822 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35823
35824     position: '', // set by wrapper (eg. north/south etc..)
35825     unrendered_panels : null,  // unrendered panels.
35826     createBody : function(){
35827         /** This region's body element 
35828         * @type Roo.Element */
35829         this.bodyEl = this.el.createChild({
35830                 tag: "div",
35831                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35832         });
35833     },
35834
35835     onRender: function(ctr, pos)
35836     {
35837         var dh = Roo.DomHelper;
35838         /** This region's container element 
35839         * @type Roo.Element */
35840         this.el = dh.append(ctr.dom, {
35841                 tag: "div",
35842                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35843             }, true);
35844         /** This region's title element 
35845         * @type Roo.Element */
35846     
35847         this.titleEl = dh.append(this.el.dom,
35848             {
35849                     tag: "div",
35850                     unselectable: "on",
35851                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35852                     children:[
35853                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35854                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35855                     ]}, true);
35856         
35857         this.titleEl.enableDisplayMode();
35858         /** This region's title text element 
35859         * @type HTMLElement */
35860         this.titleTextEl = this.titleEl.dom.firstChild;
35861         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35862         /*
35863         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35864         this.closeBtn.enableDisplayMode();
35865         this.closeBtn.on("click", this.closeClicked, this);
35866         this.closeBtn.hide();
35867     */
35868         this.createBody(this.config);
35869         if(this.config.hideWhenEmpty){
35870             this.hide();
35871             this.on("paneladded", this.validateVisibility, this);
35872             this.on("panelremoved", this.validateVisibility, this);
35873         }
35874         if(this.autoScroll){
35875             this.bodyEl.setStyle("overflow", "auto");
35876         }else{
35877             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35878         }
35879         //if(c.titlebar !== false){
35880             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35881                 this.titleEl.hide();
35882             }else{
35883                 this.titleEl.show();
35884                 if(this.config.title){
35885                     this.titleTextEl.innerHTML = this.config.title;
35886                 }
35887             }
35888         //}
35889         if(this.config.collapsed){
35890             this.collapse(true);
35891         }
35892         if(this.config.hidden){
35893             this.hide();
35894         }
35895         
35896         if (this.unrendered_panels && this.unrendered_panels.length) {
35897             for (var i =0;i< this.unrendered_panels.length; i++) {
35898                 this.add(this.unrendered_panels[i]);
35899             }
35900             this.unrendered_panels = null;
35901             
35902         }
35903         
35904     },
35905     
35906     applyConfig : function(c)
35907     {
35908         /*
35909          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35910             var dh = Roo.DomHelper;
35911             if(c.titlebar !== false){
35912                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35913                 this.collapseBtn.on("click", this.collapse, this);
35914                 this.collapseBtn.enableDisplayMode();
35915                 /*
35916                 if(c.showPin === true || this.showPin){
35917                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35918                     this.stickBtn.enableDisplayMode();
35919                     this.stickBtn.on("click", this.expand, this);
35920                     this.stickBtn.hide();
35921                 }
35922                 
35923             }
35924             */
35925             /** This region's collapsed element
35926             * @type Roo.Element */
35927             /*
35928              *
35929             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35930                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35931             ]}, true);
35932             
35933             if(c.floatable !== false){
35934                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35935                this.collapsedEl.on("click", this.collapseClick, this);
35936             }
35937
35938             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35939                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35940                    id: "message", unselectable: "on", style:{"float":"left"}});
35941                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35942              }
35943             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35944             this.expandBtn.on("click", this.expand, this);
35945             
35946         }
35947         
35948         if(this.collapseBtn){
35949             this.collapseBtn.setVisible(c.collapsible == true);
35950         }
35951         
35952         this.cmargins = c.cmargins || this.cmargins ||
35953                          (this.position == "west" || this.position == "east" ?
35954                              {top: 0, left: 2, right:2, bottom: 0} :
35955                              {top: 2, left: 0, right:0, bottom: 2});
35956         */
35957         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35958         
35959         
35960         this.bottomTabs = c.tabPosition != "top";
35961         
35962         this.autoScroll = c.autoScroll || false;
35963         
35964         
35965        
35966         
35967         this.duration = c.duration || .30;
35968         this.slideDuration = c.slideDuration || .45;
35969         this.config = c;
35970        
35971     },
35972     /**
35973      * Returns true if this region is currently visible.
35974      * @return {Boolean}
35975      */
35976     isVisible : function(){
35977         return this.visible;
35978     },
35979
35980     /**
35981      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35982      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35983      */
35984     //setCollapsedTitle : function(title){
35985     //    title = title || "&#160;";
35986      //   if(this.collapsedTitleTextEl){
35987       //      this.collapsedTitleTextEl.innerHTML = title;
35988        // }
35989     //},
35990
35991     getBox : function(){
35992         var b;
35993       //  if(!this.collapsed){
35994             b = this.el.getBox(false, true);
35995        // }else{
35996           //  b = this.collapsedEl.getBox(false, true);
35997         //}
35998         return b;
35999     },
36000
36001     getMargins : function(){
36002         return this.margins;
36003         //return this.collapsed ? this.cmargins : this.margins;
36004     },
36005 /*
36006     highlight : function(){
36007         this.el.addClass("x-layout-panel-dragover");
36008     },
36009
36010     unhighlight : function(){
36011         this.el.removeClass("x-layout-panel-dragover");
36012     },
36013 */
36014     updateBox : function(box)
36015     {
36016         if (!this.bodyEl) {
36017             return; // not rendered yet..
36018         }
36019         
36020         this.box = box;
36021         if(!this.collapsed){
36022             this.el.dom.style.left = box.x + "px";
36023             this.el.dom.style.top = box.y + "px";
36024             this.updateBody(box.width, box.height);
36025         }else{
36026             this.collapsedEl.dom.style.left = box.x + "px";
36027             this.collapsedEl.dom.style.top = box.y + "px";
36028             this.collapsedEl.setSize(box.width, box.height);
36029         }
36030         if(this.tabs){
36031             this.tabs.autoSizeTabs();
36032         }
36033     },
36034
36035     updateBody : function(w, h)
36036     {
36037         if(w !== null){
36038             this.el.setWidth(w);
36039             w -= this.el.getBorderWidth("rl");
36040             if(this.config.adjustments){
36041                 w += this.config.adjustments[0];
36042             }
36043         }
36044         if(h !== null && h > 0){
36045             this.el.setHeight(h);
36046             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36047             h -= this.el.getBorderWidth("tb");
36048             if(this.config.adjustments){
36049                 h += this.config.adjustments[1];
36050             }
36051             this.bodyEl.setHeight(h);
36052             if(this.tabs){
36053                 h = this.tabs.syncHeight(h);
36054             }
36055         }
36056         if(this.panelSize){
36057             w = w !== null ? w : this.panelSize.width;
36058             h = h !== null ? h : this.panelSize.height;
36059         }
36060         if(this.activePanel){
36061             var el = this.activePanel.getEl();
36062             w = w !== null ? w : el.getWidth();
36063             h = h !== null ? h : el.getHeight();
36064             this.panelSize = {width: w, height: h};
36065             this.activePanel.setSize(w, h);
36066         }
36067         if(Roo.isIE && this.tabs){
36068             this.tabs.el.repaint();
36069         }
36070     },
36071
36072     /**
36073      * Returns the container element for this region.
36074      * @return {Roo.Element}
36075      */
36076     getEl : function(){
36077         return this.el;
36078     },
36079
36080     /**
36081      * Hides this region.
36082      */
36083     hide : function(){
36084         //if(!this.collapsed){
36085             this.el.dom.style.left = "-2000px";
36086             this.el.hide();
36087         //}else{
36088          //   this.collapsedEl.dom.style.left = "-2000px";
36089          //   this.collapsedEl.hide();
36090        // }
36091         this.visible = false;
36092         this.fireEvent("visibilitychange", this, false);
36093     },
36094
36095     /**
36096      * Shows this region if it was previously hidden.
36097      */
36098     show : function(){
36099         //if(!this.collapsed){
36100             this.el.show();
36101         //}else{
36102         //    this.collapsedEl.show();
36103        // }
36104         this.visible = true;
36105         this.fireEvent("visibilitychange", this, true);
36106     },
36107 /*
36108     closeClicked : function(){
36109         if(this.activePanel){
36110             this.remove(this.activePanel);
36111         }
36112     },
36113
36114     collapseClick : function(e){
36115         if(this.isSlid){
36116            e.stopPropagation();
36117            this.slideIn();
36118         }else{
36119            e.stopPropagation();
36120            this.slideOut();
36121         }
36122     },
36123 */
36124     /**
36125      * Collapses this region.
36126      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36127      */
36128     /*
36129     collapse : function(skipAnim, skipCheck = false){
36130         if(this.collapsed) {
36131             return;
36132         }
36133         
36134         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36135             
36136             this.collapsed = true;
36137             if(this.split){
36138                 this.split.el.hide();
36139             }
36140             if(this.config.animate && skipAnim !== true){
36141                 this.fireEvent("invalidated", this);
36142                 this.animateCollapse();
36143             }else{
36144                 this.el.setLocation(-20000,-20000);
36145                 this.el.hide();
36146                 this.collapsedEl.show();
36147                 this.fireEvent("collapsed", this);
36148                 this.fireEvent("invalidated", this);
36149             }
36150         }
36151         
36152     },
36153 */
36154     animateCollapse : function(){
36155         // overridden
36156     },
36157
36158     /**
36159      * Expands this region if it was previously collapsed.
36160      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36161      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36162      */
36163     /*
36164     expand : function(e, skipAnim){
36165         if(e) {
36166             e.stopPropagation();
36167         }
36168         if(!this.collapsed || this.el.hasActiveFx()) {
36169             return;
36170         }
36171         if(this.isSlid){
36172             this.afterSlideIn();
36173             skipAnim = true;
36174         }
36175         this.collapsed = false;
36176         if(this.config.animate && skipAnim !== true){
36177             this.animateExpand();
36178         }else{
36179             this.el.show();
36180             if(this.split){
36181                 this.split.el.show();
36182             }
36183             this.collapsedEl.setLocation(-2000,-2000);
36184             this.collapsedEl.hide();
36185             this.fireEvent("invalidated", this);
36186             this.fireEvent("expanded", this);
36187         }
36188     },
36189 */
36190     animateExpand : function(){
36191         // overridden
36192     },
36193
36194     initTabs : function()
36195     {
36196         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36197         
36198         var ts = new Roo.bootstrap.panel.Tabs({
36199                 el: this.bodyEl.dom,
36200                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36201                 disableTooltips: this.config.disableTabTips,
36202                 toolbar : this.config.toolbar
36203             });
36204         
36205         if(this.config.hideTabs){
36206             ts.stripWrap.setDisplayed(false);
36207         }
36208         this.tabs = ts;
36209         ts.resizeTabs = this.config.resizeTabs === true;
36210         ts.minTabWidth = this.config.minTabWidth || 40;
36211         ts.maxTabWidth = this.config.maxTabWidth || 250;
36212         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36213         ts.monitorResize = false;
36214         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36215         ts.bodyEl.addClass('roo-layout-tabs-body');
36216         this.panels.each(this.initPanelAsTab, this);
36217     },
36218
36219     initPanelAsTab : function(panel){
36220         var ti = this.tabs.addTab(
36221             panel.getEl().id,
36222             panel.getTitle(),
36223             null,
36224             this.config.closeOnTab && panel.isClosable(),
36225             panel.tpl
36226         );
36227         if(panel.tabTip !== undefined){
36228             ti.setTooltip(panel.tabTip);
36229         }
36230         ti.on("activate", function(){
36231               this.setActivePanel(panel);
36232         }, this);
36233         
36234         if(this.config.closeOnTab){
36235             ti.on("beforeclose", function(t, e){
36236                 e.cancel = true;
36237                 this.remove(panel);
36238             }, this);
36239         }
36240         
36241         panel.tabItem = ti;
36242         
36243         return ti;
36244     },
36245
36246     updatePanelTitle : function(panel, title)
36247     {
36248         if(this.activePanel == panel){
36249             this.updateTitle(title);
36250         }
36251         if(this.tabs){
36252             var ti = this.tabs.getTab(panel.getEl().id);
36253             ti.setText(title);
36254             if(panel.tabTip !== undefined){
36255                 ti.setTooltip(panel.tabTip);
36256             }
36257         }
36258     },
36259
36260     updateTitle : function(title){
36261         if(this.titleTextEl && !this.config.title){
36262             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36263         }
36264     },
36265
36266     setActivePanel : function(panel)
36267     {
36268         panel = this.getPanel(panel);
36269         if(this.activePanel && this.activePanel != panel){
36270             if(this.activePanel.setActiveState(false) === false){
36271                 return;
36272             }
36273         }
36274         this.activePanel = panel;
36275         panel.setActiveState(true);
36276         if(this.panelSize){
36277             panel.setSize(this.panelSize.width, this.panelSize.height);
36278         }
36279         if(this.closeBtn){
36280             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36281         }
36282         this.updateTitle(panel.getTitle());
36283         if(this.tabs){
36284             this.fireEvent("invalidated", this);
36285         }
36286         this.fireEvent("panelactivated", this, panel);
36287     },
36288
36289     /**
36290      * Shows the specified panel.
36291      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36292      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36293      */
36294     showPanel : function(panel)
36295     {
36296         panel = this.getPanel(panel);
36297         if(panel){
36298             if(this.tabs){
36299                 var tab = this.tabs.getTab(panel.getEl().id);
36300                 if(tab.isHidden()){
36301                     this.tabs.unhideTab(tab.id);
36302                 }
36303                 tab.activate();
36304             }else{
36305                 this.setActivePanel(panel);
36306             }
36307         }
36308         return panel;
36309     },
36310
36311     /**
36312      * Get the active panel for this region.
36313      * @return {Roo.ContentPanel} The active panel or null
36314      */
36315     getActivePanel : function(){
36316         return this.activePanel;
36317     },
36318
36319     validateVisibility : function(){
36320         if(this.panels.getCount() < 1){
36321             this.updateTitle("&#160;");
36322             this.closeBtn.hide();
36323             this.hide();
36324         }else{
36325             if(!this.isVisible()){
36326                 this.show();
36327             }
36328         }
36329     },
36330
36331     /**
36332      * Adds the passed ContentPanel(s) to this region.
36333      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36334      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36335      */
36336     add : function(panel)
36337     {
36338         if(arguments.length > 1){
36339             for(var i = 0, len = arguments.length; i < len; i++) {
36340                 this.add(arguments[i]);
36341             }
36342             return null;
36343         }
36344         
36345         // if we have not been rendered yet, then we can not really do much of this..
36346         if (!this.bodyEl) {
36347             this.unrendered_panels.push(panel);
36348             return panel;
36349         }
36350         
36351         
36352         
36353         
36354         if(this.hasPanel(panel)){
36355             this.showPanel(panel);
36356             return panel;
36357         }
36358         panel.setRegion(this);
36359         this.panels.add(panel);
36360        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36361             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36362             // and hide them... ???
36363             this.bodyEl.dom.appendChild(panel.getEl().dom);
36364             if(panel.background !== true){
36365                 this.setActivePanel(panel);
36366             }
36367             this.fireEvent("paneladded", this, panel);
36368             return panel;
36369         }
36370         */
36371         if(!this.tabs){
36372             this.initTabs();
36373         }else{
36374             this.initPanelAsTab(panel);
36375         }
36376         
36377         
36378         if(panel.background !== true){
36379             this.tabs.activate(panel.getEl().id);
36380         }
36381         this.fireEvent("paneladded", this, panel);
36382         return panel;
36383     },
36384
36385     /**
36386      * Hides the tab for the specified panel.
36387      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36388      */
36389     hidePanel : function(panel){
36390         if(this.tabs && (panel = this.getPanel(panel))){
36391             this.tabs.hideTab(panel.getEl().id);
36392         }
36393     },
36394
36395     /**
36396      * Unhides the tab for a previously hidden panel.
36397      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36398      */
36399     unhidePanel : function(panel){
36400         if(this.tabs && (panel = this.getPanel(panel))){
36401             this.tabs.unhideTab(panel.getEl().id);
36402         }
36403     },
36404
36405     clearPanels : function(){
36406         while(this.panels.getCount() > 0){
36407              this.remove(this.panels.first());
36408         }
36409     },
36410
36411     /**
36412      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36413      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36414      * @param {Boolean} preservePanel Overrides the config preservePanel option
36415      * @return {Roo.ContentPanel} The panel that was removed
36416      */
36417     remove : function(panel, preservePanel)
36418     {
36419         panel = this.getPanel(panel);
36420         if(!panel){
36421             return null;
36422         }
36423         var e = {};
36424         this.fireEvent("beforeremove", this, panel, e);
36425         if(e.cancel === true){
36426             return null;
36427         }
36428         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36429         var panelId = panel.getId();
36430         this.panels.removeKey(panelId);
36431         if(preservePanel){
36432             document.body.appendChild(panel.getEl().dom);
36433         }
36434         if(this.tabs){
36435             this.tabs.removeTab(panel.getEl().id);
36436         }else if (!preservePanel){
36437             this.bodyEl.dom.removeChild(panel.getEl().dom);
36438         }
36439         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36440             var p = this.panels.first();
36441             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36442             tempEl.appendChild(p.getEl().dom);
36443             this.bodyEl.update("");
36444             this.bodyEl.dom.appendChild(p.getEl().dom);
36445             tempEl = null;
36446             this.updateTitle(p.getTitle());
36447             this.tabs = null;
36448             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36449             this.setActivePanel(p);
36450         }
36451         panel.setRegion(null);
36452         if(this.activePanel == panel){
36453             this.activePanel = null;
36454         }
36455         if(this.config.autoDestroy !== false && preservePanel !== true){
36456             try{panel.destroy();}catch(e){}
36457         }
36458         this.fireEvent("panelremoved", this, panel);
36459         return panel;
36460     },
36461
36462     /**
36463      * Returns the TabPanel component used by this region
36464      * @return {Roo.TabPanel}
36465      */
36466     getTabs : function(){
36467         return this.tabs;
36468     },
36469
36470     createTool : function(parentEl, className){
36471         var btn = Roo.DomHelper.append(parentEl, {
36472             tag: "div",
36473             cls: "x-layout-tools-button",
36474             children: [ {
36475                 tag: "div",
36476                 cls: "roo-layout-tools-button-inner " + className,
36477                 html: "&#160;"
36478             }]
36479         }, true);
36480         btn.addClassOnOver("roo-layout-tools-button-over");
36481         return btn;
36482     }
36483 });/*
36484  * Based on:
36485  * Ext JS Library 1.1.1
36486  * Copyright(c) 2006-2007, Ext JS, LLC.
36487  *
36488  * Originally Released Under LGPL - original licence link has changed is not relivant.
36489  *
36490  * Fork - LGPL
36491  * <script type="text/javascript">
36492  */
36493  
36494
36495
36496 /**
36497  * @class Roo.SplitLayoutRegion
36498  * @extends Roo.LayoutRegion
36499  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36500  */
36501 Roo.bootstrap.layout.Split = function(config){
36502     this.cursor = config.cursor;
36503     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36504 };
36505
36506 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36507 {
36508     splitTip : "Drag to resize.",
36509     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36510     useSplitTips : false,
36511
36512     applyConfig : function(config){
36513         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36514     },
36515     
36516     onRender : function(ctr,pos) {
36517         
36518         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36519         if(!this.config.split){
36520             return;
36521         }
36522         if(!this.split){
36523             
36524             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36525                             tag: "div",
36526                             id: this.el.id + "-split",
36527                             cls: "roo-layout-split roo-layout-split-"+this.position,
36528                             html: "&#160;"
36529             });
36530             /** The SplitBar for this region 
36531             * @type Roo.SplitBar */
36532             // does not exist yet...
36533             Roo.log([this.position, this.orientation]);
36534             
36535             this.split = new Roo.bootstrap.SplitBar({
36536                 dragElement : splitEl,
36537                 resizingElement: this.el,
36538                 orientation : this.orientation
36539             });
36540             
36541             this.split.on("moved", this.onSplitMove, this);
36542             this.split.useShim = this.config.useShim === true;
36543             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36544             if(this.useSplitTips){
36545                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36546             }
36547             //if(config.collapsible){
36548             //    this.split.el.on("dblclick", this.collapse,  this);
36549             //}
36550         }
36551         if(typeof this.config.minSize != "undefined"){
36552             this.split.minSize = this.config.minSize;
36553         }
36554         if(typeof this.config.maxSize != "undefined"){
36555             this.split.maxSize = this.config.maxSize;
36556         }
36557         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36558             this.hideSplitter();
36559         }
36560         
36561     },
36562
36563     getHMaxSize : function(){
36564          var cmax = this.config.maxSize || 10000;
36565          var center = this.mgr.getRegion("center");
36566          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36567     },
36568
36569     getVMaxSize : function(){
36570          var cmax = this.config.maxSize || 10000;
36571          var center = this.mgr.getRegion("center");
36572          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36573     },
36574
36575     onSplitMove : function(split, newSize){
36576         this.fireEvent("resized", this, newSize);
36577     },
36578     
36579     /** 
36580      * Returns the {@link Roo.SplitBar} for this region.
36581      * @return {Roo.SplitBar}
36582      */
36583     getSplitBar : function(){
36584         return this.split;
36585     },
36586     
36587     hide : function(){
36588         this.hideSplitter();
36589         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36590     },
36591
36592     hideSplitter : function(){
36593         if(this.split){
36594             this.split.el.setLocation(-2000,-2000);
36595             this.split.el.hide();
36596         }
36597     },
36598
36599     show : function(){
36600         if(this.split){
36601             this.split.el.show();
36602         }
36603         Roo.bootstrap.layout.Split.superclass.show.call(this);
36604     },
36605     
36606     beforeSlide: function(){
36607         if(Roo.isGecko){// firefox overflow auto bug workaround
36608             this.bodyEl.clip();
36609             if(this.tabs) {
36610                 this.tabs.bodyEl.clip();
36611             }
36612             if(this.activePanel){
36613                 this.activePanel.getEl().clip();
36614                 
36615                 if(this.activePanel.beforeSlide){
36616                     this.activePanel.beforeSlide();
36617                 }
36618             }
36619         }
36620     },
36621     
36622     afterSlide : function(){
36623         if(Roo.isGecko){// firefox overflow auto bug workaround
36624             this.bodyEl.unclip();
36625             if(this.tabs) {
36626                 this.tabs.bodyEl.unclip();
36627             }
36628             if(this.activePanel){
36629                 this.activePanel.getEl().unclip();
36630                 if(this.activePanel.afterSlide){
36631                     this.activePanel.afterSlide();
36632                 }
36633             }
36634         }
36635     },
36636
36637     initAutoHide : function(){
36638         if(this.autoHide !== false){
36639             if(!this.autoHideHd){
36640                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36641                 this.autoHideHd = {
36642                     "mouseout": function(e){
36643                         if(!e.within(this.el, true)){
36644                             st.delay(500);
36645                         }
36646                     },
36647                     "mouseover" : function(e){
36648                         st.cancel();
36649                     },
36650                     scope : this
36651                 };
36652             }
36653             this.el.on(this.autoHideHd);
36654         }
36655     },
36656
36657     clearAutoHide : function(){
36658         if(this.autoHide !== false){
36659             this.el.un("mouseout", this.autoHideHd.mouseout);
36660             this.el.un("mouseover", this.autoHideHd.mouseover);
36661         }
36662     },
36663
36664     clearMonitor : function(){
36665         Roo.get(document).un("click", this.slideInIf, this);
36666     },
36667
36668     // these names are backwards but not changed for compat
36669     slideOut : function(){
36670         if(this.isSlid || this.el.hasActiveFx()){
36671             return;
36672         }
36673         this.isSlid = true;
36674         if(this.collapseBtn){
36675             this.collapseBtn.hide();
36676         }
36677         this.closeBtnState = this.closeBtn.getStyle('display');
36678         this.closeBtn.hide();
36679         if(this.stickBtn){
36680             this.stickBtn.show();
36681         }
36682         this.el.show();
36683         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36684         this.beforeSlide();
36685         this.el.setStyle("z-index", 10001);
36686         this.el.slideIn(this.getSlideAnchor(), {
36687             callback: function(){
36688                 this.afterSlide();
36689                 this.initAutoHide();
36690                 Roo.get(document).on("click", this.slideInIf, this);
36691                 this.fireEvent("slideshow", this);
36692             },
36693             scope: this,
36694             block: true
36695         });
36696     },
36697
36698     afterSlideIn : function(){
36699         this.clearAutoHide();
36700         this.isSlid = false;
36701         this.clearMonitor();
36702         this.el.setStyle("z-index", "");
36703         if(this.collapseBtn){
36704             this.collapseBtn.show();
36705         }
36706         this.closeBtn.setStyle('display', this.closeBtnState);
36707         if(this.stickBtn){
36708             this.stickBtn.hide();
36709         }
36710         this.fireEvent("slidehide", this);
36711     },
36712
36713     slideIn : function(cb){
36714         if(!this.isSlid || this.el.hasActiveFx()){
36715             Roo.callback(cb);
36716             return;
36717         }
36718         this.isSlid = false;
36719         this.beforeSlide();
36720         this.el.slideOut(this.getSlideAnchor(), {
36721             callback: function(){
36722                 this.el.setLeftTop(-10000, -10000);
36723                 this.afterSlide();
36724                 this.afterSlideIn();
36725                 Roo.callback(cb);
36726             },
36727             scope: this,
36728             block: true
36729         });
36730     },
36731     
36732     slideInIf : function(e){
36733         if(!e.within(this.el)){
36734             this.slideIn();
36735         }
36736     },
36737
36738     animateCollapse : function(){
36739         this.beforeSlide();
36740         this.el.setStyle("z-index", 20000);
36741         var anchor = this.getSlideAnchor();
36742         this.el.slideOut(anchor, {
36743             callback : function(){
36744                 this.el.setStyle("z-index", "");
36745                 this.collapsedEl.slideIn(anchor, {duration:.3});
36746                 this.afterSlide();
36747                 this.el.setLocation(-10000,-10000);
36748                 this.el.hide();
36749                 this.fireEvent("collapsed", this);
36750             },
36751             scope: this,
36752             block: true
36753         });
36754     },
36755
36756     animateExpand : function(){
36757         this.beforeSlide();
36758         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36759         this.el.setStyle("z-index", 20000);
36760         this.collapsedEl.hide({
36761             duration:.1
36762         });
36763         this.el.slideIn(this.getSlideAnchor(), {
36764             callback : function(){
36765                 this.el.setStyle("z-index", "");
36766                 this.afterSlide();
36767                 if(this.split){
36768                     this.split.el.show();
36769                 }
36770                 this.fireEvent("invalidated", this);
36771                 this.fireEvent("expanded", this);
36772             },
36773             scope: this,
36774             block: true
36775         });
36776     },
36777
36778     anchors : {
36779         "west" : "left",
36780         "east" : "right",
36781         "north" : "top",
36782         "south" : "bottom"
36783     },
36784
36785     sanchors : {
36786         "west" : "l",
36787         "east" : "r",
36788         "north" : "t",
36789         "south" : "b"
36790     },
36791
36792     canchors : {
36793         "west" : "tl-tr",
36794         "east" : "tr-tl",
36795         "north" : "tl-bl",
36796         "south" : "bl-tl"
36797     },
36798
36799     getAnchor : function(){
36800         return this.anchors[this.position];
36801     },
36802
36803     getCollapseAnchor : function(){
36804         return this.canchors[this.position];
36805     },
36806
36807     getSlideAnchor : function(){
36808         return this.sanchors[this.position];
36809     },
36810
36811     getAlignAdj : function(){
36812         var cm = this.cmargins;
36813         switch(this.position){
36814             case "west":
36815                 return [0, 0];
36816             break;
36817             case "east":
36818                 return [0, 0];
36819             break;
36820             case "north":
36821                 return [0, 0];
36822             break;
36823             case "south":
36824                 return [0, 0];
36825             break;
36826         }
36827     },
36828
36829     getExpandAdj : function(){
36830         var c = this.collapsedEl, cm = this.cmargins;
36831         switch(this.position){
36832             case "west":
36833                 return [-(cm.right+c.getWidth()+cm.left), 0];
36834             break;
36835             case "east":
36836                 return [cm.right+c.getWidth()+cm.left, 0];
36837             break;
36838             case "north":
36839                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36840             break;
36841             case "south":
36842                 return [0, cm.top+cm.bottom+c.getHeight()];
36843             break;
36844         }
36845     }
36846 });/*
36847  * Based on:
36848  * Ext JS Library 1.1.1
36849  * Copyright(c) 2006-2007, Ext JS, LLC.
36850  *
36851  * Originally Released Under LGPL - original licence link has changed is not relivant.
36852  *
36853  * Fork - LGPL
36854  * <script type="text/javascript">
36855  */
36856 /*
36857  * These classes are private internal classes
36858  */
36859 Roo.bootstrap.layout.Center = function(config){
36860     config.region = "center";
36861     Roo.bootstrap.layout.Region.call(this, config);
36862     this.visible = true;
36863     this.minWidth = config.minWidth || 20;
36864     this.minHeight = config.minHeight || 20;
36865 };
36866
36867 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36868     hide : function(){
36869         // center panel can't be hidden
36870     },
36871     
36872     show : function(){
36873         // center panel can't be hidden
36874     },
36875     
36876     getMinWidth: function(){
36877         return this.minWidth;
36878     },
36879     
36880     getMinHeight: function(){
36881         return this.minHeight;
36882     }
36883 });
36884
36885
36886
36887
36888  
36889
36890
36891
36892
36893
36894 Roo.bootstrap.layout.North = function(config)
36895 {
36896     config.region = 'north';
36897     config.cursor = 'n-resize';
36898     
36899     Roo.bootstrap.layout.Split.call(this, config);
36900     
36901     
36902     if(this.split){
36903         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36904         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36905         this.split.el.addClass("roo-layout-split-v");
36906     }
36907     var size = config.initialSize || config.height;
36908     if(typeof size != "undefined"){
36909         this.el.setHeight(size);
36910     }
36911 };
36912 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36913 {
36914     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36915     
36916     
36917     
36918     getBox : function(){
36919         if(this.collapsed){
36920             return this.collapsedEl.getBox();
36921         }
36922         var box = this.el.getBox();
36923         if(this.split){
36924             box.height += this.split.el.getHeight();
36925         }
36926         return box;
36927     },
36928     
36929     updateBox : function(box){
36930         if(this.split && !this.collapsed){
36931             box.height -= this.split.el.getHeight();
36932             this.split.el.setLeft(box.x);
36933             this.split.el.setTop(box.y+box.height);
36934             this.split.el.setWidth(box.width);
36935         }
36936         if(this.collapsed){
36937             this.updateBody(box.width, null);
36938         }
36939         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36940     }
36941 });
36942
36943
36944
36945
36946
36947 Roo.bootstrap.layout.South = function(config){
36948     config.region = 'south';
36949     config.cursor = 's-resize';
36950     Roo.bootstrap.layout.Split.call(this, config);
36951     if(this.split){
36952         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36953         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36954         this.split.el.addClass("roo-layout-split-v");
36955     }
36956     var size = config.initialSize || config.height;
36957     if(typeof size != "undefined"){
36958         this.el.setHeight(size);
36959     }
36960 };
36961
36962 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36963     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36964     getBox : function(){
36965         if(this.collapsed){
36966             return this.collapsedEl.getBox();
36967         }
36968         var box = this.el.getBox();
36969         if(this.split){
36970             var sh = this.split.el.getHeight();
36971             box.height += sh;
36972             box.y -= sh;
36973         }
36974         return box;
36975     },
36976     
36977     updateBox : function(box){
36978         if(this.split && !this.collapsed){
36979             var sh = this.split.el.getHeight();
36980             box.height -= sh;
36981             box.y += sh;
36982             this.split.el.setLeft(box.x);
36983             this.split.el.setTop(box.y-sh);
36984             this.split.el.setWidth(box.width);
36985         }
36986         if(this.collapsed){
36987             this.updateBody(box.width, null);
36988         }
36989         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36990     }
36991 });
36992
36993 Roo.bootstrap.layout.East = function(config){
36994     config.region = "east";
36995     config.cursor = "e-resize";
36996     Roo.bootstrap.layout.Split.call(this, config);
36997     if(this.split){
36998         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36999         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37000         this.split.el.addClass("roo-layout-split-h");
37001     }
37002     var size = config.initialSize || config.width;
37003     if(typeof size != "undefined"){
37004         this.el.setWidth(size);
37005     }
37006 };
37007 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37008     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37009     getBox : function(){
37010         if(this.collapsed){
37011             return this.collapsedEl.getBox();
37012         }
37013         var box = this.el.getBox();
37014         if(this.split){
37015             var sw = this.split.el.getWidth();
37016             box.width += sw;
37017             box.x -= sw;
37018         }
37019         return box;
37020     },
37021
37022     updateBox : function(box){
37023         if(this.split && !this.collapsed){
37024             var sw = this.split.el.getWidth();
37025             box.width -= sw;
37026             this.split.el.setLeft(box.x);
37027             this.split.el.setTop(box.y);
37028             this.split.el.setHeight(box.height);
37029             box.x += sw;
37030         }
37031         if(this.collapsed){
37032             this.updateBody(null, box.height);
37033         }
37034         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37035     }
37036 });
37037
37038 Roo.bootstrap.layout.West = function(config){
37039     config.region = "west";
37040     config.cursor = "w-resize";
37041     
37042     Roo.bootstrap.layout.Split.call(this, config);
37043     if(this.split){
37044         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37045         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37046         this.split.el.addClass("roo-layout-split-h");
37047     }
37048     
37049 };
37050 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37051     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37052     
37053     onRender: function(ctr, pos)
37054     {
37055         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37056         var size = this.config.initialSize || this.config.width;
37057         if(typeof size != "undefined"){
37058             this.el.setWidth(size);
37059         }
37060     },
37061     
37062     getBox : function(){
37063         if(this.collapsed){
37064             return this.collapsedEl.getBox();
37065         }
37066         var box = this.el.getBox();
37067         if(this.split){
37068             box.width += this.split.el.getWidth();
37069         }
37070         return box;
37071     },
37072     
37073     updateBox : function(box){
37074         if(this.split && !this.collapsed){
37075             var sw = this.split.el.getWidth();
37076             box.width -= sw;
37077             this.split.el.setLeft(box.x+box.width);
37078             this.split.el.setTop(box.y);
37079             this.split.el.setHeight(box.height);
37080         }
37081         if(this.collapsed){
37082             this.updateBody(null, box.height);
37083         }
37084         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37085     }
37086 });
37087 Roo.namespace("Roo.bootstrap.panel");/*
37088  * Based on:
37089  * Ext JS Library 1.1.1
37090  * Copyright(c) 2006-2007, Ext JS, LLC.
37091  *
37092  * Originally Released Under LGPL - original licence link has changed is not relivant.
37093  *
37094  * Fork - LGPL
37095  * <script type="text/javascript">
37096  */
37097 /**
37098  * @class Roo.ContentPanel
37099  * @extends Roo.util.Observable
37100  * A basic ContentPanel element.
37101  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37102  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37103  * @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
37104  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37105  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37106  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37107  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37108  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37109  * @cfg {String} title          The title for this panel
37110  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37111  * @cfg {String} url            Calls {@link #setUrl} with this value
37112  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37113  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37114  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37115  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37116  * @cfg {Boolean} badges render the badges
37117
37118  * @constructor
37119  * Create a new ContentPanel.
37120  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37121  * @param {String/Object} config A string to set only the title or a config object
37122  * @param {String} content (optional) Set the HTML content for this panel
37123  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37124  */
37125 Roo.bootstrap.panel.Content = function( config){
37126     
37127     this.tpl = config.tpl || false;
37128     
37129     var el = config.el;
37130     var content = config.content;
37131
37132     if(config.autoCreate){ // xtype is available if this is called from factory
37133         el = Roo.id();
37134     }
37135     this.el = Roo.get(el);
37136     if(!this.el && config && config.autoCreate){
37137         if(typeof config.autoCreate == "object"){
37138             if(!config.autoCreate.id){
37139                 config.autoCreate.id = config.id||el;
37140             }
37141             this.el = Roo.DomHelper.append(document.body,
37142                         config.autoCreate, true);
37143         }else{
37144             var elcfg =  {   tag: "div",
37145                             cls: "roo-layout-inactive-content",
37146                             id: config.id||el
37147                             };
37148             if (config.html) {
37149                 elcfg.html = config.html;
37150                 
37151             }
37152                         
37153             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37154         }
37155     } 
37156     this.closable = false;
37157     this.loaded = false;
37158     this.active = false;
37159    
37160       
37161     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37162         
37163         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37164         
37165         this.wrapEl = this.el; //this.el.wrap();
37166         var ti = [];
37167         if (config.toolbar.items) {
37168             ti = config.toolbar.items ;
37169             delete config.toolbar.items ;
37170         }
37171         
37172         var nitems = [];
37173         this.toolbar.render(this.wrapEl, 'before');
37174         for(var i =0;i < ti.length;i++) {
37175           //  Roo.log(['add child', items[i]]);
37176             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37177         }
37178         this.toolbar.items = nitems;
37179         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37180         delete config.toolbar;
37181         
37182     }
37183     /*
37184     // xtype created footer. - not sure if will work as we normally have to render first..
37185     if (this.footer && !this.footer.el && this.footer.xtype) {
37186         if (!this.wrapEl) {
37187             this.wrapEl = this.el.wrap();
37188         }
37189     
37190         this.footer.container = this.wrapEl.createChild();
37191          
37192         this.footer = Roo.factory(this.footer, Roo);
37193         
37194     }
37195     */
37196     
37197      if(typeof config == "string"){
37198         this.title = config;
37199     }else{
37200         Roo.apply(this, config);
37201     }
37202     
37203     if(this.resizeEl){
37204         this.resizeEl = Roo.get(this.resizeEl, true);
37205     }else{
37206         this.resizeEl = this.el;
37207     }
37208     // handle view.xtype
37209     
37210  
37211     
37212     
37213     this.addEvents({
37214         /**
37215          * @event activate
37216          * Fires when this panel is activated. 
37217          * @param {Roo.ContentPanel} this
37218          */
37219         "activate" : true,
37220         /**
37221          * @event deactivate
37222          * Fires when this panel is activated. 
37223          * @param {Roo.ContentPanel} this
37224          */
37225         "deactivate" : true,
37226
37227         /**
37228          * @event resize
37229          * Fires when this panel is resized if fitToFrame is true.
37230          * @param {Roo.ContentPanel} this
37231          * @param {Number} width The width after any component adjustments
37232          * @param {Number} height The height after any component adjustments
37233          */
37234         "resize" : true,
37235         
37236          /**
37237          * @event render
37238          * Fires when this tab is created
37239          * @param {Roo.ContentPanel} this
37240          */
37241         "render" : true
37242         
37243         
37244         
37245     });
37246     
37247
37248     
37249     
37250     if(this.autoScroll){
37251         this.resizeEl.setStyle("overflow", "auto");
37252     } else {
37253         // fix randome scrolling
37254         //this.el.on('scroll', function() {
37255         //    Roo.log('fix random scolling');
37256         //    this.scrollTo('top',0); 
37257         //});
37258     }
37259     content = content || this.content;
37260     if(content){
37261         this.setContent(content);
37262     }
37263     if(config && config.url){
37264         this.setUrl(this.url, this.params, this.loadOnce);
37265     }
37266     
37267     
37268     
37269     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37270     
37271     if (this.view && typeof(this.view.xtype) != 'undefined') {
37272         this.view.el = this.el.appendChild(document.createElement("div"));
37273         this.view = Roo.factory(this.view); 
37274         this.view.render  &&  this.view.render(false, '');  
37275     }
37276     
37277     
37278     this.fireEvent('render', this);
37279 };
37280
37281 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37282     
37283     tabTip : '',
37284     
37285     setRegion : function(region){
37286         this.region = region;
37287         this.setActiveClass(region && !this.background);
37288     },
37289     
37290     
37291     setActiveClass: function(state)
37292     {
37293         if(state){
37294            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37295            this.el.setStyle('position','relative');
37296         }else{
37297            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37298            this.el.setStyle('position', 'absolute');
37299         } 
37300     },
37301     
37302     /**
37303      * Returns the toolbar for this Panel if one was configured. 
37304      * @return {Roo.Toolbar} 
37305      */
37306     getToolbar : function(){
37307         return this.toolbar;
37308     },
37309     
37310     setActiveState : function(active)
37311     {
37312         this.active = active;
37313         this.setActiveClass(active);
37314         if(!active){
37315             if(this.fireEvent("deactivate", this) === false){
37316                 return false;
37317             }
37318             return true;
37319         }
37320         this.fireEvent("activate", this);
37321         return true;
37322     },
37323     /**
37324      * Updates this panel's element
37325      * @param {String} content The new content
37326      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37327     */
37328     setContent : function(content, loadScripts){
37329         this.el.update(content, loadScripts);
37330     },
37331
37332     ignoreResize : function(w, h){
37333         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37334             return true;
37335         }else{
37336             this.lastSize = {width: w, height: h};
37337             return false;
37338         }
37339     },
37340     /**
37341      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37342      * @return {Roo.UpdateManager} The UpdateManager
37343      */
37344     getUpdateManager : function(){
37345         return this.el.getUpdateManager();
37346     },
37347      /**
37348      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37349      * @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:
37350 <pre><code>
37351 panel.load({
37352     url: "your-url.php",
37353     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37354     callback: yourFunction,
37355     scope: yourObject, //(optional scope)
37356     discardUrl: false,
37357     nocache: false,
37358     text: "Loading...",
37359     timeout: 30,
37360     scripts: false
37361 });
37362 </code></pre>
37363      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37364      * 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.
37365      * @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}
37366      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37367      * @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.
37368      * @return {Roo.ContentPanel} this
37369      */
37370     load : function(){
37371         var um = this.el.getUpdateManager();
37372         um.update.apply(um, arguments);
37373         return this;
37374     },
37375
37376
37377     /**
37378      * 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.
37379      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37380      * @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)
37381      * @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)
37382      * @return {Roo.UpdateManager} The UpdateManager
37383      */
37384     setUrl : function(url, params, loadOnce){
37385         if(this.refreshDelegate){
37386             this.removeListener("activate", this.refreshDelegate);
37387         }
37388         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37389         this.on("activate", this.refreshDelegate);
37390         return this.el.getUpdateManager();
37391     },
37392     
37393     _handleRefresh : function(url, params, loadOnce){
37394         if(!loadOnce || !this.loaded){
37395             var updater = this.el.getUpdateManager();
37396             updater.update(url, params, this._setLoaded.createDelegate(this));
37397         }
37398     },
37399     
37400     _setLoaded : function(){
37401         this.loaded = true;
37402     }, 
37403     
37404     /**
37405      * Returns this panel's id
37406      * @return {String} 
37407      */
37408     getId : function(){
37409         return this.el.id;
37410     },
37411     
37412     /** 
37413      * Returns this panel's element - used by regiosn to add.
37414      * @return {Roo.Element} 
37415      */
37416     getEl : function(){
37417         return this.wrapEl || this.el;
37418     },
37419     
37420    
37421     
37422     adjustForComponents : function(width, height)
37423     {
37424         //Roo.log('adjustForComponents ');
37425         if(this.resizeEl != this.el){
37426             width -= this.el.getFrameWidth('lr');
37427             height -= this.el.getFrameWidth('tb');
37428         }
37429         if(this.toolbar){
37430             var te = this.toolbar.getEl();
37431             te.setWidth(width);
37432             height -= te.getHeight();
37433         }
37434         if(this.footer){
37435             var te = this.footer.getEl();
37436             te.setWidth(width);
37437             height -= te.getHeight();
37438         }
37439         
37440         
37441         if(this.adjustments){
37442             width += this.adjustments[0];
37443             height += this.adjustments[1];
37444         }
37445         return {"width": width, "height": height};
37446     },
37447     
37448     setSize : function(width, height){
37449         if(this.fitToFrame && !this.ignoreResize(width, height)){
37450             if(this.fitContainer && this.resizeEl != this.el){
37451                 this.el.setSize(width, height);
37452             }
37453             var size = this.adjustForComponents(width, height);
37454             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37455             this.fireEvent('resize', this, size.width, size.height);
37456         }
37457     },
37458     
37459     /**
37460      * Returns this panel's title
37461      * @return {String} 
37462      */
37463     getTitle : function(){
37464         
37465         if (typeof(this.title) != 'object') {
37466             return this.title;
37467         }
37468         
37469         var t = '';
37470         for (var k in this.title) {
37471             if (!this.title.hasOwnProperty(k)) {
37472                 continue;
37473             }
37474             
37475             if (k.indexOf('-') >= 0) {
37476                 var s = k.split('-');
37477                 for (var i = 0; i<s.length; i++) {
37478                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37479                 }
37480             } else {
37481                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37482             }
37483         }
37484         return t;
37485     },
37486     
37487     /**
37488      * Set this panel's title
37489      * @param {String} title
37490      */
37491     setTitle : function(title){
37492         this.title = title;
37493         if(this.region){
37494             this.region.updatePanelTitle(this, title);
37495         }
37496     },
37497     
37498     /**
37499      * Returns true is this panel was configured to be closable
37500      * @return {Boolean} 
37501      */
37502     isClosable : function(){
37503         return this.closable;
37504     },
37505     
37506     beforeSlide : function(){
37507         this.el.clip();
37508         this.resizeEl.clip();
37509     },
37510     
37511     afterSlide : function(){
37512         this.el.unclip();
37513         this.resizeEl.unclip();
37514     },
37515     
37516     /**
37517      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37518      *   Will fail silently if the {@link #setUrl} method has not been called.
37519      *   This does not activate the panel, just updates its content.
37520      */
37521     refresh : function(){
37522         if(this.refreshDelegate){
37523            this.loaded = false;
37524            this.refreshDelegate();
37525         }
37526     },
37527     
37528     /**
37529      * Destroys this panel
37530      */
37531     destroy : function(){
37532         this.el.removeAllListeners();
37533         var tempEl = document.createElement("span");
37534         tempEl.appendChild(this.el.dom);
37535         tempEl.innerHTML = "";
37536         this.el.remove();
37537         this.el = null;
37538     },
37539     
37540     /**
37541      * form - if the content panel contains a form - this is a reference to it.
37542      * @type {Roo.form.Form}
37543      */
37544     form : false,
37545     /**
37546      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37547      *    This contains a reference to it.
37548      * @type {Roo.View}
37549      */
37550     view : false,
37551     
37552       /**
37553      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37554      * <pre><code>
37555
37556 layout.addxtype({
37557        xtype : 'Form',
37558        items: [ .... ]
37559    }
37560 );
37561
37562 </code></pre>
37563      * @param {Object} cfg Xtype definition of item to add.
37564      */
37565     
37566     
37567     getChildContainer: function () {
37568         return this.getEl();
37569     }
37570     
37571     
37572     /*
37573         var  ret = new Roo.factory(cfg);
37574         return ret;
37575         
37576         
37577         // add form..
37578         if (cfg.xtype.match(/^Form$/)) {
37579             
37580             var el;
37581             //if (this.footer) {
37582             //    el = this.footer.container.insertSibling(false, 'before');
37583             //} else {
37584                 el = this.el.createChild();
37585             //}
37586
37587             this.form = new  Roo.form.Form(cfg);
37588             
37589             
37590             if ( this.form.allItems.length) {
37591                 this.form.render(el.dom);
37592             }
37593             return this.form;
37594         }
37595         // should only have one of theses..
37596         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37597             // views.. should not be just added - used named prop 'view''
37598             
37599             cfg.el = this.el.appendChild(document.createElement("div"));
37600             // factory?
37601             
37602             var ret = new Roo.factory(cfg);
37603              
37604              ret.render && ret.render(false, ''); // render blank..
37605             this.view = ret;
37606             return ret;
37607         }
37608         return false;
37609     }
37610     \*/
37611 });
37612  
37613 /**
37614  * @class Roo.bootstrap.panel.Grid
37615  * @extends Roo.bootstrap.panel.Content
37616  * @constructor
37617  * Create a new GridPanel.
37618  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37619  * @param {Object} config A the config object
37620   
37621  */
37622
37623
37624
37625 Roo.bootstrap.panel.Grid = function(config)
37626 {
37627     
37628       
37629     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37630         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37631
37632     config.el = this.wrapper;
37633     //this.el = this.wrapper;
37634     
37635       if (config.container) {
37636         // ctor'ed from a Border/panel.grid
37637         
37638         
37639         this.wrapper.setStyle("overflow", "hidden");
37640         this.wrapper.addClass('roo-grid-container');
37641
37642     }
37643     
37644     
37645     if(config.toolbar){
37646         var tool_el = this.wrapper.createChild();    
37647         this.toolbar = Roo.factory(config.toolbar);
37648         var ti = [];
37649         if (config.toolbar.items) {
37650             ti = config.toolbar.items ;
37651             delete config.toolbar.items ;
37652         }
37653         
37654         var nitems = [];
37655         this.toolbar.render(tool_el);
37656         for(var i =0;i < ti.length;i++) {
37657           //  Roo.log(['add child', items[i]]);
37658             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37659         }
37660         this.toolbar.items = nitems;
37661         
37662         delete config.toolbar;
37663     }
37664     
37665     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37666     config.grid.scrollBody = true;;
37667     config.grid.monitorWindowResize = false; // turn off autosizing
37668     config.grid.autoHeight = false;
37669     config.grid.autoWidth = false;
37670     
37671     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37672     
37673     if (config.background) {
37674         // render grid on panel activation (if panel background)
37675         this.on('activate', function(gp) {
37676             if (!gp.grid.rendered) {
37677                 gp.grid.render(this.wrapper);
37678                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37679             }
37680         });
37681             
37682     } else {
37683         this.grid.render(this.wrapper);
37684         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37685
37686     }
37687     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37688     // ??? needed ??? config.el = this.wrapper;
37689     
37690     
37691     
37692   
37693     // xtype created footer. - not sure if will work as we normally have to render first..
37694     if (this.footer && !this.footer.el && this.footer.xtype) {
37695         
37696         var ctr = this.grid.getView().getFooterPanel(true);
37697         this.footer.dataSource = this.grid.dataSource;
37698         this.footer = Roo.factory(this.footer, Roo);
37699         this.footer.render(ctr);
37700         
37701     }
37702     
37703     
37704     
37705     
37706      
37707 };
37708
37709 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37710     getId : function(){
37711         return this.grid.id;
37712     },
37713     
37714     /**
37715      * Returns the grid for this panel
37716      * @return {Roo.bootstrap.Table} 
37717      */
37718     getGrid : function(){
37719         return this.grid;    
37720     },
37721     
37722     setSize : function(width, height){
37723         if(!this.ignoreResize(width, height)){
37724             var grid = this.grid;
37725             var size = this.adjustForComponents(width, height);
37726             var gridel = grid.getGridEl();
37727             gridel.setSize(size.width, size.height);
37728             /*
37729             var thd = grid.getGridEl().select('thead',true).first();
37730             var tbd = grid.getGridEl().select('tbody', true).first();
37731             if (tbd) {
37732                 tbd.setSize(width, height - thd.getHeight());
37733             }
37734             */
37735             grid.autoSize();
37736         }
37737     },
37738      
37739     
37740     
37741     beforeSlide : function(){
37742         this.grid.getView().scroller.clip();
37743     },
37744     
37745     afterSlide : function(){
37746         this.grid.getView().scroller.unclip();
37747     },
37748     
37749     destroy : function(){
37750         this.grid.destroy();
37751         delete this.grid;
37752         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37753     }
37754 });
37755
37756 /**
37757  * @class Roo.bootstrap.panel.Nest
37758  * @extends Roo.bootstrap.panel.Content
37759  * @constructor
37760  * Create a new Panel, that can contain a layout.Border.
37761  * 
37762  * 
37763  * @param {Roo.BorderLayout} layout The layout for this panel
37764  * @param {String/Object} config A string to set only the title or a config object
37765  */
37766 Roo.bootstrap.panel.Nest = function(config)
37767 {
37768     // construct with only one argument..
37769     /* FIXME - implement nicer consturctors
37770     if (layout.layout) {
37771         config = layout;
37772         layout = config.layout;
37773         delete config.layout;
37774     }
37775     if (layout.xtype && !layout.getEl) {
37776         // then layout needs constructing..
37777         layout = Roo.factory(layout, Roo);
37778     }
37779     */
37780     
37781     config.el =  config.layout.getEl();
37782     
37783     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37784     
37785     config.layout.monitorWindowResize = false; // turn off autosizing
37786     this.layout = config.layout;
37787     this.layout.getEl().addClass("roo-layout-nested-layout");
37788     
37789     
37790     
37791     
37792 };
37793
37794 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37795
37796     setSize : function(width, height){
37797         if(!this.ignoreResize(width, height)){
37798             var size = this.adjustForComponents(width, height);
37799             var el = this.layout.getEl();
37800             if (size.height < 1) {
37801                 el.setWidth(size.width);   
37802             } else {
37803                 el.setSize(size.width, size.height);
37804             }
37805             var touch = el.dom.offsetWidth;
37806             this.layout.layout();
37807             // ie requires a double layout on the first pass
37808             if(Roo.isIE && !this.initialized){
37809                 this.initialized = true;
37810                 this.layout.layout();
37811             }
37812         }
37813     },
37814     
37815     // activate all subpanels if not currently active..
37816     
37817     setActiveState : function(active){
37818         this.active = active;
37819         this.setActiveClass(active);
37820         
37821         if(!active){
37822             this.fireEvent("deactivate", this);
37823             return;
37824         }
37825         
37826         this.fireEvent("activate", this);
37827         // not sure if this should happen before or after..
37828         if (!this.layout) {
37829             return; // should not happen..
37830         }
37831         var reg = false;
37832         for (var r in this.layout.regions) {
37833             reg = this.layout.getRegion(r);
37834             if (reg.getActivePanel()) {
37835                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37836                 reg.setActivePanel(reg.getActivePanel());
37837                 continue;
37838             }
37839             if (!reg.panels.length) {
37840                 continue;
37841             }
37842             reg.showPanel(reg.getPanel(0));
37843         }
37844         
37845         
37846         
37847         
37848     },
37849     
37850     /**
37851      * Returns the nested BorderLayout for this panel
37852      * @return {Roo.BorderLayout} 
37853      */
37854     getLayout : function(){
37855         return this.layout;
37856     },
37857     
37858      /**
37859      * Adds a xtype elements to the layout of the nested panel
37860      * <pre><code>
37861
37862 panel.addxtype({
37863        xtype : 'ContentPanel',
37864        region: 'west',
37865        items: [ .... ]
37866    }
37867 );
37868
37869 panel.addxtype({
37870         xtype : 'NestedLayoutPanel',
37871         region: 'west',
37872         layout: {
37873            center: { },
37874            west: { }   
37875         },
37876         items : [ ... list of content panels or nested layout panels.. ]
37877    }
37878 );
37879 </code></pre>
37880      * @param {Object} cfg Xtype definition of item to add.
37881      */
37882     addxtype : function(cfg) {
37883         return this.layout.addxtype(cfg);
37884     
37885     }
37886 });        /*
37887  * Based on:
37888  * Ext JS Library 1.1.1
37889  * Copyright(c) 2006-2007, Ext JS, LLC.
37890  *
37891  * Originally Released Under LGPL - original licence link has changed is not relivant.
37892  *
37893  * Fork - LGPL
37894  * <script type="text/javascript">
37895  */
37896 /**
37897  * @class Roo.TabPanel
37898  * @extends Roo.util.Observable
37899  * A lightweight tab container.
37900  * <br><br>
37901  * Usage:
37902  * <pre><code>
37903 // basic tabs 1, built from existing content
37904 var tabs = new Roo.TabPanel("tabs1");
37905 tabs.addTab("script", "View Script");
37906 tabs.addTab("markup", "View Markup");
37907 tabs.activate("script");
37908
37909 // more advanced tabs, built from javascript
37910 var jtabs = new Roo.TabPanel("jtabs");
37911 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37912
37913 // set up the UpdateManager
37914 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37915 var updater = tab2.getUpdateManager();
37916 updater.setDefaultUrl("ajax1.htm");
37917 tab2.on('activate', updater.refresh, updater, true);
37918
37919 // Use setUrl for Ajax loading
37920 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37921 tab3.setUrl("ajax2.htm", null, true);
37922
37923 // Disabled tab
37924 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37925 tab4.disable();
37926
37927 jtabs.activate("jtabs-1");
37928  * </code></pre>
37929  * @constructor
37930  * Create a new TabPanel.
37931  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37932  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37933  */
37934 Roo.bootstrap.panel.Tabs = function(config){
37935     /**
37936     * The container element for this TabPanel.
37937     * @type Roo.Element
37938     */
37939     this.el = Roo.get(config.el);
37940     delete config.el;
37941     if(config){
37942         if(typeof config == "boolean"){
37943             this.tabPosition = config ? "bottom" : "top";
37944         }else{
37945             Roo.apply(this, config);
37946         }
37947     }
37948     
37949     if(this.tabPosition == "bottom"){
37950         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37951         this.el.addClass("roo-tabs-bottom");
37952     }
37953     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37954     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37955     this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
37956     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37957     if(Roo.isIE){
37958         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37959     }
37960     if(this.tabPosition != "bottom"){
37961         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37962          * @type Roo.Element
37963          */
37964         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37965         this.el.addClass("roo-tabs-top");
37966     }
37967     this.items = [];
37968
37969     this.bodyEl.setStyle("position", "relative");
37970
37971     this.active = null;
37972     this.activateDelegate = this.activate.createDelegate(this);
37973
37974     this.addEvents({
37975         /**
37976          * @event tabchange
37977          * Fires when the active tab changes
37978          * @param {Roo.TabPanel} this
37979          * @param {Roo.TabPanelItem} activePanel The new active tab
37980          */
37981         "tabchange": true,
37982         /**
37983          * @event beforetabchange
37984          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37985          * @param {Roo.TabPanel} this
37986          * @param {Object} e Set cancel to true on this object to cancel the tab change
37987          * @param {Roo.TabPanelItem} tab The tab being changed to
37988          */
37989         "beforetabchange" : true
37990     });
37991
37992     Roo.EventManager.onWindowResize(this.onResize, this);
37993     this.cpad = this.el.getPadding("lr");
37994     this.hiddenCount = 0;
37995
37996
37997     // toolbar on the tabbar support...
37998     if (this.toolbar) {
37999         alert("no toolbar support yet");
38000         this.toolbar  = false;
38001         /*
38002         var tcfg = this.toolbar;
38003         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38004         this.toolbar = new Roo.Toolbar(tcfg);
38005         if (Roo.isSafari) {
38006             var tbl = tcfg.container.child('table', true);
38007             tbl.setAttribute('width', '100%');
38008         }
38009         */
38010         
38011     }
38012    
38013
38014
38015     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38016 };
38017
38018 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38019     /*
38020      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38021      */
38022     tabPosition : "top",
38023     /*
38024      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38025      */
38026     currentTabWidth : 0,
38027     /*
38028      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38029      */
38030     minTabWidth : 40,
38031     /*
38032      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38033      */
38034     maxTabWidth : 250,
38035     /*
38036      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38037      */
38038     preferredTabWidth : 175,
38039     /*
38040      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38041      */
38042     resizeTabs : false,
38043     /*
38044      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38045      */
38046     monitorResize : true,
38047     /*
38048      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38049      */
38050     toolbar : false,
38051
38052     /**
38053      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38054      * @param {String} id The id of the div to use <b>or create</b>
38055      * @param {String} text The text for the tab
38056      * @param {String} content (optional) Content to put in the TabPanelItem body
38057      * @param {Boolean} closable (optional) True to create a close icon on the tab
38058      * @return {Roo.TabPanelItem} The created TabPanelItem
38059      */
38060     addTab : function(id, text, content, closable, tpl)
38061     {
38062         var item = new Roo.bootstrap.panel.TabItem({
38063             panel: this,
38064             id : id,
38065             text : text,
38066             closable : closable,
38067             tpl : tpl
38068         });
38069         this.addTabItem(item);
38070         if(content){
38071             item.setContent(content);
38072         }
38073         return item;
38074     },
38075
38076     /**
38077      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38078      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38079      * @return {Roo.TabPanelItem}
38080      */
38081     getTab : function(id){
38082         return this.items[id];
38083     },
38084
38085     /**
38086      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38087      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38088      */
38089     hideTab : function(id){
38090         var t = this.items[id];
38091         if(!t.isHidden()){
38092            t.setHidden(true);
38093            this.hiddenCount++;
38094            this.autoSizeTabs();
38095         }
38096     },
38097
38098     /**
38099      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38100      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38101      */
38102     unhideTab : function(id){
38103         var t = this.items[id];
38104         if(t.isHidden()){
38105            t.setHidden(false);
38106            this.hiddenCount--;
38107            this.autoSizeTabs();
38108         }
38109     },
38110
38111     /**
38112      * Adds an existing {@link Roo.TabPanelItem}.
38113      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38114      */
38115     addTabItem : function(item)
38116     {
38117         this.items[item.id] = item;
38118         this.items.push(item);
38119         this.autoSizeTabs();
38120       //  if(this.resizeTabs){
38121     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38122   //         this.autoSizeTabs();
38123 //        }else{
38124 //            item.autoSize();
38125        // }
38126     },
38127
38128     /**
38129      * Removes a {@link Roo.TabPanelItem}.
38130      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38131      */
38132     removeTab : function(id){
38133         var items = this.items;
38134         var tab = items[id];
38135         if(!tab) { return; }
38136         var index = items.indexOf(tab);
38137         if(this.active == tab && items.length > 1){
38138             var newTab = this.getNextAvailable(index);
38139             if(newTab) {
38140                 newTab.activate();
38141             }
38142         }
38143         this.stripEl.dom.removeChild(tab.pnode.dom);
38144         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38145             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38146         }
38147         items.splice(index, 1);
38148         delete this.items[tab.id];
38149         tab.fireEvent("close", tab);
38150         tab.purgeListeners();
38151         this.autoSizeTabs();
38152     },
38153
38154     getNextAvailable : function(start){
38155         var items = this.items;
38156         var index = start;
38157         // look for a next tab that will slide over to
38158         // replace the one being removed
38159         while(index < items.length){
38160             var item = items[++index];
38161             if(item && !item.isHidden()){
38162                 return item;
38163             }
38164         }
38165         // if one isn't found select the previous tab (on the left)
38166         index = start;
38167         while(index >= 0){
38168             var item = items[--index];
38169             if(item && !item.isHidden()){
38170                 return item;
38171             }
38172         }
38173         return null;
38174     },
38175
38176     /**
38177      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38178      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38179      */
38180     disableTab : function(id){
38181         var tab = this.items[id];
38182         if(tab && this.active != tab){
38183             tab.disable();
38184         }
38185     },
38186
38187     /**
38188      * Enables a {@link Roo.TabPanelItem} that is disabled.
38189      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38190      */
38191     enableTab : function(id){
38192         var tab = this.items[id];
38193         tab.enable();
38194     },
38195
38196     /**
38197      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38198      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38199      * @return {Roo.TabPanelItem} The TabPanelItem.
38200      */
38201     activate : function(id)
38202     {
38203         var tab = this.items[id];
38204         if(!tab){
38205             return null;
38206         }
38207         if(tab == this.active || tab.disabled){
38208             return tab;
38209         }
38210         var e = {};
38211         this.fireEvent("beforetabchange", this, e, tab);
38212         if(e.cancel !== true && !tab.disabled){
38213             if(this.active){
38214                 this.active.hide();
38215             }
38216             this.active = this.items[id];
38217             this.active.show();
38218             this.fireEvent("tabchange", this, this.active);
38219         }
38220         return tab;
38221     },
38222
38223     /**
38224      * Gets the active {@link Roo.TabPanelItem}.
38225      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38226      */
38227     getActiveTab : function(){
38228         return this.active;
38229     },
38230
38231     /**
38232      * Updates the tab body element to fit the height of the container element
38233      * for overflow scrolling
38234      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38235      */
38236     syncHeight : function(targetHeight){
38237         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38238         var bm = this.bodyEl.getMargins();
38239         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38240         this.bodyEl.setHeight(newHeight);
38241         return newHeight;
38242     },
38243
38244     onResize : function(){
38245         if(this.monitorResize){
38246             this.autoSizeTabs();
38247         }
38248     },
38249
38250     /**
38251      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38252      */
38253     beginUpdate : function(){
38254         this.updating = true;
38255     },
38256
38257     /**
38258      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38259      */
38260     endUpdate : function(){
38261         this.updating = false;
38262         this.autoSizeTabs();
38263     },
38264
38265     /**
38266      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38267      */
38268     autoSizeTabs : function()
38269     {
38270         var count = this.items.length;
38271         var vcount = count - this.hiddenCount;
38272         
38273         if (vcount < 2) {
38274             this.stripEl.hide();
38275         } else {
38276             this.stripEl.show();
38277         }
38278         
38279         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38280             return;
38281         }
38282         
38283         
38284         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38285         var availWidth = Math.floor(w / vcount);
38286         var b = this.stripBody;
38287         if(b.getWidth() > w){
38288             var tabs = this.items;
38289             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38290             if(availWidth < this.minTabWidth){
38291                 /*if(!this.sleft){    // incomplete scrolling code
38292                     this.createScrollButtons();
38293                 }
38294                 this.showScroll();
38295                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38296             }
38297         }else{
38298             if(this.currentTabWidth < this.preferredTabWidth){
38299                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38300             }
38301         }
38302     },
38303
38304     /**
38305      * Returns the number of tabs in this TabPanel.
38306      * @return {Number}
38307      */
38308      getCount : function(){
38309          return this.items.length;
38310      },
38311
38312     /**
38313      * Resizes all the tabs to the passed width
38314      * @param {Number} The new width
38315      */
38316     setTabWidth : function(width){
38317         this.currentTabWidth = width;
38318         for(var i = 0, len = this.items.length; i < len; i++) {
38319                 if(!this.items[i].isHidden()) {
38320                 this.items[i].setWidth(width);
38321             }
38322         }
38323     },
38324
38325     /**
38326      * Destroys this TabPanel
38327      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38328      */
38329     destroy : function(removeEl){
38330         Roo.EventManager.removeResizeListener(this.onResize, this);
38331         for(var i = 0, len = this.items.length; i < len; i++){
38332             this.items[i].purgeListeners();
38333         }
38334         if(removeEl === true){
38335             this.el.update("");
38336             this.el.remove();
38337         }
38338     },
38339     
38340     createStrip : function(container)
38341     {
38342         var strip = document.createElement("nav");
38343         strip.className = Roo.bootstrap.version == 4 ?
38344             "navbar-light bg-light" : 
38345             "navbar navbar-default"; //"x-tabs-wrap";
38346         container.appendChild(strip);
38347         return strip;
38348     },
38349     
38350     createStripList : function(strip)
38351     {
38352         // div wrapper for retard IE
38353         // returns the "tr" element.
38354         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38355         //'<div class="x-tabs-strip-wrap">'+
38356           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38357           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38358         return strip.firstChild; //.firstChild.firstChild.firstChild;
38359     },
38360     createBody : function(container)
38361     {
38362         var body = document.createElement("div");
38363         Roo.id(body, "tab-body");
38364         //Roo.fly(body).addClass("x-tabs-body");
38365         Roo.fly(body).addClass("tab-content");
38366         container.appendChild(body);
38367         return body;
38368     },
38369     createItemBody :function(bodyEl, id){
38370         var body = Roo.getDom(id);
38371         if(!body){
38372             body = document.createElement("div");
38373             body.id = id;
38374         }
38375         //Roo.fly(body).addClass("x-tabs-item-body");
38376         Roo.fly(body).addClass("tab-pane");
38377          bodyEl.insertBefore(body, bodyEl.firstChild);
38378         return body;
38379     },
38380     /** @private */
38381     createStripElements :  function(stripEl, text, closable, tpl)
38382     {
38383         var td = document.createElement("li"); // was td..
38384         td.className = 'nav-item';
38385         
38386         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38387         
38388         
38389         stripEl.appendChild(td);
38390         /*if(closable){
38391             td.className = "x-tabs-closable";
38392             if(!this.closeTpl){
38393                 this.closeTpl = new Roo.Template(
38394                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38395                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38396                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38397                 );
38398             }
38399             var el = this.closeTpl.overwrite(td, {"text": text});
38400             var close = el.getElementsByTagName("div")[0];
38401             var inner = el.getElementsByTagName("em")[0];
38402             return {"el": el, "close": close, "inner": inner};
38403         } else {
38404         */
38405         // not sure what this is..
38406 //            if(!this.tabTpl){
38407                 //this.tabTpl = new Roo.Template(
38408                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38409                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38410                 //);
38411 //                this.tabTpl = new Roo.Template(
38412 //                   '<a href="#">' +
38413 //                   '<span unselectable="on"' +
38414 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38415 //                            ' >{text}</span></a>'
38416 //                );
38417 //                
38418 //            }
38419
38420
38421             var template = tpl || this.tabTpl || false;
38422             
38423             if(!template){
38424                 template =  new Roo.Template(
38425                         Roo.bootstrap.version == 4 ? 
38426                             (
38427                                 '<a class="nav-link" href="#" unselectable="on"' +
38428                                      (this.disableTooltips ? '' : ' title="{text}"') +
38429                                      ' >{text}</a>'
38430                             ) : (
38431                                 '<a class="nav-link" href="#">' +
38432                                 '<span unselectable="on"' +
38433                                          (this.disableTooltips ? '' : ' title="{text}"') +
38434                                     ' >{text}</span></a>'
38435                             )
38436                 );
38437             }
38438             
38439             switch (typeof(template)) {
38440                 case 'object' :
38441                     break;
38442                 case 'string' :
38443                     template = new Roo.Template(template);
38444                     break;
38445                 default :
38446                     break;
38447             }
38448             
38449             var el = template.overwrite(td, {"text": text});
38450             
38451             var inner = el.getElementsByTagName("span")[0];
38452             
38453             return {"el": el, "inner": inner};
38454             
38455     }
38456         
38457     
38458 });
38459
38460 /**
38461  * @class Roo.TabPanelItem
38462  * @extends Roo.util.Observable
38463  * Represents an individual item (tab plus body) in a TabPanel.
38464  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38465  * @param {String} id The id of this TabPanelItem
38466  * @param {String} text The text for the tab of this TabPanelItem
38467  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38468  */
38469 Roo.bootstrap.panel.TabItem = function(config){
38470     /**
38471      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38472      * @type Roo.TabPanel
38473      */
38474     this.tabPanel = config.panel;
38475     /**
38476      * The id for this TabPanelItem
38477      * @type String
38478      */
38479     this.id = config.id;
38480     /** @private */
38481     this.disabled = false;
38482     /** @private */
38483     this.text = config.text;
38484     /** @private */
38485     this.loaded = false;
38486     this.closable = config.closable;
38487
38488     /**
38489      * The body element for this TabPanelItem.
38490      * @type Roo.Element
38491      */
38492     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38493     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38494     this.bodyEl.setStyle("display", "block");
38495     this.bodyEl.setStyle("zoom", "1");
38496     //this.hideAction();
38497
38498     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38499     /** @private */
38500     this.el = Roo.get(els.el);
38501     this.inner = Roo.get(els.inner, true);
38502      this.textEl = Roo.bootstrap.version == 4 ?
38503         this.el : Roo.get(this.el.dom.firstChild, true);
38504
38505     this.linode = Roo.get(els.el.parentNode, true);
38506     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38507
38508     
38509 //    this.el.on("mousedown", this.onTabMouseDown, this);
38510     this.el.on("click", this.onTabClick, this);
38511     /** @private */
38512     if(config.closable){
38513         var c = Roo.get(els.close, true);
38514         c.dom.title = this.closeText;
38515         c.addClassOnOver("close-over");
38516         c.on("click", this.closeClick, this);
38517      }
38518
38519     this.addEvents({
38520          /**
38521          * @event activate
38522          * Fires when this tab becomes the active tab.
38523          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38524          * @param {Roo.TabPanelItem} this
38525          */
38526         "activate": true,
38527         /**
38528          * @event beforeclose
38529          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38530          * @param {Roo.TabPanelItem} this
38531          * @param {Object} e Set cancel to true on this object to cancel the close.
38532          */
38533         "beforeclose": true,
38534         /**
38535          * @event close
38536          * Fires when this tab is closed.
38537          * @param {Roo.TabPanelItem} this
38538          */
38539          "close": true,
38540         /**
38541          * @event deactivate
38542          * Fires when this tab is no longer the active tab.
38543          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38544          * @param {Roo.TabPanelItem} this
38545          */
38546          "deactivate" : true
38547     });
38548     this.hidden = false;
38549
38550     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38551 };
38552
38553 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38554            {
38555     purgeListeners : function(){
38556        Roo.util.Observable.prototype.purgeListeners.call(this);
38557        this.el.removeAllListeners();
38558     },
38559     /**
38560      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38561      */
38562     show : function(){
38563         this.status_node.addClass("active");
38564         this.showAction();
38565         if(Roo.isOpera){
38566             this.tabPanel.stripWrap.repaint();
38567         }
38568         this.fireEvent("activate", this.tabPanel, this);
38569     },
38570
38571     /**
38572      * Returns true if this tab is the active tab.
38573      * @return {Boolean}
38574      */
38575     isActive : function(){
38576         return this.tabPanel.getActiveTab() == this;
38577     },
38578
38579     /**
38580      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38581      */
38582     hide : function(){
38583         this.status_node.removeClass("active");
38584         this.hideAction();
38585         this.fireEvent("deactivate", this.tabPanel, this);
38586     },
38587
38588     hideAction : function(){
38589         this.bodyEl.hide();
38590         this.bodyEl.setStyle("position", "absolute");
38591         this.bodyEl.setLeft("-20000px");
38592         this.bodyEl.setTop("-20000px");
38593     },
38594
38595     showAction : function(){
38596         this.bodyEl.setStyle("position", "relative");
38597         this.bodyEl.setTop("");
38598         this.bodyEl.setLeft("");
38599         this.bodyEl.show();
38600     },
38601
38602     /**
38603      * Set the tooltip for the tab.
38604      * @param {String} tooltip The tab's tooltip
38605      */
38606     setTooltip : function(text){
38607         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38608             this.textEl.dom.qtip = text;
38609             this.textEl.dom.removeAttribute('title');
38610         }else{
38611             this.textEl.dom.title = text;
38612         }
38613     },
38614
38615     onTabClick : function(e){
38616         e.preventDefault();
38617         this.tabPanel.activate(this.id);
38618     },
38619
38620     onTabMouseDown : function(e){
38621         e.preventDefault();
38622         this.tabPanel.activate(this.id);
38623     },
38624 /*
38625     getWidth : function(){
38626         return this.inner.getWidth();
38627     },
38628
38629     setWidth : function(width){
38630         var iwidth = width - this.linode.getPadding("lr");
38631         this.inner.setWidth(iwidth);
38632         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38633         this.linode.setWidth(width);
38634     },
38635 */
38636     /**
38637      * Show or hide the tab
38638      * @param {Boolean} hidden True to hide or false to show.
38639      */
38640     setHidden : function(hidden){
38641         this.hidden = hidden;
38642         this.linode.setStyle("display", hidden ? "none" : "");
38643     },
38644
38645     /**
38646      * Returns true if this tab is "hidden"
38647      * @return {Boolean}
38648      */
38649     isHidden : function(){
38650         return this.hidden;
38651     },
38652
38653     /**
38654      * Returns the text for this tab
38655      * @return {String}
38656      */
38657     getText : function(){
38658         return this.text;
38659     },
38660     /*
38661     autoSize : function(){
38662         //this.el.beginMeasure();
38663         this.textEl.setWidth(1);
38664         /*
38665          *  #2804 [new] Tabs in Roojs
38666          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38667          */
38668         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38669         //this.el.endMeasure();
38670     //},
38671
38672     /**
38673      * Sets the text for the tab (Note: this also sets the tooltip text)
38674      * @param {String} text The tab's text and tooltip
38675      */
38676     setText : function(text){
38677         this.text = text;
38678         this.textEl.update(text);
38679         this.setTooltip(text);
38680         //if(!this.tabPanel.resizeTabs){
38681         //    this.autoSize();
38682         //}
38683     },
38684     /**
38685      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38686      */
38687     activate : function(){
38688         this.tabPanel.activate(this.id);
38689     },
38690
38691     /**
38692      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38693      */
38694     disable : function(){
38695         if(this.tabPanel.active != this){
38696             this.disabled = true;
38697             this.status_node.addClass("disabled");
38698         }
38699     },
38700
38701     /**
38702      * Enables this TabPanelItem if it was previously disabled.
38703      */
38704     enable : function(){
38705         this.disabled = false;
38706         this.status_node.removeClass("disabled");
38707     },
38708
38709     /**
38710      * Sets the content for this TabPanelItem.
38711      * @param {String} content The content
38712      * @param {Boolean} loadScripts true to look for and load scripts
38713      */
38714     setContent : function(content, loadScripts){
38715         this.bodyEl.update(content, loadScripts);
38716     },
38717
38718     /**
38719      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38720      * @return {Roo.UpdateManager} The UpdateManager
38721      */
38722     getUpdateManager : function(){
38723         return this.bodyEl.getUpdateManager();
38724     },
38725
38726     /**
38727      * Set a URL to be used to load the content for this TabPanelItem.
38728      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38729      * @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)
38730      * @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)
38731      * @return {Roo.UpdateManager} The UpdateManager
38732      */
38733     setUrl : function(url, params, loadOnce){
38734         if(this.refreshDelegate){
38735             this.un('activate', this.refreshDelegate);
38736         }
38737         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38738         this.on("activate", this.refreshDelegate);
38739         return this.bodyEl.getUpdateManager();
38740     },
38741
38742     /** @private */
38743     _handleRefresh : function(url, params, loadOnce){
38744         if(!loadOnce || !this.loaded){
38745             var updater = this.bodyEl.getUpdateManager();
38746             updater.update(url, params, this._setLoaded.createDelegate(this));
38747         }
38748     },
38749
38750     /**
38751      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38752      *   Will fail silently if the setUrl method has not been called.
38753      *   This does not activate the panel, just updates its content.
38754      */
38755     refresh : function(){
38756         if(this.refreshDelegate){
38757            this.loaded = false;
38758            this.refreshDelegate();
38759         }
38760     },
38761
38762     /** @private */
38763     _setLoaded : function(){
38764         this.loaded = true;
38765     },
38766
38767     /** @private */
38768     closeClick : function(e){
38769         var o = {};
38770         e.stopEvent();
38771         this.fireEvent("beforeclose", this, o);
38772         if(o.cancel !== true){
38773             this.tabPanel.removeTab(this.id);
38774         }
38775     },
38776     /**
38777      * The text displayed in the tooltip for the close icon.
38778      * @type String
38779      */
38780     closeText : "Close this tab"
38781 });
38782 /**
38783 *    This script refer to:
38784 *    Title: International Telephone Input
38785 *    Author: Jack O'Connor
38786 *    Code version:  v12.1.12
38787 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38788 **/
38789
38790 Roo.bootstrap.PhoneInputData = function() {
38791     var d = [
38792       [
38793         "Afghanistan (‫افغانستان‬‎)",
38794         "af",
38795         "93"
38796       ],
38797       [
38798         "Albania (Shqipëri)",
38799         "al",
38800         "355"
38801       ],
38802       [
38803         "Algeria (‫الجزائر‬‎)",
38804         "dz",
38805         "213"
38806       ],
38807       [
38808         "American Samoa",
38809         "as",
38810         "1684"
38811       ],
38812       [
38813         "Andorra",
38814         "ad",
38815         "376"
38816       ],
38817       [
38818         "Angola",
38819         "ao",
38820         "244"
38821       ],
38822       [
38823         "Anguilla",
38824         "ai",
38825         "1264"
38826       ],
38827       [
38828         "Antigua and Barbuda",
38829         "ag",
38830         "1268"
38831       ],
38832       [
38833         "Argentina",
38834         "ar",
38835         "54"
38836       ],
38837       [
38838         "Armenia (Հայաստան)",
38839         "am",
38840         "374"
38841       ],
38842       [
38843         "Aruba",
38844         "aw",
38845         "297"
38846       ],
38847       [
38848         "Australia",
38849         "au",
38850         "61",
38851         0
38852       ],
38853       [
38854         "Austria (Österreich)",
38855         "at",
38856         "43"
38857       ],
38858       [
38859         "Azerbaijan (Azərbaycan)",
38860         "az",
38861         "994"
38862       ],
38863       [
38864         "Bahamas",
38865         "bs",
38866         "1242"
38867       ],
38868       [
38869         "Bahrain (‫البحرين‬‎)",
38870         "bh",
38871         "973"
38872       ],
38873       [
38874         "Bangladesh (বাংলাদেশ)",
38875         "bd",
38876         "880"
38877       ],
38878       [
38879         "Barbados",
38880         "bb",
38881         "1246"
38882       ],
38883       [
38884         "Belarus (Беларусь)",
38885         "by",
38886         "375"
38887       ],
38888       [
38889         "Belgium (België)",
38890         "be",
38891         "32"
38892       ],
38893       [
38894         "Belize",
38895         "bz",
38896         "501"
38897       ],
38898       [
38899         "Benin (Bénin)",
38900         "bj",
38901         "229"
38902       ],
38903       [
38904         "Bermuda",
38905         "bm",
38906         "1441"
38907       ],
38908       [
38909         "Bhutan (འབྲུག)",
38910         "bt",
38911         "975"
38912       ],
38913       [
38914         "Bolivia",
38915         "bo",
38916         "591"
38917       ],
38918       [
38919         "Bosnia and Herzegovina (Босна и Херцеговина)",
38920         "ba",
38921         "387"
38922       ],
38923       [
38924         "Botswana",
38925         "bw",
38926         "267"
38927       ],
38928       [
38929         "Brazil (Brasil)",
38930         "br",
38931         "55"
38932       ],
38933       [
38934         "British Indian Ocean Territory",
38935         "io",
38936         "246"
38937       ],
38938       [
38939         "British Virgin Islands",
38940         "vg",
38941         "1284"
38942       ],
38943       [
38944         "Brunei",
38945         "bn",
38946         "673"
38947       ],
38948       [
38949         "Bulgaria (България)",
38950         "bg",
38951         "359"
38952       ],
38953       [
38954         "Burkina Faso",
38955         "bf",
38956         "226"
38957       ],
38958       [
38959         "Burundi (Uburundi)",
38960         "bi",
38961         "257"
38962       ],
38963       [
38964         "Cambodia (កម្ពុជា)",
38965         "kh",
38966         "855"
38967       ],
38968       [
38969         "Cameroon (Cameroun)",
38970         "cm",
38971         "237"
38972       ],
38973       [
38974         "Canada",
38975         "ca",
38976         "1",
38977         1,
38978         ["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"]
38979       ],
38980       [
38981         "Cape Verde (Kabu Verdi)",
38982         "cv",
38983         "238"
38984       ],
38985       [
38986         "Caribbean Netherlands",
38987         "bq",
38988         "599",
38989         1
38990       ],
38991       [
38992         "Cayman Islands",
38993         "ky",
38994         "1345"
38995       ],
38996       [
38997         "Central African Republic (République centrafricaine)",
38998         "cf",
38999         "236"
39000       ],
39001       [
39002         "Chad (Tchad)",
39003         "td",
39004         "235"
39005       ],
39006       [
39007         "Chile",
39008         "cl",
39009         "56"
39010       ],
39011       [
39012         "China (中国)",
39013         "cn",
39014         "86"
39015       ],
39016       [
39017         "Christmas Island",
39018         "cx",
39019         "61",
39020         2
39021       ],
39022       [
39023         "Cocos (Keeling) Islands",
39024         "cc",
39025         "61",
39026         1
39027       ],
39028       [
39029         "Colombia",
39030         "co",
39031         "57"
39032       ],
39033       [
39034         "Comoros (‫جزر القمر‬‎)",
39035         "km",
39036         "269"
39037       ],
39038       [
39039         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39040         "cd",
39041         "243"
39042       ],
39043       [
39044         "Congo (Republic) (Congo-Brazzaville)",
39045         "cg",
39046         "242"
39047       ],
39048       [
39049         "Cook Islands",
39050         "ck",
39051         "682"
39052       ],
39053       [
39054         "Costa Rica",
39055         "cr",
39056         "506"
39057       ],
39058       [
39059         "Côte d’Ivoire",
39060         "ci",
39061         "225"
39062       ],
39063       [
39064         "Croatia (Hrvatska)",
39065         "hr",
39066         "385"
39067       ],
39068       [
39069         "Cuba",
39070         "cu",
39071         "53"
39072       ],
39073       [
39074         "Curaçao",
39075         "cw",
39076         "599",
39077         0
39078       ],
39079       [
39080         "Cyprus (Κύπρος)",
39081         "cy",
39082         "357"
39083       ],
39084       [
39085         "Czech Republic (Česká republika)",
39086         "cz",
39087         "420"
39088       ],
39089       [
39090         "Denmark (Danmark)",
39091         "dk",
39092         "45"
39093       ],
39094       [
39095         "Djibouti",
39096         "dj",
39097         "253"
39098       ],
39099       [
39100         "Dominica",
39101         "dm",
39102         "1767"
39103       ],
39104       [
39105         "Dominican Republic (República Dominicana)",
39106         "do",
39107         "1",
39108         2,
39109         ["809", "829", "849"]
39110       ],
39111       [
39112         "Ecuador",
39113         "ec",
39114         "593"
39115       ],
39116       [
39117         "Egypt (‫مصر‬‎)",
39118         "eg",
39119         "20"
39120       ],
39121       [
39122         "El Salvador",
39123         "sv",
39124         "503"
39125       ],
39126       [
39127         "Equatorial Guinea (Guinea Ecuatorial)",
39128         "gq",
39129         "240"
39130       ],
39131       [
39132         "Eritrea",
39133         "er",
39134         "291"
39135       ],
39136       [
39137         "Estonia (Eesti)",
39138         "ee",
39139         "372"
39140       ],
39141       [
39142         "Ethiopia",
39143         "et",
39144         "251"
39145       ],
39146       [
39147         "Falkland Islands (Islas Malvinas)",
39148         "fk",
39149         "500"
39150       ],
39151       [
39152         "Faroe Islands (Føroyar)",
39153         "fo",
39154         "298"
39155       ],
39156       [
39157         "Fiji",
39158         "fj",
39159         "679"
39160       ],
39161       [
39162         "Finland (Suomi)",
39163         "fi",
39164         "358",
39165         0
39166       ],
39167       [
39168         "France",
39169         "fr",
39170         "33"
39171       ],
39172       [
39173         "French Guiana (Guyane française)",
39174         "gf",
39175         "594"
39176       ],
39177       [
39178         "French Polynesia (Polynésie française)",
39179         "pf",
39180         "689"
39181       ],
39182       [
39183         "Gabon",
39184         "ga",
39185         "241"
39186       ],
39187       [
39188         "Gambia",
39189         "gm",
39190         "220"
39191       ],
39192       [
39193         "Georgia (საქართველო)",
39194         "ge",
39195         "995"
39196       ],
39197       [
39198         "Germany (Deutschland)",
39199         "de",
39200         "49"
39201       ],
39202       [
39203         "Ghana (Gaana)",
39204         "gh",
39205         "233"
39206       ],
39207       [
39208         "Gibraltar",
39209         "gi",
39210         "350"
39211       ],
39212       [
39213         "Greece (Ελλάδα)",
39214         "gr",
39215         "30"
39216       ],
39217       [
39218         "Greenland (Kalaallit Nunaat)",
39219         "gl",
39220         "299"
39221       ],
39222       [
39223         "Grenada",
39224         "gd",
39225         "1473"
39226       ],
39227       [
39228         "Guadeloupe",
39229         "gp",
39230         "590",
39231         0
39232       ],
39233       [
39234         "Guam",
39235         "gu",
39236         "1671"
39237       ],
39238       [
39239         "Guatemala",
39240         "gt",
39241         "502"
39242       ],
39243       [
39244         "Guernsey",
39245         "gg",
39246         "44",
39247         1
39248       ],
39249       [
39250         "Guinea (Guinée)",
39251         "gn",
39252         "224"
39253       ],
39254       [
39255         "Guinea-Bissau (Guiné Bissau)",
39256         "gw",
39257         "245"
39258       ],
39259       [
39260         "Guyana",
39261         "gy",
39262         "592"
39263       ],
39264       [
39265         "Haiti",
39266         "ht",
39267         "509"
39268       ],
39269       [
39270         "Honduras",
39271         "hn",
39272         "504"
39273       ],
39274       [
39275         "Hong Kong (香港)",
39276         "hk",
39277         "852"
39278       ],
39279       [
39280         "Hungary (Magyarország)",
39281         "hu",
39282         "36"
39283       ],
39284       [
39285         "Iceland (Ísland)",
39286         "is",
39287         "354"
39288       ],
39289       [
39290         "India (भारत)",
39291         "in",
39292         "91"
39293       ],
39294       [
39295         "Indonesia",
39296         "id",
39297         "62"
39298       ],
39299       [
39300         "Iran (‫ایران‬‎)",
39301         "ir",
39302         "98"
39303       ],
39304       [
39305         "Iraq (‫العراق‬‎)",
39306         "iq",
39307         "964"
39308       ],
39309       [
39310         "Ireland",
39311         "ie",
39312         "353"
39313       ],
39314       [
39315         "Isle of Man",
39316         "im",
39317         "44",
39318         2
39319       ],
39320       [
39321         "Israel (‫ישראל‬‎)",
39322         "il",
39323         "972"
39324       ],
39325       [
39326         "Italy (Italia)",
39327         "it",
39328         "39",
39329         0
39330       ],
39331       [
39332         "Jamaica",
39333         "jm",
39334         "1876"
39335       ],
39336       [
39337         "Japan (日本)",
39338         "jp",
39339         "81"
39340       ],
39341       [
39342         "Jersey",
39343         "je",
39344         "44",
39345         3
39346       ],
39347       [
39348         "Jordan (‫الأردن‬‎)",
39349         "jo",
39350         "962"
39351       ],
39352       [
39353         "Kazakhstan (Казахстан)",
39354         "kz",
39355         "7",
39356         1
39357       ],
39358       [
39359         "Kenya",
39360         "ke",
39361         "254"
39362       ],
39363       [
39364         "Kiribati",
39365         "ki",
39366         "686"
39367       ],
39368       [
39369         "Kosovo",
39370         "xk",
39371         "383"
39372       ],
39373       [
39374         "Kuwait (‫الكويت‬‎)",
39375         "kw",
39376         "965"
39377       ],
39378       [
39379         "Kyrgyzstan (Кыргызстан)",
39380         "kg",
39381         "996"
39382       ],
39383       [
39384         "Laos (ລາວ)",
39385         "la",
39386         "856"
39387       ],
39388       [
39389         "Latvia (Latvija)",
39390         "lv",
39391         "371"
39392       ],
39393       [
39394         "Lebanon (‫لبنان‬‎)",
39395         "lb",
39396         "961"
39397       ],
39398       [
39399         "Lesotho",
39400         "ls",
39401         "266"
39402       ],
39403       [
39404         "Liberia",
39405         "lr",
39406         "231"
39407       ],
39408       [
39409         "Libya (‫ليبيا‬‎)",
39410         "ly",
39411         "218"
39412       ],
39413       [
39414         "Liechtenstein",
39415         "li",
39416         "423"
39417       ],
39418       [
39419         "Lithuania (Lietuva)",
39420         "lt",
39421         "370"
39422       ],
39423       [
39424         "Luxembourg",
39425         "lu",
39426         "352"
39427       ],
39428       [
39429         "Macau (澳門)",
39430         "mo",
39431         "853"
39432       ],
39433       [
39434         "Macedonia (FYROM) (Македонија)",
39435         "mk",
39436         "389"
39437       ],
39438       [
39439         "Madagascar (Madagasikara)",
39440         "mg",
39441         "261"
39442       ],
39443       [
39444         "Malawi",
39445         "mw",
39446         "265"
39447       ],
39448       [
39449         "Malaysia",
39450         "my",
39451         "60"
39452       ],
39453       [
39454         "Maldives",
39455         "mv",
39456         "960"
39457       ],
39458       [
39459         "Mali",
39460         "ml",
39461         "223"
39462       ],
39463       [
39464         "Malta",
39465         "mt",
39466         "356"
39467       ],
39468       [
39469         "Marshall Islands",
39470         "mh",
39471         "692"
39472       ],
39473       [
39474         "Martinique",
39475         "mq",
39476         "596"
39477       ],
39478       [
39479         "Mauritania (‫موريتانيا‬‎)",
39480         "mr",
39481         "222"
39482       ],
39483       [
39484         "Mauritius (Moris)",
39485         "mu",
39486         "230"
39487       ],
39488       [
39489         "Mayotte",
39490         "yt",
39491         "262",
39492         1
39493       ],
39494       [
39495         "Mexico (México)",
39496         "mx",
39497         "52"
39498       ],
39499       [
39500         "Micronesia",
39501         "fm",
39502         "691"
39503       ],
39504       [
39505         "Moldova (Republica Moldova)",
39506         "md",
39507         "373"
39508       ],
39509       [
39510         "Monaco",
39511         "mc",
39512         "377"
39513       ],
39514       [
39515         "Mongolia (Монгол)",
39516         "mn",
39517         "976"
39518       ],
39519       [
39520         "Montenegro (Crna Gora)",
39521         "me",
39522         "382"
39523       ],
39524       [
39525         "Montserrat",
39526         "ms",
39527         "1664"
39528       ],
39529       [
39530         "Morocco (‫المغرب‬‎)",
39531         "ma",
39532         "212",
39533         0
39534       ],
39535       [
39536         "Mozambique (Moçambique)",
39537         "mz",
39538         "258"
39539       ],
39540       [
39541         "Myanmar (Burma) (မြန်မာ)",
39542         "mm",
39543         "95"
39544       ],
39545       [
39546         "Namibia (Namibië)",
39547         "na",
39548         "264"
39549       ],
39550       [
39551         "Nauru",
39552         "nr",
39553         "674"
39554       ],
39555       [
39556         "Nepal (नेपाल)",
39557         "np",
39558         "977"
39559       ],
39560       [
39561         "Netherlands (Nederland)",
39562         "nl",
39563         "31"
39564       ],
39565       [
39566         "New Caledonia (Nouvelle-Calédonie)",
39567         "nc",
39568         "687"
39569       ],
39570       [
39571         "New Zealand",
39572         "nz",
39573         "64"
39574       ],
39575       [
39576         "Nicaragua",
39577         "ni",
39578         "505"
39579       ],
39580       [
39581         "Niger (Nijar)",
39582         "ne",
39583         "227"
39584       ],
39585       [
39586         "Nigeria",
39587         "ng",
39588         "234"
39589       ],
39590       [
39591         "Niue",
39592         "nu",
39593         "683"
39594       ],
39595       [
39596         "Norfolk Island",
39597         "nf",
39598         "672"
39599       ],
39600       [
39601         "North Korea (조선 민주주의 인민 공화국)",
39602         "kp",
39603         "850"
39604       ],
39605       [
39606         "Northern Mariana Islands",
39607         "mp",
39608         "1670"
39609       ],
39610       [
39611         "Norway (Norge)",
39612         "no",
39613         "47",
39614         0
39615       ],
39616       [
39617         "Oman (‫عُمان‬‎)",
39618         "om",
39619         "968"
39620       ],
39621       [
39622         "Pakistan (‫پاکستان‬‎)",
39623         "pk",
39624         "92"
39625       ],
39626       [
39627         "Palau",
39628         "pw",
39629         "680"
39630       ],
39631       [
39632         "Palestine (‫فلسطين‬‎)",
39633         "ps",
39634         "970"
39635       ],
39636       [
39637         "Panama (Panamá)",
39638         "pa",
39639         "507"
39640       ],
39641       [
39642         "Papua New Guinea",
39643         "pg",
39644         "675"
39645       ],
39646       [
39647         "Paraguay",
39648         "py",
39649         "595"
39650       ],
39651       [
39652         "Peru (Perú)",
39653         "pe",
39654         "51"
39655       ],
39656       [
39657         "Philippines",
39658         "ph",
39659         "63"
39660       ],
39661       [
39662         "Poland (Polska)",
39663         "pl",
39664         "48"
39665       ],
39666       [
39667         "Portugal",
39668         "pt",
39669         "351"
39670       ],
39671       [
39672         "Puerto Rico",
39673         "pr",
39674         "1",
39675         3,
39676         ["787", "939"]
39677       ],
39678       [
39679         "Qatar (‫قطر‬‎)",
39680         "qa",
39681         "974"
39682       ],
39683       [
39684         "Réunion (La Réunion)",
39685         "re",
39686         "262",
39687         0
39688       ],
39689       [
39690         "Romania (România)",
39691         "ro",
39692         "40"
39693       ],
39694       [
39695         "Russia (Россия)",
39696         "ru",
39697         "7",
39698         0
39699       ],
39700       [
39701         "Rwanda",
39702         "rw",
39703         "250"
39704       ],
39705       [
39706         "Saint Barthélemy",
39707         "bl",
39708         "590",
39709         1
39710       ],
39711       [
39712         "Saint Helena",
39713         "sh",
39714         "290"
39715       ],
39716       [
39717         "Saint Kitts and Nevis",
39718         "kn",
39719         "1869"
39720       ],
39721       [
39722         "Saint Lucia",
39723         "lc",
39724         "1758"
39725       ],
39726       [
39727         "Saint Martin (Saint-Martin (partie française))",
39728         "mf",
39729         "590",
39730         2
39731       ],
39732       [
39733         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39734         "pm",
39735         "508"
39736       ],
39737       [
39738         "Saint Vincent and the Grenadines",
39739         "vc",
39740         "1784"
39741       ],
39742       [
39743         "Samoa",
39744         "ws",
39745         "685"
39746       ],
39747       [
39748         "San Marino",
39749         "sm",
39750         "378"
39751       ],
39752       [
39753         "São Tomé and Príncipe (São Tomé e Príncipe)",
39754         "st",
39755         "239"
39756       ],
39757       [
39758         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39759         "sa",
39760         "966"
39761       ],
39762       [
39763         "Senegal (Sénégal)",
39764         "sn",
39765         "221"
39766       ],
39767       [
39768         "Serbia (Србија)",
39769         "rs",
39770         "381"
39771       ],
39772       [
39773         "Seychelles",
39774         "sc",
39775         "248"
39776       ],
39777       [
39778         "Sierra Leone",
39779         "sl",
39780         "232"
39781       ],
39782       [
39783         "Singapore",
39784         "sg",
39785         "65"
39786       ],
39787       [
39788         "Sint Maarten",
39789         "sx",
39790         "1721"
39791       ],
39792       [
39793         "Slovakia (Slovensko)",
39794         "sk",
39795         "421"
39796       ],
39797       [
39798         "Slovenia (Slovenija)",
39799         "si",
39800         "386"
39801       ],
39802       [
39803         "Solomon Islands",
39804         "sb",
39805         "677"
39806       ],
39807       [
39808         "Somalia (Soomaaliya)",
39809         "so",
39810         "252"
39811       ],
39812       [
39813         "South Africa",
39814         "za",
39815         "27"
39816       ],
39817       [
39818         "South Korea (대한민국)",
39819         "kr",
39820         "82"
39821       ],
39822       [
39823         "South Sudan (‫جنوب السودان‬‎)",
39824         "ss",
39825         "211"
39826       ],
39827       [
39828         "Spain (España)",
39829         "es",
39830         "34"
39831       ],
39832       [
39833         "Sri Lanka (ශ්‍රී ලංකාව)",
39834         "lk",
39835         "94"
39836       ],
39837       [
39838         "Sudan (‫السودان‬‎)",
39839         "sd",
39840         "249"
39841       ],
39842       [
39843         "Suriname",
39844         "sr",
39845         "597"
39846       ],
39847       [
39848         "Svalbard and Jan Mayen",
39849         "sj",
39850         "47",
39851         1
39852       ],
39853       [
39854         "Swaziland",
39855         "sz",
39856         "268"
39857       ],
39858       [
39859         "Sweden (Sverige)",
39860         "se",
39861         "46"
39862       ],
39863       [
39864         "Switzerland (Schweiz)",
39865         "ch",
39866         "41"
39867       ],
39868       [
39869         "Syria (‫سوريا‬‎)",
39870         "sy",
39871         "963"
39872       ],
39873       [
39874         "Taiwan (台灣)",
39875         "tw",
39876         "886"
39877       ],
39878       [
39879         "Tajikistan",
39880         "tj",
39881         "992"
39882       ],
39883       [
39884         "Tanzania",
39885         "tz",
39886         "255"
39887       ],
39888       [
39889         "Thailand (ไทย)",
39890         "th",
39891         "66"
39892       ],
39893       [
39894         "Timor-Leste",
39895         "tl",
39896         "670"
39897       ],
39898       [
39899         "Togo",
39900         "tg",
39901         "228"
39902       ],
39903       [
39904         "Tokelau",
39905         "tk",
39906         "690"
39907       ],
39908       [
39909         "Tonga",
39910         "to",
39911         "676"
39912       ],
39913       [
39914         "Trinidad and Tobago",
39915         "tt",
39916         "1868"
39917       ],
39918       [
39919         "Tunisia (‫تونس‬‎)",
39920         "tn",
39921         "216"
39922       ],
39923       [
39924         "Turkey (Türkiye)",
39925         "tr",
39926         "90"
39927       ],
39928       [
39929         "Turkmenistan",
39930         "tm",
39931         "993"
39932       ],
39933       [
39934         "Turks and Caicos Islands",
39935         "tc",
39936         "1649"
39937       ],
39938       [
39939         "Tuvalu",
39940         "tv",
39941         "688"
39942       ],
39943       [
39944         "U.S. Virgin Islands",
39945         "vi",
39946         "1340"
39947       ],
39948       [
39949         "Uganda",
39950         "ug",
39951         "256"
39952       ],
39953       [
39954         "Ukraine (Україна)",
39955         "ua",
39956         "380"
39957       ],
39958       [
39959         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39960         "ae",
39961         "971"
39962       ],
39963       [
39964         "United Kingdom",
39965         "gb",
39966         "44",
39967         0
39968       ],
39969       [
39970         "United States",
39971         "us",
39972         "1",
39973         0
39974       ],
39975       [
39976         "Uruguay",
39977         "uy",
39978         "598"
39979       ],
39980       [
39981         "Uzbekistan (Oʻzbekiston)",
39982         "uz",
39983         "998"
39984       ],
39985       [
39986         "Vanuatu",
39987         "vu",
39988         "678"
39989       ],
39990       [
39991         "Vatican City (Città del Vaticano)",
39992         "va",
39993         "39",
39994         1
39995       ],
39996       [
39997         "Venezuela",
39998         "ve",
39999         "58"
40000       ],
40001       [
40002         "Vietnam (Việt Nam)",
40003         "vn",
40004         "84"
40005       ],
40006       [
40007         "Wallis and Futuna (Wallis-et-Futuna)",
40008         "wf",
40009         "681"
40010       ],
40011       [
40012         "Western Sahara (‫الصحراء الغربية‬‎)",
40013         "eh",
40014         "212",
40015         1
40016       ],
40017       [
40018         "Yemen (‫اليمن‬‎)",
40019         "ye",
40020         "967"
40021       ],
40022       [
40023         "Zambia",
40024         "zm",
40025         "260"
40026       ],
40027       [
40028         "Zimbabwe",
40029         "zw",
40030         "263"
40031       ],
40032       [
40033         "Åland Islands",
40034         "ax",
40035         "358",
40036         1
40037       ]
40038   ];
40039   
40040   return d;
40041 }/**
40042 *    This script refer to:
40043 *    Title: International Telephone Input
40044 *    Author: Jack O'Connor
40045 *    Code version:  v12.1.12
40046 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40047 **/
40048
40049 /**
40050  * @class Roo.bootstrap.PhoneInput
40051  * @extends Roo.bootstrap.TriggerField
40052  * An input with International dial-code selection
40053  
40054  * @cfg {String} defaultDialCode default '+852'
40055  * @cfg {Array} preferedCountries default []
40056   
40057  * @constructor
40058  * Create a new PhoneInput.
40059  * @param {Object} config Configuration options
40060  */
40061
40062 Roo.bootstrap.PhoneInput = function(config) {
40063     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40064 };
40065
40066 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40067         
40068         listWidth: undefined,
40069         
40070         selectedClass: 'active',
40071         
40072         invalidClass : "has-warning",
40073         
40074         validClass: 'has-success',
40075         
40076         allowed: '0123456789',
40077         
40078         max_length: 15,
40079         
40080         /**
40081          * @cfg {String} defaultDialCode The default dial code when initializing the input
40082          */
40083         defaultDialCode: '+852',
40084         
40085         /**
40086          * @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
40087          */
40088         preferedCountries: false,
40089         
40090         getAutoCreate : function()
40091         {
40092             var data = Roo.bootstrap.PhoneInputData();
40093             var align = this.labelAlign || this.parentLabelAlign();
40094             var id = Roo.id();
40095             
40096             this.allCountries = [];
40097             this.dialCodeMapping = [];
40098             
40099             for (var i = 0; i < data.length; i++) {
40100               var c = data[i];
40101               this.allCountries[i] = {
40102                 name: c[0],
40103                 iso2: c[1],
40104                 dialCode: c[2],
40105                 priority: c[3] || 0,
40106                 areaCodes: c[4] || null
40107               };
40108               this.dialCodeMapping[c[2]] = {
40109                   name: c[0],
40110                   iso2: c[1],
40111                   priority: c[3] || 0,
40112                   areaCodes: c[4] || null
40113               };
40114             }
40115             
40116             var cfg = {
40117                 cls: 'form-group',
40118                 cn: []
40119             };
40120             
40121             var input =  {
40122                 tag: 'input',
40123                 id : id,
40124                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40125                 maxlength: this.max_length,
40126                 cls : 'form-control tel-input',
40127                 autocomplete: 'new-password'
40128             };
40129             
40130             var hiddenInput = {
40131                 tag: 'input',
40132                 type: 'hidden',
40133                 cls: 'hidden-tel-input'
40134             };
40135             
40136             if (this.name) {
40137                 hiddenInput.name = this.name;
40138             }
40139             
40140             if (this.disabled) {
40141                 input.disabled = true;
40142             }
40143             
40144             var flag_container = {
40145                 tag: 'div',
40146                 cls: 'flag-box',
40147                 cn: [
40148                     {
40149                         tag: 'div',
40150                         cls: 'flag'
40151                     },
40152                     {
40153                         tag: 'div',
40154                         cls: 'caret'
40155                     }
40156                 ]
40157             };
40158             
40159             var box = {
40160                 tag: 'div',
40161                 cls: this.hasFeedback ? 'has-feedback' : '',
40162                 cn: [
40163                     hiddenInput,
40164                     input,
40165                     {
40166                         tag: 'input',
40167                         cls: 'dial-code-holder',
40168                         disabled: true
40169                     }
40170                 ]
40171             };
40172             
40173             var container = {
40174                 cls: 'roo-select2-container input-group',
40175                 cn: [
40176                     flag_container,
40177                     box
40178                 ]
40179             };
40180             
40181             if (this.fieldLabel.length) {
40182                 var indicator = {
40183                     tag: 'i',
40184                     tooltip: 'This field is required'
40185                 };
40186                 
40187                 var label = {
40188                     tag: 'label',
40189                     'for':  id,
40190                     cls: 'control-label',
40191                     cn: []
40192                 };
40193                 
40194                 var label_text = {
40195                     tag: 'span',
40196                     html: this.fieldLabel
40197                 };
40198                 
40199                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40200                 label.cn = [
40201                     indicator,
40202                     label_text
40203                 ];
40204                 
40205                 if(this.indicatorpos == 'right') {
40206                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40207                     label.cn = [
40208                         label_text,
40209                         indicator
40210                     ];
40211                 }
40212                 
40213                 if(align == 'left') {
40214                     container = {
40215                         tag: 'div',
40216                         cn: [
40217                             container
40218                         ]
40219                     };
40220                     
40221                     if(this.labelWidth > 12){
40222                         label.style = "width: " + this.labelWidth + 'px';
40223                     }
40224                     if(this.labelWidth < 13 && this.labelmd == 0){
40225                         this.labelmd = this.labelWidth;
40226                     }
40227                     if(this.labellg > 0){
40228                         label.cls += ' col-lg-' + this.labellg;
40229                         input.cls += ' col-lg-' + (12 - this.labellg);
40230                     }
40231                     if(this.labelmd > 0){
40232                         label.cls += ' col-md-' + this.labelmd;
40233                         container.cls += ' col-md-' + (12 - this.labelmd);
40234                     }
40235                     if(this.labelsm > 0){
40236                         label.cls += ' col-sm-' + this.labelsm;
40237                         container.cls += ' col-sm-' + (12 - this.labelsm);
40238                     }
40239                     if(this.labelxs > 0){
40240                         label.cls += ' col-xs-' + this.labelxs;
40241                         container.cls += ' col-xs-' + (12 - this.labelxs);
40242                     }
40243                 }
40244             }
40245             
40246             cfg.cn = [
40247                 label,
40248                 container
40249             ];
40250             
40251             var settings = this;
40252             
40253             ['xs','sm','md','lg'].map(function(size){
40254                 if (settings[size]) {
40255                     cfg.cls += ' col-' + size + '-' + settings[size];
40256                 }
40257             });
40258             
40259             this.store = new Roo.data.Store({
40260                 proxy : new Roo.data.MemoryProxy({}),
40261                 reader : new Roo.data.JsonReader({
40262                     fields : [
40263                         {
40264                             'name' : 'name',
40265                             'type' : 'string'
40266                         },
40267                         {
40268                             'name' : 'iso2',
40269                             'type' : 'string'
40270                         },
40271                         {
40272                             'name' : 'dialCode',
40273                             'type' : 'string'
40274                         },
40275                         {
40276                             'name' : 'priority',
40277                             'type' : 'string'
40278                         },
40279                         {
40280                             'name' : 'areaCodes',
40281                             'type' : 'string'
40282                         }
40283                     ]
40284                 })
40285             });
40286             
40287             if(!this.preferedCountries) {
40288                 this.preferedCountries = [
40289                     'hk',
40290                     'gb',
40291                     'us'
40292                 ];
40293             }
40294             
40295             var p = this.preferedCountries.reverse();
40296             
40297             if(p) {
40298                 for (var i = 0; i < p.length; i++) {
40299                     for (var j = 0; j < this.allCountries.length; j++) {
40300                         if(this.allCountries[j].iso2 == p[i]) {
40301                             var t = this.allCountries[j];
40302                             this.allCountries.splice(j,1);
40303                             this.allCountries.unshift(t);
40304                         }
40305                     } 
40306                 }
40307             }
40308             
40309             this.store.proxy.data = {
40310                 success: true,
40311                 data: this.allCountries
40312             };
40313             
40314             return cfg;
40315         },
40316         
40317         initEvents : function()
40318         {
40319             this.createList();
40320             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40321             
40322             this.indicator = this.indicatorEl();
40323             this.flag = this.flagEl();
40324             this.dialCodeHolder = this.dialCodeHolderEl();
40325             
40326             this.trigger = this.el.select('div.flag-box',true).first();
40327             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40328             
40329             var _this = this;
40330             
40331             (function(){
40332                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40333                 _this.list.setWidth(lw);
40334             }).defer(100);
40335             
40336             this.list.on('mouseover', this.onViewOver, this);
40337             this.list.on('mousemove', this.onViewMove, this);
40338             this.inputEl().on("keyup", this.onKeyUp, this);
40339             this.inputEl().on("keypress", this.onKeyPress, this);
40340             
40341             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40342
40343             this.view = new Roo.View(this.list, this.tpl, {
40344                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40345             });
40346             
40347             this.view.on('click', this.onViewClick, this);
40348             this.setValue(this.defaultDialCode);
40349         },
40350         
40351         onTriggerClick : function(e)
40352         {
40353             Roo.log('trigger click');
40354             if(this.disabled){
40355                 return;
40356             }
40357             
40358             if(this.isExpanded()){
40359                 this.collapse();
40360                 this.hasFocus = false;
40361             }else {
40362                 this.store.load({});
40363                 this.hasFocus = true;
40364                 this.expand();
40365             }
40366         },
40367         
40368         isExpanded : function()
40369         {
40370             return this.list.isVisible();
40371         },
40372         
40373         collapse : function()
40374         {
40375             if(!this.isExpanded()){
40376                 return;
40377             }
40378             this.list.hide();
40379             Roo.get(document).un('mousedown', this.collapseIf, this);
40380             Roo.get(document).un('mousewheel', this.collapseIf, this);
40381             this.fireEvent('collapse', this);
40382             this.validate();
40383         },
40384         
40385         expand : function()
40386         {
40387             Roo.log('expand');
40388
40389             if(this.isExpanded() || !this.hasFocus){
40390                 return;
40391             }
40392             
40393             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40394             this.list.setWidth(lw);
40395             
40396             this.list.show();
40397             this.restrictHeight();
40398             
40399             Roo.get(document).on('mousedown', this.collapseIf, this);
40400             Roo.get(document).on('mousewheel', this.collapseIf, this);
40401             
40402             this.fireEvent('expand', this);
40403         },
40404         
40405         restrictHeight : function()
40406         {
40407             this.list.alignTo(this.inputEl(), this.listAlign);
40408             this.list.alignTo(this.inputEl(), this.listAlign);
40409         },
40410         
40411         onViewOver : function(e, t)
40412         {
40413             if(this.inKeyMode){
40414                 return;
40415             }
40416             var item = this.view.findItemFromChild(t);
40417             
40418             if(item){
40419                 var index = this.view.indexOf(item);
40420                 this.select(index, false);
40421             }
40422         },
40423
40424         // private
40425         onViewClick : function(view, doFocus, el, e)
40426         {
40427             var index = this.view.getSelectedIndexes()[0];
40428             
40429             var r = this.store.getAt(index);
40430             
40431             if(r){
40432                 this.onSelect(r, index);
40433             }
40434             if(doFocus !== false && !this.blockFocus){
40435                 this.inputEl().focus();
40436             }
40437         },
40438         
40439         onViewMove : function(e, t)
40440         {
40441             this.inKeyMode = false;
40442         },
40443         
40444         select : function(index, scrollIntoView)
40445         {
40446             this.selectedIndex = index;
40447             this.view.select(index);
40448             if(scrollIntoView !== false){
40449                 var el = this.view.getNode(index);
40450                 if(el){
40451                     this.list.scrollChildIntoView(el, false);
40452                 }
40453             }
40454         },
40455         
40456         createList : function()
40457         {
40458             this.list = Roo.get(document.body).createChild({
40459                 tag: 'ul',
40460                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40461                 style: 'display:none'
40462             });
40463             
40464             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40465         },
40466         
40467         collapseIf : function(e)
40468         {
40469             var in_combo  = e.within(this.el);
40470             var in_list =  e.within(this.list);
40471             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40472             
40473             if (in_combo || in_list || is_list) {
40474                 return;
40475             }
40476             this.collapse();
40477         },
40478         
40479         onSelect : function(record, index)
40480         {
40481             if(this.fireEvent('beforeselect', this, record, index) !== false){
40482                 
40483                 this.setFlagClass(record.data.iso2);
40484                 this.setDialCode(record.data.dialCode);
40485                 this.hasFocus = false;
40486                 this.collapse();
40487                 this.fireEvent('select', this, record, index);
40488             }
40489         },
40490         
40491         flagEl : function()
40492         {
40493             var flag = this.el.select('div.flag',true).first();
40494             if(!flag){
40495                 return false;
40496             }
40497             return flag;
40498         },
40499         
40500         dialCodeHolderEl : function()
40501         {
40502             var d = this.el.select('input.dial-code-holder',true).first();
40503             if(!d){
40504                 return false;
40505             }
40506             return d;
40507         },
40508         
40509         setDialCode : function(v)
40510         {
40511             this.dialCodeHolder.dom.value = '+'+v;
40512         },
40513         
40514         setFlagClass : function(n)
40515         {
40516             this.flag.dom.className = 'flag '+n;
40517         },
40518         
40519         getValue : function()
40520         {
40521             var v = this.inputEl().getValue();
40522             if(this.dialCodeHolder) {
40523                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40524             }
40525             return v;
40526         },
40527         
40528         setValue : function(v)
40529         {
40530             var d = this.getDialCode(v);
40531             
40532             //invalid dial code
40533             if(v.length == 0 || !d || d.length == 0) {
40534                 if(this.rendered){
40535                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40536                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40537                 }
40538                 return;
40539             }
40540             
40541             //valid dial code
40542             this.setFlagClass(this.dialCodeMapping[d].iso2);
40543             this.setDialCode(d);
40544             this.inputEl().dom.value = v.replace('+'+d,'');
40545             this.hiddenEl().dom.value = this.getValue();
40546             
40547             this.validate();
40548         },
40549         
40550         getDialCode : function(v)
40551         {
40552             v = v ||  '';
40553             
40554             if (v.length == 0) {
40555                 return this.dialCodeHolder.dom.value;
40556             }
40557             
40558             var dialCode = "";
40559             if (v.charAt(0) != "+") {
40560                 return false;
40561             }
40562             var numericChars = "";
40563             for (var i = 1; i < v.length; i++) {
40564               var c = v.charAt(i);
40565               if (!isNaN(c)) {
40566                 numericChars += c;
40567                 if (this.dialCodeMapping[numericChars]) {
40568                   dialCode = v.substr(1, i);
40569                 }
40570                 if (numericChars.length == 4) {
40571                   break;
40572                 }
40573               }
40574             }
40575             return dialCode;
40576         },
40577         
40578         reset : function()
40579         {
40580             this.setValue(this.defaultDialCode);
40581             this.validate();
40582         },
40583         
40584         hiddenEl : function()
40585         {
40586             return this.el.select('input.hidden-tel-input',true).first();
40587         },
40588         
40589         // after setting val
40590         onKeyUp : function(e){
40591             this.setValue(this.getValue());
40592         },
40593         
40594         onKeyPress : function(e){
40595             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40596                 e.stopEvent();
40597             }
40598         }
40599         
40600 });
40601 /**
40602  * @class Roo.bootstrap.MoneyField
40603  * @extends Roo.bootstrap.ComboBox
40604  * Bootstrap MoneyField class
40605  * 
40606  * @constructor
40607  * Create a new MoneyField.
40608  * @param {Object} config Configuration options
40609  */
40610
40611 Roo.bootstrap.MoneyField = function(config) {
40612     
40613     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40614     
40615 };
40616
40617 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40618     
40619     /**
40620      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40621      */
40622     allowDecimals : true,
40623     /**
40624      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40625      */
40626     decimalSeparator : ".",
40627     /**
40628      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40629      */
40630     decimalPrecision : 0,
40631     /**
40632      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40633      */
40634     allowNegative : true,
40635     /**
40636      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40637      */
40638     allowZero: true,
40639     /**
40640      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40641      */
40642     minValue : Number.NEGATIVE_INFINITY,
40643     /**
40644      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40645      */
40646     maxValue : Number.MAX_VALUE,
40647     /**
40648      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40649      */
40650     minText : "The minimum value for this field is {0}",
40651     /**
40652      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40653      */
40654     maxText : "The maximum value for this field is {0}",
40655     /**
40656      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40657      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40658      */
40659     nanText : "{0} is not a valid number",
40660     /**
40661      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40662      */
40663     castInt : true,
40664     /**
40665      * @cfg {String} defaults currency of the MoneyField
40666      * value should be in lkey
40667      */
40668     defaultCurrency : false,
40669     /**
40670      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40671      */
40672     thousandsDelimiter : false,
40673     /**
40674      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40675      */
40676     max_length: false,
40677     
40678     inputlg : 9,
40679     inputmd : 9,
40680     inputsm : 9,
40681     inputxs : 6,
40682     
40683     store : false,
40684     
40685     getAutoCreate : function()
40686     {
40687         var align = this.labelAlign || this.parentLabelAlign();
40688         
40689         var id = Roo.id();
40690
40691         var cfg = {
40692             cls: 'form-group',
40693             cn: []
40694         };
40695
40696         var input =  {
40697             tag: 'input',
40698             id : id,
40699             cls : 'form-control roo-money-amount-input',
40700             autocomplete: 'new-password'
40701         };
40702         
40703         var hiddenInput = {
40704             tag: 'input',
40705             type: 'hidden',
40706             id: Roo.id(),
40707             cls: 'hidden-number-input'
40708         };
40709         
40710         if(this.max_length) {
40711             input.maxlength = this.max_length; 
40712         }
40713         
40714         if (this.name) {
40715             hiddenInput.name = this.name;
40716         }
40717
40718         if (this.disabled) {
40719             input.disabled = true;
40720         }
40721
40722         var clg = 12 - this.inputlg;
40723         var cmd = 12 - this.inputmd;
40724         var csm = 12 - this.inputsm;
40725         var cxs = 12 - this.inputxs;
40726         
40727         var container = {
40728             tag : 'div',
40729             cls : 'row roo-money-field',
40730             cn : [
40731                 {
40732                     tag : 'div',
40733                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40734                     cn : [
40735                         {
40736                             tag : 'div',
40737                             cls: 'roo-select2-container input-group',
40738                             cn: [
40739                                 {
40740                                     tag : 'input',
40741                                     cls : 'form-control roo-money-currency-input',
40742                                     autocomplete: 'new-password',
40743                                     readOnly : 1,
40744                                     name : this.currencyName
40745                                 },
40746                                 {
40747                                     tag :'span',
40748                                     cls : 'input-group-addon',
40749                                     cn : [
40750                                         {
40751                                             tag: 'span',
40752                                             cls: 'caret'
40753                                         }
40754                                     ]
40755                                 }
40756                             ]
40757                         }
40758                     ]
40759                 },
40760                 {
40761                     tag : 'div',
40762                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40763                     cn : [
40764                         {
40765                             tag: 'div',
40766                             cls: this.hasFeedback ? 'has-feedback' : '',
40767                             cn: [
40768                                 input
40769                             ]
40770                         }
40771                     ]
40772                 }
40773             ]
40774             
40775         };
40776         
40777         if (this.fieldLabel.length) {
40778             var indicator = {
40779                 tag: 'i',
40780                 tooltip: 'This field is required'
40781             };
40782
40783             var label = {
40784                 tag: 'label',
40785                 'for':  id,
40786                 cls: 'control-label',
40787                 cn: []
40788             };
40789
40790             var label_text = {
40791                 tag: 'span',
40792                 html: this.fieldLabel
40793             };
40794
40795             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40796             label.cn = [
40797                 indicator,
40798                 label_text
40799             ];
40800
40801             if(this.indicatorpos == 'right') {
40802                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40803                 label.cn = [
40804                     label_text,
40805                     indicator
40806                 ];
40807             }
40808
40809             if(align == 'left') {
40810                 container = {
40811                     tag: 'div',
40812                     cn: [
40813                         container
40814                     ]
40815                 };
40816
40817                 if(this.labelWidth > 12){
40818                     label.style = "width: " + this.labelWidth + 'px';
40819                 }
40820                 if(this.labelWidth < 13 && this.labelmd == 0){
40821                     this.labelmd = this.labelWidth;
40822                 }
40823                 if(this.labellg > 0){
40824                     label.cls += ' col-lg-' + this.labellg;
40825                     input.cls += ' col-lg-' + (12 - this.labellg);
40826                 }
40827                 if(this.labelmd > 0){
40828                     label.cls += ' col-md-' + this.labelmd;
40829                     container.cls += ' col-md-' + (12 - this.labelmd);
40830                 }
40831                 if(this.labelsm > 0){
40832                     label.cls += ' col-sm-' + this.labelsm;
40833                     container.cls += ' col-sm-' + (12 - this.labelsm);
40834                 }
40835                 if(this.labelxs > 0){
40836                     label.cls += ' col-xs-' + this.labelxs;
40837                     container.cls += ' col-xs-' + (12 - this.labelxs);
40838                 }
40839             }
40840         }
40841
40842         cfg.cn = [
40843             label,
40844             container,
40845             hiddenInput
40846         ];
40847         
40848         var settings = this;
40849
40850         ['xs','sm','md','lg'].map(function(size){
40851             if (settings[size]) {
40852                 cfg.cls += ' col-' + size + '-' + settings[size];
40853             }
40854         });
40855         
40856         return cfg;
40857     },
40858     
40859     initEvents : function()
40860     {
40861         this.indicator = this.indicatorEl();
40862         
40863         this.initCurrencyEvent();
40864         
40865         this.initNumberEvent();
40866     },
40867     
40868     initCurrencyEvent : function()
40869     {
40870         if (!this.store) {
40871             throw "can not find store for combo";
40872         }
40873         
40874         this.store = Roo.factory(this.store, Roo.data);
40875         this.store.parent = this;
40876         
40877         this.createList();
40878         
40879         this.triggerEl = this.el.select('.input-group-addon', true).first();
40880         
40881         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40882         
40883         var _this = this;
40884         
40885         (function(){
40886             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40887             _this.list.setWidth(lw);
40888         }).defer(100);
40889         
40890         this.list.on('mouseover', this.onViewOver, this);
40891         this.list.on('mousemove', this.onViewMove, this);
40892         this.list.on('scroll', this.onViewScroll, this);
40893         
40894         if(!this.tpl){
40895             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40896         }
40897         
40898         this.view = new Roo.View(this.list, this.tpl, {
40899             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40900         });
40901         
40902         this.view.on('click', this.onViewClick, this);
40903         
40904         this.store.on('beforeload', this.onBeforeLoad, this);
40905         this.store.on('load', this.onLoad, this);
40906         this.store.on('loadexception', this.onLoadException, this);
40907         
40908         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40909             "up" : function(e){
40910                 this.inKeyMode = true;
40911                 this.selectPrev();
40912             },
40913
40914             "down" : function(e){
40915                 if(!this.isExpanded()){
40916                     this.onTriggerClick();
40917                 }else{
40918                     this.inKeyMode = true;
40919                     this.selectNext();
40920                 }
40921             },
40922
40923             "enter" : function(e){
40924                 this.collapse();
40925                 
40926                 if(this.fireEvent("specialkey", this, e)){
40927                     this.onViewClick(false);
40928                 }
40929                 
40930                 return true;
40931             },
40932
40933             "esc" : function(e){
40934                 this.collapse();
40935             },
40936
40937             "tab" : function(e){
40938                 this.collapse();
40939                 
40940                 if(this.fireEvent("specialkey", this, e)){
40941                     this.onViewClick(false);
40942                 }
40943                 
40944                 return true;
40945             },
40946
40947             scope : this,
40948
40949             doRelay : function(foo, bar, hname){
40950                 if(hname == 'down' || this.scope.isExpanded()){
40951                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40952                 }
40953                 return true;
40954             },
40955
40956             forceKeyDown: true
40957         });
40958         
40959         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40960         
40961     },
40962     
40963     initNumberEvent : function(e)
40964     {
40965         this.inputEl().on("keydown" , this.fireKey,  this);
40966         this.inputEl().on("focus", this.onFocus,  this);
40967         this.inputEl().on("blur", this.onBlur,  this);
40968         
40969         this.inputEl().relayEvent('keyup', this);
40970         
40971         if(this.indicator){
40972             this.indicator.addClass('invisible');
40973         }
40974  
40975         this.originalValue = this.getValue();
40976         
40977         if(this.validationEvent == 'keyup'){
40978             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40979             this.inputEl().on('keyup', this.filterValidation, this);
40980         }
40981         else if(this.validationEvent !== false){
40982             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40983         }
40984         
40985         if(this.selectOnFocus){
40986             this.on("focus", this.preFocus, this);
40987             
40988         }
40989         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40990             this.inputEl().on("keypress", this.filterKeys, this);
40991         } else {
40992             this.inputEl().relayEvent('keypress', this);
40993         }
40994         
40995         var allowed = "0123456789";
40996         
40997         if(this.allowDecimals){
40998             allowed += this.decimalSeparator;
40999         }
41000         
41001         if(this.allowNegative){
41002             allowed += "-";
41003         }
41004         
41005         if(this.thousandsDelimiter) {
41006             allowed += ",";
41007         }
41008         
41009         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41010         
41011         var keyPress = function(e){
41012             
41013             var k = e.getKey();
41014             
41015             var c = e.getCharCode();
41016             
41017             if(
41018                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41019                     allowed.indexOf(String.fromCharCode(c)) === -1
41020             ){
41021                 e.stopEvent();
41022                 return;
41023             }
41024             
41025             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41026                 return;
41027             }
41028             
41029             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41030                 e.stopEvent();
41031             }
41032         };
41033         
41034         this.inputEl().on("keypress", keyPress, this);
41035         
41036     },
41037     
41038     onTriggerClick : function(e)
41039     {   
41040         if(this.disabled){
41041             return;
41042         }
41043         
41044         this.page = 0;
41045         this.loadNext = false;
41046         
41047         if(this.isExpanded()){
41048             this.collapse();
41049             return;
41050         }
41051         
41052         this.hasFocus = true;
41053         
41054         if(this.triggerAction == 'all') {
41055             this.doQuery(this.allQuery, true);
41056             return;
41057         }
41058         
41059         this.doQuery(this.getRawValue());
41060     },
41061     
41062     getCurrency : function()
41063     {   
41064         var v = this.currencyEl().getValue();
41065         
41066         return v;
41067     },
41068     
41069     restrictHeight : function()
41070     {
41071         this.list.alignTo(this.currencyEl(), this.listAlign);
41072         this.list.alignTo(this.currencyEl(), this.listAlign);
41073     },
41074     
41075     onViewClick : function(view, doFocus, el, e)
41076     {
41077         var index = this.view.getSelectedIndexes()[0];
41078         
41079         var r = this.store.getAt(index);
41080         
41081         if(r){
41082             this.onSelect(r, index);
41083         }
41084     },
41085     
41086     onSelect : function(record, index){
41087         
41088         if(this.fireEvent('beforeselect', this, record, index) !== false){
41089         
41090             this.setFromCurrencyData(index > -1 ? record.data : false);
41091             
41092             this.collapse();
41093             
41094             this.fireEvent('select', this, record, index);
41095         }
41096     },
41097     
41098     setFromCurrencyData : function(o)
41099     {
41100         var currency = '';
41101         
41102         this.lastCurrency = o;
41103         
41104         if (this.currencyField) {
41105             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41106         } else {
41107             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41108         }
41109         
41110         this.lastSelectionText = currency;
41111         
41112         //setting default currency
41113         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41114             this.setCurrency(this.defaultCurrency);
41115             return;
41116         }
41117         
41118         this.setCurrency(currency);
41119     },
41120     
41121     setFromData : function(o)
41122     {
41123         var c = {};
41124         
41125         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41126         
41127         this.setFromCurrencyData(c);
41128         
41129         var value = '';
41130         
41131         if (this.name) {
41132             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41133         } else {
41134             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41135         }
41136         
41137         this.setValue(value);
41138         
41139     },
41140     
41141     setCurrency : function(v)
41142     {   
41143         this.currencyValue = v;
41144         
41145         if(this.rendered){
41146             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41147             this.validate();
41148         }
41149     },
41150     
41151     setValue : function(v)
41152     {
41153         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41154         
41155         this.value = v;
41156         
41157         if(this.rendered){
41158             
41159             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41160             
41161             this.inputEl().dom.value = (v == '') ? '' :
41162                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41163             
41164             if(!this.allowZero && v === '0') {
41165                 this.hiddenEl().dom.value = '';
41166                 this.inputEl().dom.value = '';
41167             }
41168             
41169             this.validate();
41170         }
41171     },
41172     
41173     getRawValue : function()
41174     {
41175         var v = this.inputEl().getValue();
41176         
41177         return v;
41178     },
41179     
41180     getValue : function()
41181     {
41182         return this.fixPrecision(this.parseValue(this.getRawValue()));
41183     },
41184     
41185     parseValue : function(value)
41186     {
41187         if(this.thousandsDelimiter) {
41188             value += "";
41189             r = new RegExp(",", "g");
41190             value = value.replace(r, "");
41191         }
41192         
41193         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41194         return isNaN(value) ? '' : value;
41195         
41196     },
41197     
41198     fixPrecision : function(value)
41199     {
41200         if(this.thousandsDelimiter) {
41201             value += "";
41202             r = new RegExp(",", "g");
41203             value = value.replace(r, "");
41204         }
41205         
41206         var nan = isNaN(value);
41207         
41208         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41209             return nan ? '' : value;
41210         }
41211         return parseFloat(value).toFixed(this.decimalPrecision);
41212     },
41213     
41214     decimalPrecisionFcn : function(v)
41215     {
41216         return Math.floor(v);
41217     },
41218     
41219     validateValue : function(value)
41220     {
41221         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41222             return false;
41223         }
41224         
41225         var num = this.parseValue(value);
41226         
41227         if(isNaN(num)){
41228             this.markInvalid(String.format(this.nanText, value));
41229             return false;
41230         }
41231         
41232         if(num < this.minValue){
41233             this.markInvalid(String.format(this.minText, this.minValue));
41234             return false;
41235         }
41236         
41237         if(num > this.maxValue){
41238             this.markInvalid(String.format(this.maxText, this.maxValue));
41239             return false;
41240         }
41241         
41242         return true;
41243     },
41244     
41245     validate : function()
41246     {
41247         if(this.disabled || this.allowBlank){
41248             this.markValid();
41249             return true;
41250         }
41251         
41252         var currency = this.getCurrency();
41253         
41254         if(this.validateValue(this.getRawValue()) && currency.length){
41255             this.markValid();
41256             return true;
41257         }
41258         
41259         this.markInvalid();
41260         return false;
41261     },
41262     
41263     getName: function()
41264     {
41265         return this.name;
41266     },
41267     
41268     beforeBlur : function()
41269     {
41270         if(!this.castInt){
41271             return;
41272         }
41273         
41274         var v = this.parseValue(this.getRawValue());
41275         
41276         if(v || v == 0){
41277             this.setValue(v);
41278         }
41279     },
41280     
41281     onBlur : function()
41282     {
41283         this.beforeBlur();
41284         
41285         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41286             //this.el.removeClass(this.focusClass);
41287         }
41288         
41289         this.hasFocus = false;
41290         
41291         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41292             this.validate();
41293         }
41294         
41295         var v = this.getValue();
41296         
41297         if(String(v) !== String(this.startValue)){
41298             this.fireEvent('change', this, v, this.startValue);
41299         }
41300         
41301         this.fireEvent("blur", this);
41302     },
41303     
41304     inputEl : function()
41305     {
41306         return this.el.select('.roo-money-amount-input', true).first();
41307     },
41308     
41309     currencyEl : function()
41310     {
41311         return this.el.select('.roo-money-currency-input', true).first();
41312     },
41313     
41314     hiddenEl : function()
41315     {
41316         return this.el.select('input.hidden-number-input',true).first();
41317     }
41318     
41319 });